KQ-1743 · SERIES/EDIT
Series Edit
A recurring meeting's editor whose strip of upcoming occurrences always shows what Save would do. Choosing This and following cleaves the row at the chosen date: a gap opens before it and the chips after it reflow on springs.glide while a dashed rule draws down the gap on springs.flick, then the chips in scope slide to the new time — later dropping, earlier rising — each a cascade step after the one before, their times rolling on springs.snap, while earlier chips stay put; a one-off save stamps its mark in on springs.recoil. The strip is a horizontal listbox whose arrow keys move the edit (and the cleave) to another date, the scope is a radio group, and clashes with other events are listed before saving.
Gaugeworks weekly review
Weekly on Thursday · 10:00–10:45 · Ops room 2
Arrow keys choose another date to edit; Home and End jump to the first and last.
editing thu 12 nov · this meeting
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/series-edit
Props
- chipStyle
- "tile" | "pill" | "stub"
- default "tile"
- How each occurrence chip is drawn: a two-line tile, a one-line pill, or a calendar stub with a month band.
- density
- "compact" | "regular" | "roomy"
- default "regular"
- Chip size, gaps, padding and field height.
- accent
- "cobalt" | "moss" | "plum" | "ember"
- default "cobalt"
- The colour of the edit — chips in scope, the cleave, the day lane and Save — as pigments at a fixed lightness.
- series
- SeriesInfo
- default defaultSeries
- The recurring meeting: { id, title, weekday (0 is Monday), start, duration, room }.
- occurrences / defaultOccurrences
- SeriesOccurrence[]
- default — / defaultSeriesOccurrences
- Each { id, date, start?, duration?, room?, exception?: "moved" | "cancelled", split? }; controlled or initial.
- onOccurrencesChange
- (occurrences) => void
- default —
- Fires from Save with every occurrence.
- busy
- SeriesBusy[]
- default defaultSeriesBusy
- Other events ({ id, title, date, start, end }) checked for clashes before saving.
- rooms
- string[]
- default defaultSeriesRooms
- Rooms offered by the Room field.
- now
- number
- default defaultSeriesNow
- The current time in ms; occurrences before today are not shown. Never read from the clock.
- anchor / defaultAnchor
- string
- default — / the first upcoming meeting
- The occurrence being edited, by id; controlled or initial.
- onAnchorChange
- (id) => void
- default —
- Fires from the press or arrow key that chose another occurrence.
- scope / defaultScope
- "one" | "following" | "all"
- default — / "one"
- Which meetings the edit applies to; controlled or initial.
- onScopeChange
- (scope) => void
- default —
- Fires from the press or key that changed the scope.
- defaultChanges
- Partial<SeriesChanges>
- default —
- Pre-fills the form ({ start, duration, room }) on top of the anchor's own values.
- onEditOne
- (occurrence, changes) => void
- default —
- Save with scope "one": the occurrence, now a one-off, and its new values.
- onEditFollowing
- (occurrence, changes) => void
- default —
- Save with scope "following": the first occurrence of the new series and its values.
- onEditAll
- (changes) => void
- default —
- Save with scope "all": the series' new values. One-off changes keep their own.
- onDiscard
- () => void
- default —
- Discard was pressed: the form went back to the anchor's values.
- status
- "ready" | "loading" | "error"
- default "ready"
- Loading shows placeholder chips and fields; error explains and offers Try again.
- onRetry
- () => void
- default —
- Try again was pressed after the series failed to load.
- 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, changed or saved.
- className
- string
- default —
- Classes for the surface.
| Prop | Type | Default | Description |
|---|---|---|---|
| chipStyle | "tile" | "pill" | "stub" | "tile" | How each occurrence chip is drawn: a two-line tile, a one-line pill, or a calendar stub with a month band. |
| density | "compact" | "regular" | "roomy" | "regular" | Chip size, gaps, padding and field height. |
| accent | "cobalt" | "moss" | "plum" | "ember" | "cobalt" | The colour of the edit — chips in scope, the cleave, the day lane and Save — as pigments at a fixed lightness. |
| series | SeriesInfo | defaultSeries | The recurring meeting: { id, title, weekday (0 is Monday), start, duration, room }. |
| occurrences / defaultOccurrences | SeriesOccurrence[] | — / defaultSeriesOccurrences | Each { id, date, start?, duration?, room?, exception?: "moved" | "cancelled", split? }; controlled or initial. |
| onOccurrencesChange | (occurrences) => void | — | Fires from Save with every occurrence. |
| busy | SeriesBusy[] | defaultSeriesBusy | Other events ({ id, title, date, start, end }) checked for clashes before saving. |
| rooms | string[] | defaultSeriesRooms | Rooms offered by the Room field. |
| now | number | defaultSeriesNow | The current time in ms; occurrences before today are not shown. Never read from the clock. |
| anchor / defaultAnchor | string | — / the first upcoming meeting | The occurrence being edited, by id; controlled or initial. |
| onAnchorChange | (id) => void | — | Fires from the press or arrow key that chose another occurrence. |
| scope / defaultScope | "one" | "following" | "all" | — / "one" | Which meetings the edit applies to; controlled or initial. |
| onScopeChange | (scope) => void | — | Fires from the press or key that changed the scope. |
| defaultChanges | Partial<SeriesChanges> | — | Pre-fills the form ({ start, duration, room }) on top of the anchor's own values. |
| onEditOne | (occurrence, changes) => void | — | Save with scope "one": the occurrence, now a one-off, and its new values. |
| onEditFollowing | (occurrence, changes) => void | — | Save with scope "following": the first occurrence of the new series and its values. |
| onEditAll | (changes) => void | — | Save with scope "all": the series' new values. One-off changes keep their own. |
| onDiscard | () => void | — | Discard was pressed: the form went back to the anchor's values. |
| status | "ready" | "loading" | "error" | "ready" | Loading shows placeholder chips and fields; error explains and offers Try again. |
| onRetry | () => void | — | Try again was pressed after the series failed to load. |
| height | number | 540 | Height of the surface in px; the body scrolls inside it. |
| disabled | boolean | false | Read only: nothing can be chosen, changed or saved. |
| className | string | — | Classes for the surface. |