KQ-1744 · LEAVE/PLANNER
Leave Planner
A time-off request surface: a balance meter of the leave type's days over one to three months of calendar, with teammates already away drawn as soft overlapping stripes on their dates. Choosing a range drains the meter a day at a time — one motion value walking about 45 ms a day on the linear ease — while the count of working days rolls and each working day in the calendar lights in the same beat; weekends and holidays inside the range stay hatched because they are skipped, the type switch slides on springs.snap and the months glide in on springs.glide. Asking for more than the balance runs the meter dry, shakes it once on a tween and explains the shortfall; each month is a grid with arrow keys, Enter for the first and last day, Shift to extend and Escape to clear.
Time off
Fernworks studio · 4 teammates
Arrow keys move between days, Page Up and Page Down between months. Enter sets the first day and then the last; Shift with an arrow extends the selection; Escape clears it.
annual · 9 left
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/leave-planner
Props
- stripeStyle
- "bands" | "hatch" | "dots"
- default "bands"
- How teammates' leave is drawn: bands that join across consecutive days, diagonal hatching that crosses where two overlap, or stacked dots.
- monthCount
- number
- default 2
- Months side by side on a wide container, 1 to 3; below a 34rem container it is always one.
- tone
- "cobalt" | "moss" | "ember" | "plum"
- default "cobalt"
- The colour of your request and balance, as a pigment at a fixed lightness.
- policies
- LeavePolicy[]
- default defaultLeavePolicies
- The kinds of leave: { id, label, allowance?, noticeDays? }; no allowance means no cap.
- balance
- Record<string, number>
- default defaultLeaveBalance
- Days left of each kind, by policy id.
- holidays
- LeaveHoliday[]
- default defaultLeaveHolidays
- Public holidays ({ date, name }); never counted as leave.
- team
- LeaveTeammate[]
- default defaultLeaveTeam
- Teammates and the spans they are already away: { id, name, away: { from, to }[] }.
- requests / defaultRequests
- LeaveRequest[]
- default — / []
- Requests already made ({ id, type, from, to, days }); their days are held off the balance. Controlled or initial.
- onRequestsChange
- (requests) => void
- default —
- Fires from Request and Cancel with every request.
- type / defaultType
- string
- default — / the first policy
- The kind of leave, by policy id; controlled or initial.
- onTypeChange
- (type) => void
- default —
- Fires from the press or arrow key that changed the kind of leave.
- range / defaultRange
- { from, to } | null
- default — / null
- The selection, ms at UTC midnight, inclusive; controlled or initial.
- onRangeChange
- (range) => void
- default —
- Fires from the press, drag or key that changed the selection.
- onRequest
- (request) => void
- default —
- Request was pressed, with the request and its working days.
- onCancel
- (request) => void
- default —
- Cancel was pressed on a request.
- now
- number
- default defaultLeaveNow
- Today in ms; earlier days cannot be chosen and notice counts from it. Never read from the clock.
- defaultMonth
- number
- default the month after now
- The first month shown: any ms inside it.
- status
- "ready" | "loading" | "error"
- default "ready"
- Loading shows a placeholder month; error explains and offers Try again.
- onRetry
- () => void
- default —
- Try again was pressed after the calendar failed to load.
- title
- string
- default "Time off"
- The heading.
- height
- number
- default 540
- Height of the surface in px; the body scrolls inside it.
- disabled
- boolean
- default false
- Read only: nothing can be chosen or requested.
- className
- string
- default —
- Classes for the surface.
| Prop | Type | Default | Description |
|---|---|---|---|
| stripeStyle | "bands" | "hatch" | "dots" | "bands" | How teammates' leave is drawn: bands that join across consecutive days, diagonal hatching that crosses where two overlap, or stacked dots. |
| monthCount | number | 2 | Months side by side on a wide container, 1 to 3; below a 34rem container it is always one. |
| tone | "cobalt" | "moss" | "ember" | "plum" | "cobalt" | The colour of your request and balance, as a pigment at a fixed lightness. |
| policies | LeavePolicy[] | defaultLeavePolicies | The kinds of leave: { id, label, allowance?, noticeDays? }; no allowance means no cap. |
| balance | Record<string, number> | defaultLeaveBalance | Days left of each kind, by policy id. |
| holidays | LeaveHoliday[] | defaultLeaveHolidays | Public holidays ({ date, name }); never counted as leave. |
| team | LeaveTeammate[] | defaultLeaveTeam | Teammates and the spans they are already away: { id, name, away: { from, to }[] }. |
| requests / defaultRequests | LeaveRequest[] | — / [] | Requests already made ({ id, type, from, to, days }); their days are held off the balance. Controlled or initial. |
| onRequestsChange | (requests) => void | — | Fires from Request and Cancel with every request. |
| type / defaultType | string | — / the first policy | The kind of leave, by policy id; controlled or initial. |
| onTypeChange | (type) => void | — | Fires from the press or arrow key that changed the kind of leave. |
| range / defaultRange | { from, to } | null | — / null | The selection, ms at UTC midnight, inclusive; controlled or initial. |
| onRangeChange | (range) => void | — | Fires from the press, drag or key that changed the selection. |
| onRequest | (request) => void | — | Request was pressed, with the request and its working days. |
| onCancel | (request) => void | — | Cancel was pressed on a request. |
| now | number | defaultLeaveNow | Today in ms; earlier days cannot be chosen and notice counts from it. Never read from the clock. |
| defaultMonth | number | the month after now | The first month shown: any ms inside it. |
| status | "ready" | "loading" | "error" | "ready" | Loading shows a placeholder month; error explains and offers Try again. |
| onRetry | () => void | — | Try again was pressed after the calendar failed to load. |
| title | string | "Time off" | The heading. |
| height | number | 540 | Height of the surface in px; the body scrolls inside it. |
| disabled | boolean | false | Read only: nothing can be chosen or requested. |
| className | string | — | Classes for the surface. |