KQ-2127 · COUNT/TAKEOVER
Count Takeover
A list header that changes role when rows are picked: the title leaves upward on the exit ease while the count rises in from below on springs.snap and the selection's actions rise after it in a cascade() stagger on snap, and clearing runs the same path backwards, the actions sinking in reverse order before the title comes back down. Each digit of the count is its own wheel that rolls on snap in the direction the count moved, and every action can answer a mixed selection with a reason — it stays focusable but aria-disabled, and hovering or focusing it slides the sentence in under the header. The list is a multi-select listbox (Space toggles, Shift with arrows or a click extends), Cmd or Ctrl+A selects all, Escape clears, and on narrow widths the secondary actions fold into a More menu.
Shipments
48 in transit · updated 4 min ago
no selection · last: none yet
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/count-takeover
Props
- items
- CountTakeoverItem[]
- default defaultCountTakeoverItems
- The rows: { id, ref, destination, customer, carrier, state: "draft" | "ready" | "transit" | "delivered" | "held", due }.
- selection / defaultSelection
- string[]
- default []
- Selected row ids, controlled or initial. Ids whose rows have gone are neither shown nor counted.
- onSelectionChange
- (ids: string[]) => void
- default —
- Fires from the click, key, Clear or action that changed the selection.
- actions
- CountTakeoverAction[]
- default defaultCountTakeoverActions
- What the header offers a selection: { id, label, icon, tone?: "default" | "danger", priority?: "primary" | "secondary", reason?: (selected) => string | null }. A reason turns the action off for that selection and is shown on hover or focus.
- idleActions
- CountTakeoverAction[]
- default defaultCountTakeoverIdleActions
- What the header offers with nothing selected; the last one is the primary button.
- onAction
- (actionId: string, ids: string[]) => void
- default —
- Fires when an action is taken, with the ids it applies to (empty for idle actions). An action that is off never fires it.
- onSelectAll
- (ids: string[]) => void
- default —
- Fires when everything is selected by the header checkbox or Cmd/Ctrl+A.
- onClear
- () => void
- default —
- Fires when the selection is cleared by Escape, Clear or the header checkbox.
- clearOnAction
- boolean
- default true
- Drop the selection once an action has been taken, so the header reverses to its title.
- title
- string
- default "Shipments"
- The list's title, and the accessible name of the piece and its listbox.
- meta
- string
- default "48 in transit · updated 4 min ago"
- The line under the title.
- noun
- { one: string; many: string }
- default { one: "shipment", many: "shipments" }
- What one row is called, for the count and the announcements.
- listHeight
- number
- default 168
- Height of the scrolling list, px.
- swapStyle
- "rise" | "push" | "fold"
- default "rise"
- How the header changes role: the title rises out as the count rises in, a sideways push, or a fold about the header's horizontal axis.
- density
- "compact" | "regular" | "roomy"
- default "regular"
- Header and row height: 44/32, 48/40 or 52/48 px.
- accent
- "cobalt" | "plum" | "mint" | "ink" | string
- default "cobalt"
- Checkboxes, the selection wash, the count and the primary action: a named accent or any CSS colour.
- status
- "ready" | "loading" | "error"
- default "ready"
- List state: loading shows skeleton rows, error offers Retry.
- onRetry
- () => void
- default —
- Called by the error state's Retry button.
- emptyText
- string
- default "No shipments match this view."
- Shown when there are no rows.
- disabled
- boolean
- default false
- Turns the list and the header off.
- className
- string
- default —
- Classes for the outer section.
| Prop | Type | Default | Description |
|---|---|---|---|
| items | CountTakeoverItem[] | defaultCountTakeoverItems | The rows: { id, ref, destination, customer, carrier, state: "draft" | "ready" | "transit" | "delivered" | "held", due }. |
| selection / defaultSelection | string[] | [] | Selected row ids, controlled or initial. Ids whose rows have gone are neither shown nor counted. |
| onSelectionChange | (ids: string[]) => void | — | Fires from the click, key, Clear or action that changed the selection. |
| actions | CountTakeoverAction[] | defaultCountTakeoverActions | What the header offers a selection: { id, label, icon, tone?: "default" | "danger", priority?: "primary" | "secondary", reason?: (selected) => string | null }. A reason turns the action off for that selection and is shown on hover or focus. |
| idleActions | CountTakeoverAction[] | defaultCountTakeoverIdleActions | What the header offers with nothing selected; the last one is the primary button. |
| onAction | (actionId: string, ids: string[]) => void | — | Fires when an action is taken, with the ids it applies to (empty for idle actions). An action that is off never fires it. |
| onSelectAll | (ids: string[]) => void | — | Fires when everything is selected by the header checkbox or Cmd/Ctrl+A. |
| onClear | () => void | — | Fires when the selection is cleared by Escape, Clear or the header checkbox. |
| clearOnAction | boolean | true | Drop the selection once an action has been taken, so the header reverses to its title. |
| title | string | "Shipments" | The list's title, and the accessible name of the piece and its listbox. |
| meta | string | "48 in transit · updated 4 min ago" | The line under the title. |
| noun | { one: string; many: string } | { one: "shipment", many: "shipments" } | What one row is called, for the count and the announcements. |
| listHeight | number | 168 | Height of the scrolling list, px. |
| swapStyle | "rise" | "push" | "fold" | "rise" | How the header changes role: the title rises out as the count rises in, a sideways push, or a fold about the header's horizontal axis. |
| density | "compact" | "regular" | "roomy" | "regular" | Header and row height: 44/32, 48/40 or 52/48 px. |
| accent | "cobalt" | "plum" | "mint" | "ink" | string | "cobalt" | Checkboxes, the selection wash, the count and the primary action: a named accent or any CSS colour. |
| status | "ready" | "loading" | "error" | "ready" | List state: loading shows skeleton rows, error offers Retry. |
| onRetry | () => void | — | Called by the error state's Retry button. |
| emptyText | string | "No shipments match this view." | Shown when there are no rows. |
| disabled | boolean | false | Turns the list and the header off. |
| className | string | — | Classes for the outer section. |