KQ-1308 · NOTICE/CENTER
Notice Center
A notification center whose groups stack into a deck: pressing one deals it out, each hidden row starting where its layer was and gliding down to its place on glide in a cascade while the group's measured height glides open, and Show less gathers them back on the exit ease. A row or a whole stack follows a sideways swipe 1:1, is thrown off on the exit ease when its projected rest passes 40% of its width (or snaps back on snap with the release velocity), and the rows under it close the gap on glide. Mark all read sends a band down the list that clears each unread row as it passes — the dot shrinks on flick — the tab pill slides on snap and times roll as `now` moves; arrows walk the rows, Enter opens, Right deals and Left gathers a stack, Delete dismisses.
Notifications7 unread
7 unread · 12 notices
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/notice-center
Props
- notices / defaultNotices
- Notice[]
- default defaultNotices
- Every notice: its source, title, body, time, read state and whether it mentions you. Controlled or uncontrolled.
- onNoticesChange
- (notices: Notice[]) => void
- default —
- Fires from the dismiss, open or sweep that changed the list, with the whole new list.
- sources
- NoticeSource[]
- default defaultNoticeSources
- Where notices come from: a name, an icon and a colour for each.
- now
- Date | number
- default defaultNoticeNow
- The moment every notice is dated against. Move it and the times roll over; days are UTC days.
- filter / defaultFilter
- "all" | "unread" | "mentions"
- default "all"
- The selected tab. Controlled or uncontrolled.
- onFilterChange
- (filter: NoticeFilter) => void
- default —
- Fires when a tab is chosen by press or arrow key.
- selected / defaultSelected
- string | null
- default null
- The open notice, shown in the detail pane from 600px wide. Controlled or uncontrolled.
- onSelectedChange
- (id: string | null) => void
- default —
- Fires when opening or dismissing changes the open notice.
- onOpen
- (id: string) => void
- default —
- A notice was opened by press or Enter. It is marked read first.
- onDismiss
- (ids: string[]) => void
- default —
- Notices were dismissed by swipe, key or button: one, or a whole collapsed group.
- onMarkAllRead
- (ids: string[]) => void
- default —
- Mark all read cleared these notices.
- onReadChange
- (id: string, read: boolean) => void
- default —
- One notice was marked read or unread.
- onRetry
- () => void
- default —
- The Retry button of the error state.
- stack
- "deck" | "fan" | "flat"
- default "deck"
- How a collapsed group shows what is under its newest notice: layers peeking below, the same layers fanned, or a flat “+3”.
- sweep
- number
- default 480
- How long Mark all read's band takes to cross the list, in ms.
- group
- "source" | "day" | "off"
- default "source"
- Group by source into collapsible stacks, by day under sticky Today, Yesterday and Earlier headings, or not at all.
- title
- string
- default "Notifications"
- The panel's heading and accessible name.
- status
- "ready" | "loading" | "error"
- default "ready"
- Loading draws placeholder rows; error offers Retry.
- label
- string
- default —
- The region's accessible name, when it should differ from the title.
- sound
- boolean
- default false
- Play a swish on dismiss and Mark all read and a pop on dealing, gathering and read changes. Off unless asked for.
- disabled
- boolean
- default false
- Shows the notices but takes no presses, swipes or keys.
- className
- string
- default —
- Classes for the root. The root is h-[560px] by default; pass a height to change it.
| Prop | Type | Default | Description |
|---|---|---|---|
| notices / defaultNotices | Notice[] | defaultNotices | Every notice: its source, title, body, time, read state and whether it mentions you. Controlled or uncontrolled. |
| onNoticesChange | (notices: Notice[]) => void | — | Fires from the dismiss, open or sweep that changed the list, with the whole new list. |
| sources | NoticeSource[] | defaultNoticeSources | Where notices come from: a name, an icon and a colour for each. |
| now | Date | number | defaultNoticeNow | The moment every notice is dated against. Move it and the times roll over; days are UTC days. |
| filter / defaultFilter | "all" | "unread" | "mentions" | "all" | The selected tab. Controlled or uncontrolled. |
| onFilterChange | (filter: NoticeFilter) => void | — | Fires when a tab is chosen by press or arrow key. |
| selected / defaultSelected | string | null | null | The open notice, shown in the detail pane from 600px wide. Controlled or uncontrolled. |
| onSelectedChange | (id: string | null) => void | — | Fires when opening or dismissing changes the open notice. |
| onOpen | (id: string) => void | — | A notice was opened by press or Enter. It is marked read first. |
| onDismiss | (ids: string[]) => void | — | Notices were dismissed by swipe, key or button: one, or a whole collapsed group. |
| onMarkAllRead | (ids: string[]) => void | — | Mark all read cleared these notices. |
| onReadChange | (id: string, read: boolean) => void | — | One notice was marked read or unread. |
| onRetry | () => void | — | The Retry button of the error state. |
| stack | "deck" | "fan" | "flat" | "deck" | How a collapsed group shows what is under its newest notice: layers peeking below, the same layers fanned, or a flat “+3”. |
| sweep | number | 480 | How long Mark all read's band takes to cross the list, in ms. |
| group | "source" | "day" | "off" | "source" | Group by source into collapsible stacks, by day under sticky Today, Yesterday and Earlier headings, or not at all. |
| title | string | "Notifications" | The panel's heading and accessible name. |
| status | "ready" | "loading" | "error" | "ready" | Loading draws placeholder rows; error offers Retry. |
| label | string | — | The region's accessible name, when it should differ from the title. |
| sound | boolean | false | Play a swish on dismiss and Mark all read and a pop on dealing, gathering and read changes. Off unless asked for. |
| disabled | boolean | false | Shows the notices but takes no presses, swipes or keys. |
| className | string | — | Classes for the root. The root is h-[560px] by default; pass a height to change it. |