KQ-1175 · KEY/RING
Key Ring
A workspace switcher drawn as keys hanging from a split ring, each bow capped in the workspace's colour and printed with its initial, each blade cut with teeth seeded from its id. Choosing turns the ring the short way on snap so the chosen key hangs at the front, a click ticking as each key passes; the keys are pendulums that trail the ring, swing and knock when it stops with a damping set by jingle, and a new workspace threads on at the split and slides round to its place on glide. The ring is a focusable listbox: arrow keys turn it and choose, Home and End go to the ends, a letter turns to the next workspace with that initial, and a horizontal drag spins it 1:1 and settles on the key the throw was heading for.
Coldbrook Bank · 7 workspaces · drag to turn
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/key-ring
Props
- workspaces
- { id: string; name: string; detail?: string; initial?: string; color?: string }[]
- default —
- The workspaces, one key each. detail is the second line under the chosen name; color is any CSS colour for the bow, one of six pigments by default.
- value / defaultValue
- string
- default —
- The chosen workspace's id, whose key hangs at the front.
- onValueChange
- (id: string) => void
- default —
- Fires from the key, click or drag that chose a workspace, with its id. A controlled ring that is refused turns back.
- onAdd
- () => void
- default —
- Shows an Add button at the ring's split end. A workspace the host then adds threads onto the ring there.
- addLabel
- string
- default "Add workspace"
- The Add button's accessible name.
- label
- string
- default "Workspaces"
- The listbox's accessible name.
- keys
- number
- default 5
- How many keys hang on the ring at once, 3 to 7. With more workspaces the ring turns through all of them, a key re-cut for the next one as it passes behind.
- ring
- "steel" | "brass" | "black"
- default "steel"
- The metal of the ring and the blades, drawn as fixed pigments so both themes read the same.
- jingle
- number
- default 0.5
- How freely the keys swing and knock when the ring turns, 0 (stiff and silent) to 1.
- sound
- boolean
- default false
- Play a click as keys pass the front and when a key is threaded, and a clack when keys knock, only after the visitor's own turn.
- disabled
- boolean
- default false
- The ring cannot be turned and Add is off.
- className
- string
- default —
- Classes for the root: a 272 px drawing over the chosen name.
| Prop | Type | Default | Description |
|---|---|---|---|
| workspaces | { id: string; name: string; detail?: string; initial?: string; color?: string }[] | — | The workspaces, one key each. detail is the second line under the chosen name; color is any CSS colour for the bow, one of six pigments by default. |
| value / defaultValue | string | — | The chosen workspace's id, whose key hangs at the front. |
| onValueChange | (id: string) => void | — | Fires from the key, click or drag that chose a workspace, with its id. A controlled ring that is refused turns back. |
| onAdd | () => void | — | Shows an Add button at the ring's split end. A workspace the host then adds threads onto the ring there. |
| addLabel | string | "Add workspace" | The Add button's accessible name. |
| label | string | "Workspaces" | The listbox's accessible name. |
| keys | number | 5 | How many keys hang on the ring at once, 3 to 7. With more workspaces the ring turns through all of them, a key re-cut for the next one as it passes behind. |
| ring | "steel" | "brass" | "black" | "steel" | The metal of the ring and the blades, drawn as fixed pigments so both themes read the same. |
| jingle | number | 0.5 | How freely the keys swing and knock when the ring turns, 0 (stiff and silent) to 1. |
| sound | boolean | false | Play a click as keys pass the front and when a key is threaded, and a clack when keys knock, only after the visitor's own turn. |
| disabled | boolean | false | The ring cannot be turned and Add is off. |
| className | string | — | Classes for the root: a 272 px drawing over the chosen name. |