fix(canvas): add keyboard resize + ARIA to SidePanel resize handle
Add role="separator" + aria-valuenow/min/max/orientation + tabIndex={0}
to make the resize handle focusable and discoverable by screen readers
(WAI-ARIA slider pattern). Add onKeyDown handler: ArrowLeft/Right moves
by 16px, Home/End snaps to min/max. Persist width to localStorage on
keyboard resize, matching the existing mouse behaviour.
Focus ring uses focus-visible:ring-2 to avoid showing on mouse click.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
56f085bae4
commit
8697a42447
@ -23,6 +23,7 @@ import { summarizeWorkspaceCapabilities } from "@/store/canvas";
|
||||
const SIDEPANEL_WIDTH_KEY = "molecule:sidepanel-width";
|
||||
const SIDEPANEL_DEFAULT_WIDTH = 480;
|
||||
const SIDEPANEL_MIN_WIDTH = 320;
|
||||
const SIDEPANEL_MAX_WIDTH = 800;
|
||||
|
||||
const TABS: { id: PanelTab; label: string; icon: string }[] = [
|
||||
{ id: "chat", label: "Chat", icon: "◈" },
|
||||
@ -72,6 +73,29 @@ export function SidePanel() {
|
||||
document.body.style.userSelect = "none";
|
||||
}, [width]);
|
||||
|
||||
const onResizeKeyDown = useCallback((e: React.KeyboardEvent) => {
|
||||
const STEP = 16;
|
||||
let newWidth: number | null = null;
|
||||
if (e.key === "ArrowLeft") {
|
||||
e.preventDefault();
|
||||
newWidth = Math.min(width + STEP, SIDEPANEL_MAX_WIDTH);
|
||||
} else if (e.key === "ArrowRight") {
|
||||
e.preventDefault();
|
||||
newWidth = Math.max(width - STEP, SIDEPANEL_MIN_WIDTH);
|
||||
} else if (e.key === "Home") {
|
||||
e.preventDefault();
|
||||
newWidth = SIDEPANEL_MIN_WIDTH;
|
||||
} else if (e.key === "End") {
|
||||
e.preventDefault();
|
||||
newWidth = SIDEPANEL_MAX_WIDTH;
|
||||
}
|
||||
if (newWidth !== null) {
|
||||
setWidth(newWidth);
|
||||
widthRef.current = newWidth;
|
||||
localStorage.setItem(SIDEPANEL_WIDTH_KEY, String(newWidth));
|
||||
}
|
||||
}, [width]);
|
||||
|
||||
useEffect(() => {
|
||||
const onMouseMove = (e: MouseEvent) => {
|
||||
if (!dragging.current) return;
|
||||
@ -111,8 +135,16 @@ export function SidePanel() {
|
||||
>
|
||||
{/* Resize handle */}
|
||||
<div
|
||||
role="separator"
|
||||
aria-label="Resize workspace panel"
|
||||
aria-valuenow={width}
|
||||
aria-valuemin={SIDEPANEL_MIN_WIDTH}
|
||||
aria-valuemax={SIDEPANEL_MAX_WIDTH}
|
||||
aria-orientation="vertical"
|
||||
tabIndex={0}
|
||||
onMouseDown={onMouseDown}
|
||||
className="absolute left-0 top-0 bottom-0 w-1.5 cursor-col-resize hover:bg-blue-500/30 active:bg-blue-500/50 transition-colors z-10"
|
||||
onKeyDown={onResizeKeyDown}
|
||||
className="absolute left-0 top-0 bottom-0 w-1.5 cursor-col-resize hover:bg-blue-500/30 active:bg-blue-500/50 transition-colors z-10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-inset"
|
||||
/>
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between px-5 py-4 border-b border-zinc-800/40 bg-zinc-900/30">
|
||||
|
||||
Loading…
Reference in New Issue
Block a user