KQ-2108 · FAVOURITE/SHELF
Favourite Shelf
A workspace sidebar with a Favourites shelf at its top that fills by dragging any row up into it: the grabbed row lifts into a layer above the sidebar on springs.flick and follows the pointer 1:1 while a dashed ghost holds its place, and over the shelf the favourites part on springs.glide to open a gap that follows the pointer as the shelf's height glides open by a row. Let go over the gap and the row glides in with the release velocity, then lands with a short squash from a springs.recoil spring kicked by the impact; let go elsewhere and it glides home, and a favourite dragged out below the shelf comes off it. Every row is one roving list: Alt+F adds or removes a favourite with the same flight, Alt with Up or Down reorders one, Delete removes one, and the folded rail turns the shelf into a strip of icons.
shelf 2 of 5 · Roadmap, Q4 launch · on Roadmap
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/favourite-shelf
Props
- shelfStyle
- "tray" | "ruled" | "card"
- default "tray"
- How the shelf is drawn: an inset tray, hairline rules, or a raised card with an accent edge.
- rowHeight
- number
- default 32
- Every row's height, in px; the gap, the landing and the shelf's height follow it.
- accent
- "cobalt" | "plum" | "mint" | "ink" | string
- default "cobalt"
- Accent for the current page, the landing pad, the stars and the shelf meter. A name or any CSS colour.
- sections
- ShelfSection[]
- default defaultShelfSections
- The sections under the shelf, in order: { id, label }.
- items
- ShelfItem[]
- default defaultShelfItems
- Every row, in order within its section: { id, label, section, icon, count? }.
- favourites / defaultFavourites
- string[]
- default — / defaultShelfFavourites
- Item ids on the shelf in shelf order, controlled or initial.
- onFavouritesChange
- (ids: string[]) => void
- default —
- Fires with the whole new shelf after any add, remove or reorder.
- onFavourite
- (id: string, index: number) => void
- default —
- An item landed on the shelf, at this index.
- onUnfavourite
- (id: string) => void
- default —
- An item left the shelf.
- onReorderFavourites
- (ids: string[]) => void
- default —
- The shelf's order changed, by a drag or by Alt with an arrow.
- limit
- number
- default 5
- The most the shelf holds; a full shelf opens no gap and says so.
- current / defaultCurrent
- string
- default — / "roadmap"
- The open page's id, controlled or initial.
- onNavigate
- (id: string) => void
- default —
- A row was opened by a press, Enter or Space.
- folded / defaultFolded
- boolean
- default — / false
- The sidebar as a 60px rail, controlled or initial.
- onFoldedChange
- (folded: boolean) => void
- default —
- Fires from the button that folded or opened the sidebar.
- workspace
- string
- default "Basinworks"
- The workspace's name in the header.
- status
- "ready" | "loading" | "error"
- default "ready"
- Whether the sidebar's data has arrived: skeleton rows while loading, a message with Try again on error.
- onRetry
- () => void
- default —
- Try again after an error.
- label
- string
- default —
- The navigation's accessible name. Defaults to "<workspace> sidebar".
- className
- string
- default —
- Classes for the outer container.
| Prop | Type | Default | Description |
|---|---|---|---|
| shelfStyle | "tray" | "ruled" | "card" | "tray" | How the shelf is drawn: an inset tray, hairline rules, or a raised card with an accent edge. |
| rowHeight | number | 32 | Every row's height, in px; the gap, the landing and the shelf's height follow it. |
| accent | "cobalt" | "plum" | "mint" | "ink" | string | "cobalt" | Accent for the current page, the landing pad, the stars and the shelf meter. A name or any CSS colour. |
| sections | ShelfSection[] | defaultShelfSections | The sections under the shelf, in order: { id, label }. |
| items | ShelfItem[] | defaultShelfItems | Every row, in order within its section: { id, label, section, icon, count? }. |
| favourites / defaultFavourites | string[] | — / defaultShelfFavourites | Item ids on the shelf in shelf order, controlled or initial. |
| onFavouritesChange | (ids: string[]) => void | — | Fires with the whole new shelf after any add, remove or reorder. |
| onFavourite | (id: string, index: number) => void | — | An item landed on the shelf, at this index. |
| onUnfavourite | (id: string) => void | — | An item left the shelf. |
| onReorderFavourites | (ids: string[]) => void | — | The shelf's order changed, by a drag or by Alt with an arrow. |
| limit | number | 5 | The most the shelf holds; a full shelf opens no gap and says so. |
| current / defaultCurrent | string | — / "roadmap" | The open page's id, controlled or initial. |
| onNavigate | (id: string) => void | — | A row was opened by a press, Enter or Space. |
| folded / defaultFolded | boolean | — / false | The sidebar as a 60px rail, controlled or initial. |
| onFoldedChange | (folded: boolean) => void | — | Fires from the button that folded or opened the sidebar. |
| workspace | string | "Basinworks" | The workspace's name in the header. |
| status | "ready" | "loading" | "error" | "ready" | Whether the sidebar's data has arrived: skeleton rows while loading, a message with Try again on error. |
| onRetry | () => void | — | Try again after an error. |
| label | string | — | The navigation's accessible name. Defaults to "<workspace> sidebar". |
| className | string | — | Classes for the outer container. |