KQ-1123 · SLOT/SWIPE
Slot Swipe
An inbox whose rows sit between a mail slot and a drawer. Swipe a row toward the slot and the slit opens to meet it while the row feeds in edgewise, clipped at the slit and squashed thinner the deeper it goes; past the threshold (or thrown past it, by projection) it runs in on the snap spring with its release velocity and the slot shuts on the recoil spring with a thwup. Swipe toward the drawer and the drawer slides out with a clock that winds and ticks as you pull; the row drops in, the clock spins round to the snooze hour and the drawer shuts. A short swipe springs back, Delete and S do the same as the swipes with the same frames and sounds, and Undo brings the last row back out of wherever it went.
Inbox
5Swipe toward the slot or press Delete to archive. Swipe toward the drawer or press S to snooze until 9:00.
5 in inbox · 0 archived · 0 snoozed
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/slot-swipe
Props
- items
- { id: string; from: string; subject: string; time?: string }[]
- default —
- Every row that can appear.
- value / defaultValue
- string[]
- default every item, in order
- The ids still in the inbox, in order. Controlled or uncontrolled; a row the host keeps after a swipe comes back out of the slot or drawer.
- onValueChange
- (ids: string[]) => void
- default —
- Fires from the swipe, key or Undo that changed the list, with the ids left — at the commit, while the row is still going in.
- onAction
- (id: string, action: "archive" | "snooze") => void
- default —
- Fires with what happened to a row: into the slot (archive) or into the drawer (snooze).
- onOpen
- (id: string) => void
- default —
- A row pressed without a swipe, or Enter or Space on it.
- label
- string
- default "Inbox"
- The list's name, shown in the header with the count.
- snoozeHour
- number
- default 9
- The hour the drawer's clock spins to, 0 to 23; also the time in the announcement and the header.
- threshold
- number
- default 0.35
- How far a row must go, or be thrown, as a share of its lane (0.2 to 0.6) before it commits. The slot and drawer are fully open, and light up, at this point.
- side
- "right" | "left"
- default "right"
- Which end of each lane the slot is on; the drawer takes the other.
- squash
- number
- default 0.5
- How much a row squeezes to fit the slot or the drawer, 0 to 1: up to 60% thinner, and a little longer along the swipe.
- undo
- boolean
- default true
- An Undo button in the header after each swipe, which brings the row back out of where it went.
- empty
- ReactNode
- default "Inbox zero"
- Shown when nothing is left.
- sound
- boolean
- default false
- A detent as a row arms, paper as it feeds into the slot and a thwup as the slot shuts; the drawer's clock ticks as it winds and spins, and the drawer shuts with a clack. Nothing plays, and no audio context exists, unless this is on.
- disabled
- boolean
- default false
- Dims the rows and stops every swipe, key and Undo.
- className
- string
- default —
- Merged onto the outer wrapper.
| Prop | Type | Default | Description |
|---|---|---|---|
| items | { id: string; from: string; subject: string; time?: string }[] | — | Every row that can appear. |
| value / defaultValue | string[] | every item, in order | The ids still in the inbox, in order. Controlled or uncontrolled; a row the host keeps after a swipe comes back out of the slot or drawer. |
| onValueChange | (ids: string[]) => void | — | Fires from the swipe, key or Undo that changed the list, with the ids left — at the commit, while the row is still going in. |
| onAction | (id: string, action: "archive" | "snooze") => void | — | Fires with what happened to a row: into the slot (archive) or into the drawer (snooze). |
| onOpen | (id: string) => void | — | A row pressed without a swipe, or Enter or Space on it. |
| label | string | "Inbox" | The list's name, shown in the header with the count. |
| snoozeHour | number | 9 | The hour the drawer's clock spins to, 0 to 23; also the time in the announcement and the header. |
| threshold | number | 0.35 | How far a row must go, or be thrown, as a share of its lane (0.2 to 0.6) before it commits. The slot and drawer are fully open, and light up, at this point. |
| side | "right" | "left" | "right" | Which end of each lane the slot is on; the drawer takes the other. |
| squash | number | 0.5 | How much a row squeezes to fit the slot or the drawer, 0 to 1: up to 60% thinner, and a little longer along the swipe. |
| undo | boolean | true | An Undo button in the header after each swipe, which brings the row back out of where it went. |
| empty | ReactNode | "Inbox zero" | Shown when nothing is left. |
| sound | boolean | false | A detent as a row arms, paper as it feeds into the slot and a thwup as the slot shuts; the drawer's clock ticks as it winds and spins, and the drawer shuts with a clack. Nothing plays, and no audio context exists, unless this is on. |
| disabled | boolean | false | Dims the rows and stops every swipe, key and Undo. |
| className | string | — | Merged onto the outer wrapper. |