KQ-1338 · SUBSCRIPTION/MANAGER
Subscription Manager
A subscription whose schedule is a row of dots you can push around: every upcoming delivery is a dot keyed by its place in the schedule, so skipping hollows it and slides the Next flag to the box that will ship on snap, a new cadence keeps the next box where it is and sends every later dot to its new day on glide, and a pause draws a hatched band across the days it covers. The strip of days drags 1:1, rubber-bands and coasts on glide with the throw's velocity (or turns month pages by direction), the cadence is chips or a detent slider that reshapes the dots live, and the next box swaps by dragging a coffee off the shelf onto a slot, both products flying from where they were on glide. Dots are toggle buttons with roving arrows, the cadence a radiogroup or slider, and a held product is placed with the arrows and Enter.
next box thu oct 8 · every 2 weeks · $34.45
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/subscription-manager
Props
- calendar
- "strip" | "month"
- default "strip"
- How the schedule is drawn: a strip of days that drags, coasts and rubber-bands, or month pages (two side by side from 40rem) whose dots hop between days on a cadence change.
- frequency
- "chips" | "slider"
- default "chips"
- How the cadence is chosen: a radiogroup of the common cadences with a sliding pill, or a detent slider from 1 to maxCadence weeks that reshapes the dots live as the thumb crosses each detent.
- swap
- "shelf" | "menu"
- default "shelf"
- How the next box is changed: drag (or pick up and place) a product from a shelf of alternatives onto a slot, or a Swap button on each slot that opens a listbox with the price difference.
- plan
- SubscriptionPlan
- default defaultSubscriptionPlan
- The merchant, plan name, the anchor delivery date (ms), per-box shipping, the payment it is charged to and how many days before a delivery it is charged.
- products
- SubscriptionProduct[]
- default defaultSubscriptionProducts
- Everything that can go in a box: name, detail, price, a packaging drawing (beans, tin, tea) and a tint.
- value / defaultValue
- SubscriptionValue
- default defaultSubscriptionValue
- The cadence in weeks, the skipped dates (YYYY-MM-DD), the resume date of a pause or null, and the product ids in the next box. Controlled or uncontrolled.
- onValueChange
- (value: SubscriptionValue) => void
- default —
- Fires from the press, drag or key that changed the plan. A cadence change also drops skips that fall off the new schedule.
- onSkip
- (date: string, skipped: boolean) => void
- default —
- A delivery was skipped or brought back.
- onSwap
- (slot: number, fromId: string, toId: string) => void
- default —
- A slot's product was swapped.
- onPause
- (until: string | null) => void
- default —
- Paused until a date, or resumed with null.
- onCadenceChange
- (weeks: number) => void
- default —
- The cadence changed: once per chip, once per detent the slider crosses.
- cadences
- number[]
- default [1, 2, 4]
- The chips' cadences, in weeks.
- maxCadence
- number
- default 8
- The slider's longest cadence, in weeks.
- horizon
- number
- default 10
- Weeks of schedule shown on the strip and across the month pages.
- freeShippingOver
- number
- default 40
- Boxes at or over this amount ship free; the total rolls as a swap crosses it.
- now
- number | Date
- default defaultSubscriptionNow
- Today on the calendar and the base for the pause choices, never the wall clock.
- formatPrice
- (amount: number) => string
- default US dollars
- Turns amounts into text.
- title
- string
- default "Subscription"
- The heading, and the region's name unless label is given.
- status
- "ready" | "loading" | "error"
- default "ready"
- Whether the plan has arrived: loading shows skeleton panels, error a message with Try again.
- onRetry
- () => void
- default —
- Try again was pressed after the plan failed to load.
- label
- string
- default —
- The region's accessible name, when the visible title is not enough.
- sound
- boolean
- default false
- Clicks for skips, detents, pick-ups and pauses; a swish when a swap lands or a cadence chip sends the dots travelling.
- disabled
- boolean
- default false
- Read only: the schedule and box still show, nothing changes.
- className
- string
- default —
- Classes for the root. It is at most 560px tall and scrolls inside itself.
| Prop | Type | Default | Description |
|---|---|---|---|
| calendar | "strip" | "month" | "strip" | How the schedule is drawn: a strip of days that drags, coasts and rubber-bands, or month pages (two side by side from 40rem) whose dots hop between days on a cadence change. |
| frequency | "chips" | "slider" | "chips" | How the cadence is chosen: a radiogroup of the common cadences with a sliding pill, or a detent slider from 1 to maxCadence weeks that reshapes the dots live as the thumb crosses each detent. |
| swap | "shelf" | "menu" | "shelf" | How the next box is changed: drag (or pick up and place) a product from a shelf of alternatives onto a slot, or a Swap button on each slot that opens a listbox with the price difference. |
| plan | SubscriptionPlan | defaultSubscriptionPlan | The merchant, plan name, the anchor delivery date (ms), per-box shipping, the payment it is charged to and how many days before a delivery it is charged. |
| products | SubscriptionProduct[] | defaultSubscriptionProducts | Everything that can go in a box: name, detail, price, a packaging drawing (beans, tin, tea) and a tint. |
| value / defaultValue | SubscriptionValue | defaultSubscriptionValue | The cadence in weeks, the skipped dates (YYYY-MM-DD), the resume date of a pause or null, and the product ids in the next box. Controlled or uncontrolled. |
| onValueChange | (value: SubscriptionValue) => void | — | Fires from the press, drag or key that changed the plan. A cadence change also drops skips that fall off the new schedule. |
| onSkip | (date: string, skipped: boolean) => void | — | A delivery was skipped or brought back. |
| onSwap | (slot: number, fromId: string, toId: string) => void | — | A slot's product was swapped. |
| onPause | (until: string | null) => void | — | Paused until a date, or resumed with null. |
| onCadenceChange | (weeks: number) => void | — | The cadence changed: once per chip, once per detent the slider crosses. |
| cadences | number[] | [1, 2, 4] | The chips' cadences, in weeks. |
| maxCadence | number | 8 | The slider's longest cadence, in weeks. |
| horizon | number | 10 | Weeks of schedule shown on the strip and across the month pages. |
| freeShippingOver | number | 40 | Boxes at or over this amount ship free; the total rolls as a swap crosses it. |
| now | number | Date | defaultSubscriptionNow | Today on the calendar and the base for the pause choices, never the wall clock. |
| formatPrice | (amount: number) => string | US dollars | Turns amounts into text. |
| title | string | "Subscription" | The heading, and the region's name unless label is given. |
| status | "ready" | "loading" | "error" | "ready" | Whether the plan has arrived: loading shows skeleton panels, error a message with Try again. |
| onRetry | () => void | — | Try again was pressed after the plan failed to load. |
| label | string | — | The region's accessible name, when the visible title is not enough. |
| sound | boolean | false | Clicks for skips, detents, pick-ups and pauses; a swish when a swap lands or a cadence chip sends the dots travelling. |
| disabled | boolean | false | Read only: the schedule and box still show, nothing changes. |
| className | string | — | Classes for the root. It is at most 560px tall and scrolls inside itself. |