@umriss-ui/schedule 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,224 @@
1
+ # Changes to `@umriss-ui/schedule`
2
+
3
+ This document describes what changes for **callers** of the package: exports,
4
+ props, behaviour. What was worked on in the repository stands in the
5
+ repository's journal (`docs/journal.md`) and in the specs under `.scratch/`.
6
+
7
+ **The numbers.** The middle digit rises when something is added; the last one
8
+ when something is repaired. As long as the first digit is `0`, no number
9
+ promises compatibility — which is why whatever changes existing behaviour stands
10
+ under a heading "Changed" of its own, no matter which digit rose.
11
+
12
+ **Release candidates.** The package started as a release candidate under the
13
+ tag `next`. With `0.1.0` it is released: the tag is gone, and the interface
14
+ moves from here under the rule above.
15
+
16
+ ---
17
+
18
+ ## 0.1.0 – The first release (Sep. 2026)
19
+
20
+ No longer a release candidate: the tag `next` is gone, `pnpm add
21
+ @umriss-ui/schedule` is the whole install line, and `@umriss-ui/core` and
22
+ `@umriss-ui/charts` are released versions too. What the candidate `0.1.0-rc.0`
23
+ was is still here; three deliveries stand on top of it, newest first, each with
24
+ its own report below.
25
+
26
+ The numbers keep their meaning from here: the middle digit rises when something
27
+ is added, the last one when something is repaired, and whatever changes
28
+ existing behaviour stands under a heading "Changed" of its own. As long as the
29
+ first digit is `0`, no number promises compatibility.
30
+
31
+ ---
32
+
33
+ ## Lanes that fold, and one language for the bars (Sep. 2026)
34
+
35
+ Delivery report for `.scratch/schedule-lane-groups/spec.md`, tickets 01–11.
36
+
37
+ ### Added
38
+
39
+ - **`<LaneGroup id label>`** puts structure over the lanes — halls, lines,
40
+ machine groups, to any depth. It is declared by composition, so a group reads
41
+ in JSX as it reads in the plant, and a lane never names its group.
42
+
43
+ A group is **never a lane** (ADR-0025). Nothing sits on it: a subtask names a
44
+ machine, a finding belongs to a machine, `canMoveTo` is asked about a machine
45
+ and every intent names one. Folding a group changes none of it.
46
+
47
+ ```tsx
48
+ <Schedule …>
49
+ <LaneGroup id="hall-a" label="Hall A">
50
+ <LaneGroup id="turning" label="Turning line">
51
+ <Lane id="lathe-1" label="Lathe 1" />
52
+ <Lane id="lathe-2" label="Lathe 2" />
53
+ </LaneGroup>
54
+ <Lane id="press-1" label="Press 1" />
55
+ </LaneGroup>
56
+ <Lane id="paint" label="Paint shop" />
57
+ </Schedule>
58
+ ```
59
+ - **`collapsedGroups`, `defaultCollapsedGroups` and `onCollapsedGroupsChange`**
60
+ say which groups are folded — controlled, with an uncontrolled default: the
61
+ shape `selectedTask` has. It is **not an intent**. Folding says what is on
62
+ screen and nothing about the plan, so a caller that applies every intent it
63
+ receives will never find a fold among them.
64
+ - **A folded group shows a miniature**: every lane in it as a thin strip, at a
65
+ smaller scale, with its work in the tasks' own colours. Transports arrive at
66
+ a strip, a finding inside a fold is marked on the strip and on the row, and a
67
+ strip can be hovered, tooltipped and selected. A strip carries no appearance,
68
+ no bar label, no progress rail and no grips — that is what unfolding is for.
69
+ - **The header of a group** carries a chevron button with `aria-expanded` and
70
+ `aria-controls`, the group's label and how many lanes it holds; children
71
+ indent one step per level. It is the first thing the schedule puts into the
72
+ tab order, and it has a focus ring of its own. Two wording entries in English
73
+ and in `@umriss-ui/core/wording/de`, plus the lane count.
74
+ - **A drag held over a folded group opens it** after a moment, so work can be
75
+ moved into a group without preparing the view first. It closes again when the
76
+ drag ends, and the caller's list is never written to — the application did
77
+ not fold anything, and hears nothing.
78
+
79
+ ### Changed
80
+
81
+ - **Every appearance is drawn differently.** The words a caller writes are
82
+ unchanged and so is `resolveAppearance`; the picture is not. Each statement
83
+ now owns exactly one property of the drawing, so that several on one bar stay
84
+ several statements:
85
+ - `"provisional"` is **hollow** - no fill at all, the surface shows through,
86
+ with its dashed outline in the task colour at full weight. It used to be a
87
+ 45-per-cent fill, which read as a setup; empty is not faint.
88
+ - `"fixed"` carries a **cap at each end** of the main time, three pixels, in
89
+ the colour the bar's label takes, set just inside the end. The hatch across
90
+ its face is gone: it lay under the label, and it was the loudest mark in
91
+ the picture for the calmest statement.
92
+ - `"muted"` is at **full height** in the task colour mixed half into the
93
+ surface, opaque and without an outline. It used to be drawn at half height,
94
+ which read as a different kind of thing and cost it its label.
95
+ - `"open"` **fades at whichever edge of the view** the bar passes, left as
96
+ well as right. A bar that began before the view used to say nothing at all.
97
+ - **A `progress` rail** lies within the main time and above the bar's lower
98
+ edge. It measures the work, so it stops where the main time does.
99
+ - **A hatch now means one thing only**: a lane a drag may not put its work on.
100
+ It is no longer a mark on any bar.
101
+ - **A refused lane is visible before it is met.** The lanes `canMoveTo` turns
102
+ down are marked from the first frame of a drag - drawn back and hatched, and
103
+ their headers with them (`[data-lane][data-refused]`, to style beside the
104
+ schedule). Over one of them the cursor says no and a hairline ties the ghost
105
+ to the pointer it is not following.
106
+ - **A refused lane no longer costs the whole gesture.** A drop after one still
107
+ reports the move in time; only the lane change is dropped. `canMoveTo` is
108
+ asked once per lane when a drag takes hold, and again at the drop.
109
+ - **`ScheduleHandle` answers for a folded lane** with its strip:
110
+ `clientPointOf` and `positionAt` read the place a lane is actually drawn, so
111
+ a caller's own marks keep pointing at the work when a group folds.
112
+
113
+ ---
114
+
115
+ ## A plan one can read (Sep. 2026)
116
+
117
+ Delivery report for `.scratch/schedule-legibility/spec.md`, tickets 01–06.
118
+ The formats of `@umriss-ui/core` became English in the same delivery
119
+ (ADR-0024), which is what the dates and numbers of a schedule follow.
120
+
121
+ ### Added
122
+
123
+ - **`label`** writes a line into every bar: the order, the article, whatever a
124
+ planner reads first. It is real DOM text, cut off with an ellipsis where the
125
+ bar is too narrow, left out where nothing would be readable, and held at the
126
+ view's edge for a bar that began before it. Its colour follows the brightness
127
+ of the bar's.
128
+ - **`route`, `anchor` and `ends`** decide how a transport is drawn: a curve, a
129
+ straight line or orthogonal segments; from the middle of a bar or from the
130
+ edge facing the other stop; with or without a dot at each end. All three are
131
+ options of the schedule and all three are overridable per transport. They
132
+ change the picture and never a finding - `leaves` and `arrives` alone decide
133
+ whether a transport is late.
134
+ - **`appearance` and `progress` on a subtask:** `"provisional"` (a dashed
135
+ outline), `"fixed"` (a hatch), `"muted"` (drawn slim), `"open"` (fading where
136
+ it continues past the view), and a share of the work that is done, drawn as a
137
+ rail along the bar. Each is a pattern or an outline and not only a colour, and
138
+ none of them uses the faint fill that means a setup or a teardown.
139
+ - **`canMoveTo(subtask, lane)`** narrows where a drag may land. The ghost stays
140
+ on the last lane that was allowed and says "Not this lane"; a refused drop
141
+ reports nothing. It is asked for work dragged in from outside as well. Two
142
+ wording entries come with it.
143
+
144
+ ---
145
+
146
+ ## The schedule in daily use (Sep. 2026)
147
+
148
+ Delivery report for `.scratch/schedule-refinement/spec.md`, tickets 01–09.
149
+ Read **Changed** first: the wheel does something else now.
150
+
151
+ ### Changed
152
+
153
+ - **The wheel scrolls the lanes** and lets the page scroll on once they are at
154
+ their end. **Ctrl or ⌘ with the wheel, and a pinch, zoom**; a horizontal
155
+ wheel or Shift with a vertical one pans through time. Before this, every
156
+ wheel movement zoomed, which trapped the wheel on a long page and left
157
+ twenty lanes unreachable by it.
158
+ - **`onSelectedTaskChange` carries the clicked subtask** as a second argument
159
+ (null for a click on a transport or on nothing), and is called again when
160
+ another subtask of the same task is clicked. A caller that reads only the
161
+ first argument is unaffected.
162
+
163
+ ### Added
164
+
165
+ - **A tooltip** on a hovered subtask or transport: the order, the stop, its
166
+ times, setup and teardown, the transport's route and duration, and the
167
+ findings on it. `tooltip={false}` switches it off; a function replaces its
168
+ content and receives a `ScheduleTooltipTarget` with the findings.
169
+ - **`now`** draws a line across the lanes at the present: `true` follows the
170
+ clock by the minute, an instant fixes it there. Off by default.
171
+ - **A drag near the edge of the plot pans the plot along**, faster the closer
172
+ to the edge, so a subtask reaches a time that was not in view in one
173
+ gesture.
174
+ - **`snap` takes a raster with an offset** (`{ step, offset }`): shifts at
175
+ 06:00, 14:00 and 22:00 are eight hours offset by six. A snapped time that
176
+ would land in time an operating calendar removes moves on to the seam, so an
177
+ intent never asks for a time the plant does not run.
178
+ - **`shiftTask(subtasks, task, by)`** returns one move intent per stop of a
179
+ task - the whole order moved, distances kept.
180
+ - **The place intent and `placing`:** work that is not on the plan can be
181
+ dragged in from any list, with the platform's drag and drop. The application
182
+ declares what it is dragging in `placing` while it drags; a drop on a lane
183
+ reports `{ kind: "place", item, task, lane, from, to, setup, teardown }`.
184
+ `subtaskFromPlace(intent, id)` builds the subtask, `applyIntent` leaves the
185
+ data alone for it, and `ripple` pushes nothing until the subtask exists.
186
+ Without `"place"` in `intents` no drop is accepted.
187
+ - **`onDomainChange`** reports the visible span after the planner pans or
188
+ zooms - for keeping a second schedule, or a chart's time axis, in step. A
189
+ span handed in through `initialDomain` is not reported back.
190
+ - **A ref handle** (`ScheduleHandle`): `positionAt(clientX, clientY)`,
191
+ `clientPointOf(time, lane?)`, `visibleDomain()`.
192
+
193
+ ---
194
+
195
+ ## 0.1.0-rc.0 – The schedule (Sep. 2026)
196
+
197
+ Delivery report for `.scratch/schedule/spec.md` (ADR-0022, ADR-0023). Not yet
198
+ published.
199
+
200
+ ### Added
201
+
202
+ - **`Schedule`** with **`Lane`**, **`Subtasks`** and **`Transports`** declared
203
+ as children. Lane headers at the left as text, a day band above the plot and a
204
+ time band below it that steps from the day down to the quarter hour with the
205
+ zoom. Subtasks drawn with main time, setup and teardown in their task's colour;
206
+ transports as lines from an end to a start, anchored per transport
207
+ (`leaves`, `arrives`). Canvas colours follow `color-scheme`.
208
+ - **Findings drawn and never resolved:** an overlap on a lane — setup and
209
+ teardown included — offset and marked; a late transport dashed in the finding
210
+ colour.
211
+ - **Pan and zoom:** drag the background in both directions, the wheel and a
212
+ pinch zoom around the pointer, a horizontal wheel pans; `zoomLimits`. An
213
+ operating calendar (`calendar`) cuts removed time out of the axis.
214
+ - **Interaction:** `onInteraction` reports click, context menu and hover with
215
+ the hit (subtask and part, transport, lane, nothing), the client point, the
216
+ time and the lane. A click selects the whole task; `selectedTask` and
217
+ `onSelectedTaskChange` control it.
218
+ - **Controlled editing:** `intents` and `onIntent` — move, lane, stretch at the
219
+ edges, setup and teardown grips on the selected subtask — with a ghost that
220
+ shows its times and findings while the drag is in flight; Escape cancels.
221
+ `snap`: the tick raster by default, a step, or `false`.
222
+ - **The arithmetic:** `findings`, `overlaps`, `lateTransports`, `ripple`,
223
+ `applyIntent`, `snapTime`, `occupied`, `departure`, `arrival`, and the types
224
+ `Task`, `Subtask`, `Transport`, `Intent` and its five kinds.
package/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Roman Händler
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,138 @@
1
+ # @umriss-ui/schedule
2
+
3
+ The schedule of a plant: **subtasks** on **lanes** over time. One task runs
4
+ across several machines in a fixed order, each stop has a main time with a
5
+ setup before it and a teardown after it, and between the stops lie
6
+ **transports** with durations of their own. The schedule draws all of it, marks
7
+ what cannot work — two subtasks on one machine at once, a transport that cannot
8
+ arrive in time — and lets a planner move things by hand without ever changing
9
+ the plan underneath them.
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ pnpm add @umriss-ui/schedule @umriss-ui/core @umriss-ui/charts
15
+ ```
16
+
17
+ Both `@umriss-ui/core` and `@umriss-ui/charts` are **peer dependencies**
18
+ (ADR-0022): the schedule takes its styling regime, provider, formats and
19
+ wording from core, and its time arithmetic — the affine scale, the time steps,
20
+ the operating calendar, the canvas colour resolution — from charts. React 18 or
21
+ 19 as a peer as well.
22
+
23
+ ## The smallest schedule that runs
24
+
25
+ ```tsx
26
+ import { Lane, Schedule, Subtasks, Transports } from "@umriss-ui/schedule";
27
+ import type { Subtask, Task, Transport } from "@umriss-ui/schedule";
28
+
29
+ const at = (h: number, m = 0) => new Date(2026, 2, 17, h, m).getTime();
30
+
31
+ const tasks: Task[] = [{ id: "a-2041", color: "#2563eb" }];
32
+ const subtasks: Subtask[] = [
33
+ { id: "cut", task: "a-2041", lane: "saw", from: at(6), to: at(7), teardown: 10 * 60_000 },
34
+ { id: "mill", task: "a-2041", lane: "mill", from: at(8), to: at(10), setup: 30 * 60_000 },
35
+ ];
36
+ const transports: Transport[] = [{ id: "move", from: "cut", to: "mill", duration: 15 * 60_000 }];
37
+
38
+ export function Plan() {
39
+ return (
40
+ <Schedule ariaLabel="Plan of Tuesday" initialDomain={[at(5), at(12)]}>
41
+ <Lane id="saw" label="Saw" />
42
+ <Lane id="mill" label="Mill" />
43
+ <Transports data={transports} />
44
+ <Subtasks data={subtasks} tasks={tasks} />
45
+ </Schedule>
46
+ );
47
+ }
48
+ ```
49
+
50
+ ## Editing is controlled
51
+
52
+ The schedule draws the data it is given and changes none of it (ADR-0023). A
53
+ drag shows a **ghost** with the findings the drop would create, and ends in an
54
+ **intent** — move, lane, stretch, setup, teardown — reported to the caller:
55
+
56
+ ```tsx
57
+ <Schedule
58
+ intents={["move", "lane"]}
59
+ onIntent={(intent) => setSubtasks((all) => all.map((s) => applyIntent(s, intent)))}
60
+ …
61
+ >
62
+ ```
63
+
64
+ `intents` lists what the application handles; without it the schedule is
65
+ read-only. `canMoveTo(subtask, lane)` narrows it: a drag across a lane the
66
+ subtask may not go to leaves the ghost where it was allowed and shows a
67
+ refusal, and a refused drop reports nothing. A drag near the edge of the plot pans the plot along, and `snap`
68
+ takes the tick raster, a step, or a step with an offset - shifts at 06:00,
69
+ 14:00 and 22:00 are eight hours offset by six.
70
+
71
+ Dependent subtasks never move by themselves: `ripple` computes the cascade for
72
+ the application to apply, or not, and `shiftTask` the moves of a whole order.
73
+ `findings`, `overlaps` and `lateTransports` return what the schedule draws as
74
+ data.
75
+
76
+ Work that is not on the plan is dragged in from any list of the application,
77
+ with the platform's drag and drop: the application says what it is dragging in
78
+ `placing`, the drop reports a `place` intent, and `subtaskFromPlace` builds the
79
+ subtask under the application's own id.
80
+
81
+ ## What a bar says
82
+
83
+ `label` writes a line into every bar, cut off where the bar is too narrow and
84
+ left out where nothing would be readable. `appearance` adds what a colour
85
+ cannot say - `"provisional"`, `"fixed"`, `"muted"`, `"open"` - and `progress`
86
+ draws the share that is done as a rail along the bar. `route`, `anchor` and
87
+ `ends` decide how a transport is drawn, per schedule or per transport, without
88
+ touching what a finding says.
89
+
90
+ Each appearance owns exactly **one** property of the drawing, so several on one
91
+ bar stay several statements: `"provisional"` the fill (none at all, the surface
92
+ shows through), `"fixed"` the ends (a cap inside each), `"muted"` the
93
+ saturation (the task colour half mixed into the surface, at full height), and
94
+ `"open"` a fade at whichever edge of the view the bar passes. `progress` owns a
95
+ rail inside the main time. The faint, outlined fill belongs to a setup and a
96
+ teardown and to nothing else.
97
+
98
+ ## Lanes that fold
99
+
100
+ `<LaneGroup>` puts halls, lines and machine groups over the lanes, to any
101
+ depth. A group is structure and never a lane: nothing sits on it, no finding is
102
+ reported for it, and no intent names it (ADR-0025).
103
+
104
+ ```tsx
105
+ <LaneGroup id="hall-a" label="Hall A">
106
+ <LaneGroup id="turning" label="Turning line">
107
+ <Lane id="lathe-1" label="Lathe 1" />
108
+ <Lane id="lathe-2" label="Lathe 2" />
109
+ </LaneGroup>
110
+ <Lane id="press-1" label="Press 1" />
111
+ </LaneGroup>
112
+ ```
113
+
114
+ Folded, a group becomes one row showing a **miniature** — every lane in it as a
115
+ thin strip, the real work smaller. Transports still arrive at the right strip,
116
+ findings are still marked, and a strip can still be hovered and selected:
117
+ folding costs a planner detail and never access. `collapsedGroups`,
118
+ `defaultCollapsedGroups` and `onCollapsedGroupsChange` put the state in the
119
+ application's hands; it is a view state, never an intent.
120
+
121
+ ## Reading and moving
122
+
123
+ A tooltip on a hovered subtask or transport names the order, the times, setup
124
+ and teardown and the findings; `tooltip` replaces its content or switches it
125
+ off. `now` draws the present across the lanes. The wheel scrolls the lanes and
126
+ releases the page at their end, Ctrl or ⌘ and a pinch zoom, Shift pans;
127
+ `onDomainChange` reports the visible span, and a ref handle turns a client
128
+ point into a time and a lane and back.
129
+
130
+ ## Where to read on
131
+
132
+ * The demo — `pnpm dev:schedule` (port 4176) — is the documentation: every page
133
+ shows running examples with their source and the props generated from `src/`.
134
+ * [`CHANGELOG.md`](CHANGELOG.md) — what changes for a caller.
135
+ * The vocabulary — **Task**, **Subtask**, **Setup**, **Teardown**,
136
+ **Transport**, **Late transport**, **Lane header**, **Lane group**,
137
+ **Miniature**, **Intent**, **Ghost** — stands in the workspace's
138
+ `CONTEXT.md`, section "The schedule".