KQ-1324 · BILLING/PANEL
Billing Panel
A billing settings panel built from data: the cards on file are a deck that sits in a pile and fans out like a hand, unfolds into wallet strips, or lies flat, every pose a glide from one function of the deck's measured width; Make default lifts the chosen card, slides it to the front on glide and the Default tag slides across to it on snap. The next bill rolls digit by digit on snap as you drag the projected-usage slider 1:1 — it rubber-bands against what has already been used and lands on its step with the throw's velocity — and invoices download through a promise with a turning ring and a check drawn on flick. The deck is a tablist (arrows open the pile and move, Escape folds it), the projection a slider (arrows, Page keys, Home and End).
default coldbrook bank ···· 4417 · next bill $139.32 · due oct 1
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/billing-panel
Props
- methods
- BillingMethod[]
- default defaultBillingMethods
- The cards on file: id, issuer, kind, last four, expiry, holder, an invented network mark and a tint (any CSS colour, kept at a fixed lightness).
- primaryMethod / defaultPrimaryMethod
- string
- default —
- The id of the default card — the one charged. Controlled or uncontrolled; defaults to the first method.
- onPrimaryMethodChange
- (id: string) => void
- default —
- Fires from Make default with the new default's id. A controlled host that refuses it leaves the deck as it was.
- expanded / defaultExpanded
- boolean
- default false
- Whether the deck is fanned open. Controlled or uncontrolled; ignored when stack is "none".
- onExpandedChange
- (expanded: boolean) => void
- default —
- Fires from the press, arrow key or Escape that opened or folded the deck.
- onAddMethod
- () => void
- default —
- Add was pressed. Without it there is no Add button.
- onRemoveMethod
- (id: string) => void
- default —
- Remove was pressed on a card that is not the default. Without it there is no Remove button; remove the card from methods to see it leave.
- plan
- BillingPlan
- default defaultBillingPlan
- The plan: name, price per cycle, the next bill's date (YYYY-MM-DD) and the cycle's length in days.
- meter
- BillingMeter
- default defaultBillingMeter
- The metered part of the bill: name and unit, used so far, included, the slider's max and step, and tier prices per 1,000 units above the allowance.
- taxRate
- number
- default 0.08
- Tax on plan and usage, 0 to 1.
- usage / defaultUsage
- number
- default —
- The projected usage for this cycle: the slider's value. Controlled or uncontrolled; defaults to the pace so far carried to the cycle's end.
- onUsageChange
- (usage: number) => void
- default —
- Fires from the drag or key that moved the projection, once per step it lands on.
- invoices
- BillingInvoice[]
- default defaultBillingInvoices
- Past invoices, newest first: number, date, amount, status (paid, open, failed, refunded) and an optional description line.
- onDownload
- (invoice: BillingInvoice) => void | Promise<unknown>
- default —
- Download was pressed. Return a promise and the row waits with a turning ring, then shows a check (or the error, and retries on the next press).
- onChangePlan
- () => void
- default —
- Change plan was pressed. Without it there is no button.
- now
- number | Date
- default defaultBillingNow
- Today: how far into the cycle the bill is, which sets the pace and the days left.
- format
- (amount: number) => string
- default —
- Money everywhere in the panel. Defaults to currency in locale.
- formatUsage
- (units: number) => string
- default —
- Usage counts. Defaults to compact: 182k, 1.2M.
- formatDate
- (date: string) => string
- default —
- Invoice dates from YYYY-MM-DD. Defaults to "Sep 1, 2026".
- locale
- string
- default "en-US"
- The default money format's locale. Fixed, so the server and the browser print the same.
- currency
- string
- default "USD"
- The default money format's currency.
- stack
- "fan" | "wallet" | "none"
- default "fan"
- How the cards sit: a pile that fans out like a hand, a pile that unfolds into strips, or a flat grid.
- estimate
- "total" | "itemized"
- default "itemized"
- How the next bill reads: one rolling total with a caption, or the total plus plan, usage and tax lines (each rolling) over a bar that re-proportions on glide.
- density
- "compact" | "regular" | "comfortable"
- default "regular"
- Padding, invoice row height and how much each row says: one line, two, or two plus its description.
- status
- "ready" | "loading" | "error"
- default "ready"
- Whether the billing data has arrived: a skeleton while loading, a message with Try again on error.
- onRetry
- () => void
- default —
- Try again was pressed while status is "error".
- title
- string
- default "Billing"
- The panel's heading; also its accessible name unless label is given.
- label
- string
- default —
- The panel's accessible name. Defaults to the title.
- sound
- boolean
- default false
- A click for each press (deck, card, Make default, Remove, Add, Download, Back to pace) and a tick, pitched by usage, for each step the projection crosses.
- disabled
- boolean
- default false
- Read only: nothing opens, moves or downloads.
- className
- string
- default —
- Classes for the root. It is 560px tall and scrolls inside itself; pass a height class to change it.
| Prop | Type | Default | Description |
|---|---|---|---|
| methods | BillingMethod[] | defaultBillingMethods | The cards on file: id, issuer, kind, last four, expiry, holder, an invented network mark and a tint (any CSS colour, kept at a fixed lightness). |
| primaryMethod / defaultPrimaryMethod | string | — | The id of the default card — the one charged. Controlled or uncontrolled; defaults to the first method. |
| onPrimaryMethodChange | (id: string) => void | — | Fires from Make default with the new default's id. A controlled host that refuses it leaves the deck as it was. |
| expanded / defaultExpanded | boolean | false | Whether the deck is fanned open. Controlled or uncontrolled; ignored when stack is "none". |
| onExpandedChange | (expanded: boolean) => void | — | Fires from the press, arrow key or Escape that opened or folded the deck. |
| onAddMethod | () => void | — | Add was pressed. Without it there is no Add button. |
| onRemoveMethod | (id: string) => void | — | Remove was pressed on a card that is not the default. Without it there is no Remove button; remove the card from methods to see it leave. |
| plan | BillingPlan | defaultBillingPlan | The plan: name, price per cycle, the next bill's date (YYYY-MM-DD) and the cycle's length in days. |
| meter | BillingMeter | defaultBillingMeter | The metered part of the bill: name and unit, used so far, included, the slider's max and step, and tier prices per 1,000 units above the allowance. |
| taxRate | number | 0.08 | Tax on plan and usage, 0 to 1. |
| usage / defaultUsage | number | — | The projected usage for this cycle: the slider's value. Controlled or uncontrolled; defaults to the pace so far carried to the cycle's end. |
| onUsageChange | (usage: number) => void | — | Fires from the drag or key that moved the projection, once per step it lands on. |
| invoices | BillingInvoice[] | defaultBillingInvoices | Past invoices, newest first: number, date, amount, status (paid, open, failed, refunded) and an optional description line. |
| onDownload | (invoice: BillingInvoice) => void | Promise<unknown> | — | Download was pressed. Return a promise and the row waits with a turning ring, then shows a check (or the error, and retries on the next press). |
| onChangePlan | () => void | — | Change plan was pressed. Without it there is no button. |
| now | number | Date | defaultBillingNow | Today: how far into the cycle the bill is, which sets the pace and the days left. |
| format | (amount: number) => string | — | Money everywhere in the panel. Defaults to currency in locale. |
| formatUsage | (units: number) => string | — | Usage counts. Defaults to compact: 182k, 1.2M. |
| formatDate | (date: string) => string | — | Invoice dates from YYYY-MM-DD. Defaults to "Sep 1, 2026". |
| locale | string | "en-US" | The default money format's locale. Fixed, so the server and the browser print the same. |
| currency | string | "USD" | The default money format's currency. |
| stack | "fan" | "wallet" | "none" | "fan" | How the cards sit: a pile that fans out like a hand, a pile that unfolds into strips, or a flat grid. |
| estimate | "total" | "itemized" | "itemized" | How the next bill reads: one rolling total with a caption, or the total plus plan, usage and tax lines (each rolling) over a bar that re-proportions on glide. |
| density | "compact" | "regular" | "comfortable" | "regular" | Padding, invoice row height and how much each row says: one line, two, or two plus its description. |
| status | "ready" | "loading" | "error" | "ready" | Whether the billing data has arrived: a skeleton while loading, a message with Try again on error. |
| onRetry | () => void | — | Try again was pressed while status is "error". |
| title | string | "Billing" | The panel's heading; also its accessible name unless label is given. |
| label | string | — | The panel's accessible name. Defaults to the title. |
| sound | boolean | false | A click for each press (deck, card, Make default, Remove, Add, Download, Back to pace) and a tick, pitched by usage, for each step the projection crosses. |
| disabled | boolean | false | Read only: nothing opens, moves or downloads. |
| className | string | — | Classes for the root. It is 560px tall and scrolls inside itself; pass a height class to change it. |