forked from molecule-ai/molecule-core
Merge pull request 'feat(canvas): keyboard-accessible edge anchors via Enter/Space' (#190) from feat/canvas-keyboard-edge-anchors into main
This commit is contained in:
commit
a9bb2c47da
@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback, useMemo } from "react";
|
||||
import { useCallback, useMemo, type KeyboardEvent } from "react";
|
||||
import { Handle, NodeResizer, Position, type NodeProps, type Node } from "@xyflow/react";
|
||||
import { useCanvasStore, type WorkspaceNodeData } from "@/store/canvas";
|
||||
import { getConfigurationError, getConfigurationStatus } from "@/store/canvas-topology";
|
||||
@ -191,7 +191,23 @@ export function WorkspaceNode({ id, data }: NodeProps<Node<WorkspaceNodeData>>)
|
||||
<Handle
|
||||
type="target"
|
||||
position={Position.Top}
|
||||
className="!w-2.5 !h-1 !rounded-full !bg-surface-card/80 !border-0 !-top-0.5 hover:!bg-blue-400 hover:!h-1.5 transition-all"
|
||||
tabIndex={0}
|
||||
role="button"
|
||||
aria-label={`Extract ${data.name} from its parent (Enter or Space)`}
|
||||
onKeyDown={(e: KeyboardEvent<HTMLDivElement>) => {
|
||||
if (e.key === "Enter" || e.key === " ") {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
// Keyboard accessibility for edge anchors: pressing Enter/Space on
|
||||
// the top handle extracts this node from its current parent,
|
||||
// moving it to the root level. Mirrors the Figma/Excalidraw
|
||||
// pattern of using the connector dot as a keyboard affordance.
|
||||
if (data.parentId) {
|
||||
void nestNode(id, null);
|
||||
}
|
||||
}
|
||||
}}
|
||||
className="!w-2.5 !h-1 !rounded-full !bg-surface-card/80 !border-0 !-top-0.5 hover:!bg-blue-400 hover:!h-1.5 focus-visible:!bg-blue-400 focus-visible:!h-1.5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-400/60 focus-visible:ring-offset-1 focus-visible:ring-offset-zinc-950 transition-all"
|
||||
/>
|
||||
|
||||
<div className="relative px-3.5 py-2.5">
|
||||
@ -358,7 +374,23 @@ export function WorkspaceNode({ id, data }: NodeProps<Node<WorkspaceNodeData>>)
|
||||
<Handle
|
||||
type="source"
|
||||
position={Position.Bottom}
|
||||
className="!w-2.5 !h-1 !rounded-full !bg-surface-card/80 !border-0 !-bottom-0.5 hover:!bg-blue-400 hover:!h-1.5 transition-all"
|
||||
tabIndex={0}
|
||||
role="button"
|
||||
aria-label={`Nest selected workspace inside ${data.name} (Enter or Space)`}
|
||||
onKeyDown={(e: KeyboardEvent<HTMLDivElement>) => {
|
||||
if (e.key === "Enter" || e.key === " ") {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
// Keyboard accessibility for edge anchors: pressing Enter/Space on
|
||||
// the bottom handle nests the currently-selected node as a child
|
||||
// of this node. Requires another node to be selected first.
|
||||
const selected = selectedNodeId;
|
||||
if (selected && selected !== id) {
|
||||
void nestNode(selected, id);
|
||||
}
|
||||
}
|
||||
}}
|
||||
className="!w-2.5 !h-1 !rounded-full !bg-surface-card/80 !border-0 !-bottom-0.5 hover:!bg-blue-400 hover:!h-1.5 focus-visible:!bg-blue-400 focus-visible:!h-1.5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-400/60 focus-visible:ring-offset-1 focus-visible:ring-offset-zinc-950 transition-all"
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
|
||||
@ -62,9 +62,9 @@ canvas/src/
|
||||
### Edge Wiring ✅
|
||||
- **Edge rendering:** React Flow SVG paths
|
||||
- **Edge click target:** 1.5px stroke (CSS `stroke-width: 1.5 !important` in globals.css)
|
||||
- **Edge creation:** React Flow drag-from-handle
|
||||
- **Edge anchors:** Visible on hover (`hover:!bg-blue-400`), not keyboard accessible
|
||||
- **Status:** Partial — mouse users only
|
||||
- **Edge creation:** React Flow drag-from-handle (mouse); keyboard via handle Enter/Space
|
||||
- **Edge anchors:** Target handle (top): `Enter/Space` extracts node from parent. Source handle (bottom): `Enter/Space` nests selected node into this node. Both have `tabIndex=0`, `role="button"`, descriptive `aria-label`, and a blue focus ring ✅
|
||||
- **Status:** Mouse + keyboard — keyboard users can nest and un-nest without a mouse
|
||||
|
||||
### Canvas Controls ✅
|
||||
- **Zoom:** React Flow Controls component (verify if keyboard accessible)
|
||||
@ -111,7 +111,7 @@ canvas/src/
|
||||
| ~~HIGH~~ | ~~Screen reader announcements for canvas state changes~~ | ~~Canvas.tsx, canvas-events.ts, canvas.ts~~ | ✅ Done — PR #172 |
|
||||
| MEDIUM | Keyboard shortcut help dialog | useKeyboardShortcuts.ts | ✅ Done (PR #175) |
|
||||
| MEDIUM | Keyboard-accessible node drag | WorkspaceNode.tsx, useDragHandlers.ts | ✅ Done (this PR) |
|
||||
| LOW | Edge anchor keyboard accessibility | A2AEdge.tsx | Not started |
|
||||
| LOW | Keyboard-accessible edge anchors | A2AEdge.tsx, WorkspaceNode.tsx | ✅ Done |
|
||||
| LOW | Node resize keyboard accessibility | WorkspaceNode.tsx (NodeResizer) | Not started |
|
||||
|
||||
---
|
||||
|
||||
Loading…
Reference in New Issue
Block a user