KQ-1267 · REFRESH/WIND
Refresh Wind
A refresh button you wind like a clock spring: drag around it, or press and hold, and the arrow turns back with the finger while its stroke coils into a tightening spiral and a ring of notches lights and ticks with the tension; let go and a per-frame integrator whips it forward from rest on the spring, then lets it coast on the stored energy against friction and air drag while the refresh runs, settling to a steady cruise if the work outlasts the energy and, once it has answered, decaying exactly onto the nearest upright. A plain press does one turn, and on success the Updated 2 min ago stamp rolls to just now on the snap spring while the notches flash and fade. It is a native button: Enter and Space refresh, Shift+Enter winds a full turn (again for another) and lets go, and Escape cancels a refresh in flight.
Line 3 pressure
Gaugeworks · Basin plant
4.82bar
updated 2 min ago · drag around to wind
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/refresh-wind
Props
- onRefresh
- (signal: AbortSignal) => void | Promise<unknown>
- default —
- Runs the refresh. Return a promise and the arrow keeps turning until it settles; the signal aborts on Escape.
- state
- "idle" | "pending" | "success" | "error"
- default —
- Controlled action state. Every move goes through onStateChange and waits for this.
- onStateChange
- (state: "idle" | "pending" | "success" | "error") => void
- default —
- Each action state, from the press, the answer or the timer that caused it.
- updatedAt / defaultUpdatedAt
- Date | number | null
- default null
- The time of the last successful refresh, controlled or initial. The stamp is written from it.
- onUpdatedAtChange
- (updatedAt: Date) => void
- default —
- The moment of each successful refresh: the now prop's value, or the clock when none is given.
- now
- Date | number
- default a clock started on mount
- The time the stamp is read against. Pass it for a relative stamp that renders on the server; without it the phrase arrives after mount and a shared 30-second clock keeps it current while the page is visible.
- onWind
- (turns: number) => void
- default —
- Fires when a wind is let go, with the turns of spring it stored.
- friction
- number
- default 0.5
- How quickly the released spin slows, 0 to 1: 0 coasts for seconds, 1 stops within a turn or two.
- wind
- number
- default 2
- Turns of spring the button holds: how far a full wind goes back, how tight the coil gets and how long the spin runs.
- stamp
- "relative" | "clock" | "off"
- default "relative"
- The stamp beside the button: Updated 2 min ago, Updated 09:40, or none (an icon-only button whose stamp is its accessible description).
- formatStamp
- (updatedAt: Date, now: Date) => string
- default relative, or a UTC clock
- Writes the stamp from the last refresh and the current time; pass one for the reader's own time zone or language.
- label
- string
- default "Refresh"
- The button's accessible name.
- pendingLabel
- string
- default "Refreshing"
- The name and the stamp while refreshing.
- errorLabel
- string
- default "Couldn't refresh"
- The stamp after a failed refresh; a press retries.
- emptyLabel
- string
- default "Not updated yet"
- The stamp before the first refresh.
- accent
- string
- default "var(--accent-bright)"
- The coil and the tension notches. Any CSS colour.
- successHold
- number
- default 1400
- How long the success flash holds before the button rests, in ms.
- errorHold
- number
- default 2600
- How long a failure holds before the button rests, in ms.
- size
- "sm" | "md" | "lg"
- default "md"
- 32, 40 or 48 px across; the coil and the notch ring scale with it.
- sound
- boolean
- default false
- Play a tick for every notch the wind lights and the whir of the spin while it spends the spring's energy. No audio context exists unless this is on.
- disabled
- boolean
- default false
- Dims the button and lets go of any wind without firing it.
- className
- string
- default —
- Merged onto the root's classes.
| Prop | Type | Default | Description |
|---|---|---|---|
| onRefresh | (signal: AbortSignal) => void | Promise<unknown> | — | Runs the refresh. Return a promise and the arrow keeps turning until it settles; the signal aborts on Escape. |
| state | "idle" | "pending" | "success" | "error" | — | Controlled action state. Every move goes through onStateChange and waits for this. |
| onStateChange | (state: "idle" | "pending" | "success" | "error") => void | — | Each action state, from the press, the answer or the timer that caused it. |
| updatedAt / defaultUpdatedAt | Date | number | null | null | The time of the last successful refresh, controlled or initial. The stamp is written from it. |
| onUpdatedAtChange | (updatedAt: Date) => void | — | The moment of each successful refresh: the now prop's value, or the clock when none is given. |
| now | Date | number | a clock started on mount | The time the stamp is read against. Pass it for a relative stamp that renders on the server; without it the phrase arrives after mount and a shared 30-second clock keeps it current while the page is visible. |
| onWind | (turns: number) => void | — | Fires when a wind is let go, with the turns of spring it stored. |
| friction | number | 0.5 | How quickly the released spin slows, 0 to 1: 0 coasts for seconds, 1 stops within a turn or two. |
| wind | number | 2 | Turns of spring the button holds: how far a full wind goes back, how tight the coil gets and how long the spin runs. |
| stamp | "relative" | "clock" | "off" | "relative" | The stamp beside the button: Updated 2 min ago, Updated 09:40, or none (an icon-only button whose stamp is its accessible description). |
| formatStamp | (updatedAt: Date, now: Date) => string | relative, or a UTC clock | Writes the stamp from the last refresh and the current time; pass one for the reader's own time zone or language. |
| label | string | "Refresh" | The button's accessible name. |
| pendingLabel | string | "Refreshing" | The name and the stamp while refreshing. |
| errorLabel | string | "Couldn't refresh" | The stamp after a failed refresh; a press retries. |
| emptyLabel | string | "Not updated yet" | The stamp before the first refresh. |
| accent | string | "var(--accent-bright)" | The coil and the tension notches. Any CSS colour. |
| successHold | number | 1400 | How long the success flash holds before the button rests, in ms. |
| errorHold | number | 2600 | How long a failure holds before the button rests, in ms. |
| size | "sm" | "md" | "lg" | "md" | 32, 40 or 48 px across; the coil and the notch ring scale with it. |
| sound | boolean | false | Play a tick for every notch the wind lights and the whir of the spin while it spends the spring's energy. No audio context exists unless this is on. |
| disabled | boolean | false | Dims the button and lets go of any wind without firing it. |
| className | string | — | Merged onto the root's classes. |