KQ-1135 · TOOL/DOCK
Tool Dock
The tool rail of a drawing canvas and the canvas it drives: the active tool sits on an indicator that slides to each new choice on the snap spring with a tick pitched up the rail, and a tool with variants unfolds its options beside it with a swish — a panel revealed from the rail's edge whose entries step out on snap, or a drawer that glides open inside the rail on glide. Single-letter shortcuts switch tools while focus or the pointer is on the dock (never while typing), the key of the active tool cycles its variants, and the canvas shows the tool's own cursor, drawn 1:1 under the pointer and popping to each new shape on snap. The rail is a toolbar of pressed buttons with one tab stop: arrows move, Space or Enter choose, and the arrow toward the options opens them as a menu of radio items with focus inside.
tool Move · key v
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/tool-dock
Props
- tools
- { id: string; label: string; glyph?: ToolDockGlyph; icon?: ReactNode; cursor?: ReactNode; key?: string; variants?: { id: string; label: string; glyph?: ToolDockGlyph; icon?: ReactNode; cursor?: ReactNode; key?: string }[] }[]
- default —
- The rail, top to bottom (or left to right). A tool with variants shows the last one used; key is a single letter. Built-in glyphs, each with a cursor: move, hand, frame, rect, ellipse, line, polygon, pen, pencil, brush, text, eraser, comment.
- value / defaultValue
- string
- default the first tool (its first variant)
- The active tool's id, or the active variant's id for a tool that has them.
- onValueChange
- (id: string) => void
- default —
- Fires from the press, key or shortcut that changed the tool.
- orientation
- "vertical" | "horizontal"
- default "vertical"
- A rail down the canvas's left, or centred along its foot.
- shortcuts
- boolean
- default true
- Single-letter keys switch tools and each button shows its letter (and names it in aria-keyshortcuts). Keys act while focus is in the dock, or while the pointer is over it and nothing else has focus; never in a field or with Ctrl, Cmd or Alt, and a handled key is claimed.
- flyout
- "side" | "tray"
- default "side"
- A tool's options float in a panel beside the rail (over the canvas, placed inside the component's box), or open as a drawer inside the rail that pushes the tools after it.
- labels
- boolean
- default false
- A label under each icon; the rail widens to fit. Off, the name is still each button's accessible name.
- children
- ReactNode
- default —
- What is on the canvas, laid out inside it (position it absolutely to fill).
- canvasHeight
- number
- default 400
- The canvas's height in px.
- label
- string
- default "Tools"
- The rail's accessible name.
- canvasLabel
- string
- default "Canvas"
- The canvas's accessible name.
- sound
- boolean
- default false
- A tick on every tool or variant change, pitched up the rail on a pentatonic scale and panned to the button; a swish as options unfold and a lower, quieter one as they fold. Nothing plays, and no audio context exists, unless this is on.
- disabled
- boolean
- default false
- Dims the dock, disables its buttons and stops the shortcuts; the system cursor comes back over the canvas.
- className
- string
- default —
- Merged onto the wrapper that holds the rail and the canvas.
| Prop | Type | Default | Description |
|---|---|---|---|
| tools | { id: string; label: string; glyph?: ToolDockGlyph; icon?: ReactNode; cursor?: ReactNode; key?: string; variants?: { id: string; label: string; glyph?: ToolDockGlyph; icon?: ReactNode; cursor?: ReactNode; key?: string }[] }[] | — | The rail, top to bottom (or left to right). A tool with variants shows the last one used; key is a single letter. Built-in glyphs, each with a cursor: move, hand, frame, rect, ellipse, line, polygon, pen, pencil, brush, text, eraser, comment. |
| value / defaultValue | string | the first tool (its first variant) | The active tool's id, or the active variant's id for a tool that has them. |
| onValueChange | (id: string) => void | — | Fires from the press, key or shortcut that changed the tool. |
| orientation | "vertical" | "horizontal" | "vertical" | A rail down the canvas's left, or centred along its foot. |
| shortcuts | boolean | true | Single-letter keys switch tools and each button shows its letter (and names it in aria-keyshortcuts). Keys act while focus is in the dock, or while the pointer is over it and nothing else has focus; never in a field or with Ctrl, Cmd or Alt, and a handled key is claimed. |
| flyout | "side" | "tray" | "side" | A tool's options float in a panel beside the rail (over the canvas, placed inside the component's box), or open as a drawer inside the rail that pushes the tools after it. |
| labels | boolean | false | A label under each icon; the rail widens to fit. Off, the name is still each button's accessible name. |
| children | ReactNode | — | What is on the canvas, laid out inside it (position it absolutely to fill). |
| canvasHeight | number | 400 | The canvas's height in px. |
| label | string | "Tools" | The rail's accessible name. |
| canvasLabel | string | "Canvas" | The canvas's accessible name. |
| sound | boolean | false | A tick on every tool or variant change, pitched up the rail on a pentatonic scale and panned to the button; a swish as options unfold and a lower, quieter one as they fold. Nothing plays, and no audio context exists, unless this is on. |
| disabled | boolean | false | Dims the dock, disables its buttons and stops the shortcuts; the system cursor comes back over the canvas. |
| className | string | — | Merged onto the wrapper that holds the rail and the canvas. |