KQ-1641 · NESTED/TICKS
Nested Ticks
A filter tree of regions, crews and people with tri-state ticks and a summary that says the choice in words ("All of Coldbrook crew, 2 of Basinworks crew"). Ticking a parent sends a ripple down its open branch: each child whose tick changes lands on springs.snap a cascade step after the one above, its check drawing on springs.flick under a passing wash, while partial parents fill their tick halfway like a level; branches open on springs.glide and lazy ones load with a spinner row or offer Retry. It is an APG tree with one roving tab stop: arrows move, open and close, Space ticks, asterisk opens every sibling, and typed letters jump to a match while dimming the rest.
Up and Down move. Right opens a branch, Left closes it. Space ticks. Asterisk opens every sibling. Type letters to find a name.
Field crews
7 of 28 people ticked
All of Coldbrook crew, 2 of Basinworks crew
7 of 28 people · ticked
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/nested-ticks
Props
- indent
- number
- default 18
- Pixels of indent per level; narrow containers indent at 60%.
- tickStyle
- "check" | "ring" | "gauge"
- default "check"
- A square with a drawn check, a ring with a dot, or a slim gauge that fills; partial parents fill halfway in each.
- accent
- "cobalt" | "signal" | "amber" | "graphite" | string
- default "cobalt"
- Tick and ripple colour: a named token, or any CSS colour.
- nodes
- NestedTickNode[]
- default defaultNestedTickNodes
- The tree; a node may be lazy, with a count of the leaves it holds before it loads.
- value / defaultValue
- string[]
- default defaultNestedTickValue
- The ticked leaves (an unloaded lazy branch counts as a leaf). Controlled or initial.
- onChange
- (leaves: string[]) => void
- default —
- Fires from the press or key that changed the ticks, with the leaf set in tree order.
- expanded / defaultExpanded
- string[]
- default defaultNestedTickExpanded
- The open branches. Controlled or initial.
- onExpandedChange
- (expanded: string[]) => void
- default —
- Fires when a branch opens or closes, with every open branch.
- onLoadChildren
- (node: NestedTickNode) => Promise<NestedTickNode[]>
- default —
- Loads a lazy branch's children on first open; a rejection offers a Retry row.
- stagger
- number
- default cascade
- Milliseconds between ticks in a ripple, capped so a ripple stays under 600 ms.
- typeaheadMs
- number
- default 900
- How long typed letters keep finding and dimming.
- noun
- { one: string; other: string }
- default { one: "person", other: "people" }
- What one leaf is called in counts and the summary.
- title
- string
- default "Field crews"
- The heading.
- status
- "ready" | "loading" | "error"
- default "ready"
- Whether the tree has arrived: skeleton rows while loading, a retry when it failed.
- onRetry
- () => void
- default —
- Try again was pressed after the tree failed to load.
- label
- string
- default the title
- The tree's accessible name.
- disabled
- boolean
- default false
- Read only: nothing can be ticked.
- className
- string
- default —
- Classes for the surface; give it a height and the tree scrolls inside it.
| Prop | Type | Default | Description |
|---|---|---|---|
| indent | number | 18 | Pixels of indent per level; narrow containers indent at 60%. |
| tickStyle | "check" | "ring" | "gauge" | "check" | A square with a drawn check, a ring with a dot, or a slim gauge that fills; partial parents fill halfway in each. |
| accent | "cobalt" | "signal" | "amber" | "graphite" | string | "cobalt" | Tick and ripple colour: a named token, or any CSS colour. |
| nodes | NestedTickNode[] | defaultNestedTickNodes | The tree; a node may be lazy, with a count of the leaves it holds before it loads. |
| value / defaultValue | string[] | defaultNestedTickValue | The ticked leaves (an unloaded lazy branch counts as a leaf). Controlled or initial. |
| onChange | (leaves: string[]) => void | — | Fires from the press or key that changed the ticks, with the leaf set in tree order. |
| expanded / defaultExpanded | string[] | defaultNestedTickExpanded | The open branches. Controlled or initial. |
| onExpandedChange | (expanded: string[]) => void | — | Fires when a branch opens or closes, with every open branch. |
| onLoadChildren | (node: NestedTickNode) => Promise<NestedTickNode[]> | — | Loads a lazy branch's children on first open; a rejection offers a Retry row. |
| stagger | number | cascade | Milliseconds between ticks in a ripple, capped so a ripple stays under 600 ms. |
| typeaheadMs | number | 900 | How long typed letters keep finding and dimming. |
| noun | { one: string; other: string } | { one: "person", other: "people" } | What one leaf is called in counts and the summary. |
| title | string | "Field crews" | The heading. |
| status | "ready" | "loading" | "error" | "ready" | Whether the tree has arrived: skeleton rows while loading, a retry when it failed. |
| onRetry | () => void | — | Try again was pressed after the tree failed to load. |
| label | string | the title | The tree's accessible name. |
| disabled | boolean | false | Read only: nothing can be ticked. |
| className | string | — | Classes for the surface; give it a height and the tree scrolls inside it. |