@umriss-ui/schedule 0.42.0 → 0.44.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/docs/llms-full.md CHANGED
@@ -1,4 +1,4 @@
1
- # @umriss-ui/schedule 0.42.0
1
+ # @umriss-ui/schedule 0.44.0
2
2
 
3
3
  > React Gantt-style schedule for data-dense applications - work on lanes over time, with dependencies, blocked time, findings and controlled editing.
4
4
 
@@ -133,7 +133,7 @@ export default function HandOver() {
133
133
  </Stack>
134
134
  <div data-callout="2">
135
135
  <Schedule
136
- ariaLabel="On-call rota, 16 to 23 March"
136
+ aria-label="On-call rota, 16 to 23 March"
137
137
  height={420}
138
138
  now={NOW}
139
139
  intents={["lane"]}
@@ -418,7 +418,7 @@ export default function TourPlan() {
418
418
  <Stack gap={3}>
419
419
  <div data-callout="1">
420
420
  <Schedule
421
- ariaLabel="Tours of Wednesday, 18 March"
421
+ aria-label="Tours of Wednesday, 18 March"
422
422
  height={470}
423
423
  intents={["move"]}
424
424
  onIntent={apply}
@@ -652,7 +652,7 @@ export default function SprintAroundLeave() {
652
652
  </Stack>
653
653
  <div data-callout="2">
654
654
  <Schedule
655
- ariaLabel="Sprint 14, work per person"
655
+ aria-label="Sprint 14, work per person"
656
656
  calendar={WORKING_DAYS}
657
657
  height={570}
658
658
  now={NOW}
@@ -844,7 +844,7 @@ export default function ReplanTheDay() {
844
844
  </div>
845
845
  <div data-callout="2">
846
846
  <Schedule
847
- ariaLabel="Plan of Tuesday, 17 March, in the planner's hands"
847
+ aria-label="Plan of Tuesday, 17 March, in the planner's hands"
848
848
  height={380}
849
849
  intents={["move", "lane", "stretch", "leadIn", "leadOut"]}
850
850
  onIntent={apply}
@@ -863,7 +863,7 @@ export default function ReplanTheDay() {
863
863
  onOpenChange={(open) => {
864
864
  if (!open) setMenu(null);
865
865
  }}
866
- ariaLabel={target !== null ? `Actions for ${target.id}` : "Actions for the plan"}
866
+ aria-label={target !== null ? `Actions for ${target.id}` : "Actions for the plan"}
867
867
  >
868
868
  {target !== null ? (
869
869
  <>
@@ -944,7 +944,7 @@ const dependencies: Dependency[] = [{ id: "handover", from: "design", to: "build
944
944
  export default function SmallestSchedule() {
945
945
  const { Schedule, Lane, Subtasks, Dependencies } = useSchedule({ initialView: { domain: [at(8), at(16)] } });
946
946
  return (
947
- <Schedule ariaLabel="Tuesday, design and build" height={150}>
947
+ <Schedule aria-label="Tuesday, design and build" height={150}>
948
948
  <Lane id="noah" label="Noah Fischer" />
949
949
  <Lane id="arjun" label="Arjun Mehta" />
950
950
  <Dependencies data={dependencies} />
@@ -964,14 +964,14 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
964
964
 
965
965
  | Prop | Type | Default | Description |
966
966
  |---|---|---|---|
967
- | [`height`](#first-schedule) | `number` | `400` | Height in CSS pixels, both bands included. The width is the host's. |
967
+ | [`height`](#first-schedule) | `number` | fills the frame, at least 400 px | Height in CSS pixels, both bands included, fixed. Left out, the schedule fills its frame - a stretched grid cell, a card the grid stretches, a container of definite height - and never goes below 400 px, which is also its height in a frame without one. A different floor goes through `style`'s `minHeight` ([ADR-0050](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0050-a-chart-fills-its-frame-never-below-its-floor.md)). The width is the host's. |
968
968
  | [`children`](#first-schedule) | `ReactNode` | — | `Lane`, `Dependencies` and `Subtasks`, in the order they are to stand and be drawn. |
969
969
 
970
970
  ###### Accessibility
971
971
 
972
972
  | Prop | Type | Default | Description |
973
973
  |---|---|---|---|
974
- | [`ariaLabel`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
974
+ | [`aria-label`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
975
975
 
976
976
  From `LaneProps` — the full table stands on [Lanes](#lanes).
977
977
 
@@ -1101,7 +1101,7 @@ const HANDOVERS: readonly Dependency[] = [
1101
1101
  export default function FirstSchedule() {
1102
1102
  const { Schedule, Lane, Subtasks, Dependencies } = useSchedule({ initialView: { domain: DAY } });
1103
1103
  return (
1104
- <Schedule ariaLabel="Tours of Tuesday, 17 March" height={240}>
1104
+ <Schedule aria-label="Tours of Tuesday, 17 March" height={240}>
1105
1105
  {VEHICLES.map((vehicle) => (
1106
1106
  <Lane key={vehicle.id} id={vehicle.id} label={vehicle.label} />
1107
1107
  ))}
@@ -1134,7 +1134,7 @@ const PEOPLE = [
1134
1134
  export default function NoWorkYet() {
1135
1135
  const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: [at(23, 8), at(27, 18)] } });
1136
1136
  return (
1137
- <Schedule ariaLabel="Sprint 15, nothing planned yet" height={170}>
1137
+ <Schedule aria-label="Sprint 15, nothing planned yet" height={170}>
1138
1138
  {PEOPLE.map((person) => (
1139
1139
  <Lane key={person.id} id={person.id} label={person.label} />
1140
1140
  ))}
@@ -1229,7 +1229,7 @@ export default function MachinePlan() {
1229
1229
  const { Schedule, Lane, Subtasks, Dependencies } = useSchedule({ initialView: { domain: DAY_OF_PLAN } });
1230
1230
  return (
1231
1231
  <Schedule
1232
- ariaLabel="Machine shop, Tuesday 17 March"
1232
+ aria-label="Machine shop, Tuesday 17 March"
1233
1233
  height={380}
1234
1234
  now={HALF_PAST_TEN}
1235
1235
  label={(step) => NAMES.get(step.task) ?? step.task}
@@ -1244,6 +1244,164 @@ export default function MachinePlan() {
1244
1244
  }
1245
1245
  ```
1246
1246
 
1247
+ ##### Fill a pane of fixed height
1248
+
1249
+ Without `height`, a schedule takes the height of its frame: this pane is 480 px, and the schedule is exactly that, bands included. Drag the pane's corner and the schedule follows.
1250
+
1251
+ ```tsx
1252
+ import { useSchedule } from "@umriss-ui/schedule";
1253
+ import type { Subtask, Task } from "@umriss-ui/schedule";
1254
+
1255
+ const at = (hours: number) => new Date(2026, 2, 17, hours).getTime();
1256
+
1257
+ const ORDERS: Task[] = [{ id: "a-2041", name: "A-2041 Housing", color: "light-dark(#2563eb, #6b9bff)" }];
1258
+
1259
+ const STEPS: Subtask[] = [
1260
+ { id: "a-2041-1", task: "a-2041", lane: "saw", from: at(6), to: at(7) },
1261
+ { id: "a-2041-2", task: "a-2041", lane: "mill", from: at(8), to: at(10) },
1262
+ { id: "a-2041-3", task: "a-2041", lane: "qa", from: at(11), to: at(12) },
1263
+ ];
1264
+
1265
+ export default function FillsItsPane() {
1266
+ const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: [at(5), at(13)] } });
1267
+ return (
1268
+ <div data-pane="" style={{ height: 480, resize: "vertical", overflow: "hidden" }}>
1269
+ <Schedule aria-label="Plan of Tuesday in a pane">
1270
+ <Lane id="saw" label="Saw" />
1271
+ <Lane id="mill" label="Mill" />
1272
+ <Lane id="qa" label="Inspection" />
1273
+ <Subtasks data={STEPS} tasks={ORDERS} />
1274
+ </Schedule>
1275
+ </div>
1276
+ );
1277
+ }
1278
+ ```
1279
+
1280
+ ##### Thirty lanes, a floor of 400 px
1281
+
1282
+ In a block with no height, a schedule is 400 px whatever its lanes. The lanes beyond the plot scroll inside it, and the page does not grow.
1283
+
1284
+ ```tsx
1285
+ import { useSchedule } from "@umriss-ui/schedule";
1286
+ import type { Subtask, Task } from "@umriss-ui/schedule";
1287
+
1288
+ const at = (hours: number) => new Date(2026, 2, 17, hours).getTime();
1289
+
1290
+ const ORDERS: Task[] = [{ id: "round", name: "Round", color: "light-dark(#0d9488, #3cc7b8)" }];
1291
+
1292
+ const STATIONS = Array.from({ length: 30 }, (_, index) => ({ id: `s-${index + 1}`, label: `Station ${index + 1}` }));
1293
+
1294
+ const STEPS: Subtask[] = STATIONS.map((station, index) => ({
1295
+ id: `round-${index + 1}`,
1296
+ task: "round",
1297
+ lane: station.id,
1298
+ from: at(6 + (index % 6)),
1299
+ to: at(7 + (index % 6)),
1300
+ }));
1301
+
1302
+ export default function ThirtyLanes() {
1303
+ const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: [at(5), at(14)] } });
1304
+ return (
1305
+ <Schedule aria-label="Round of thirty stations">
1306
+ {STATIONS.map((station) => (
1307
+ <Lane key={station.id} id={station.id} label={station.label} />
1308
+ ))}
1309
+ <Subtasks data={STEPS} tasks={ORDERS} />
1310
+ </Schedule>
1311
+ );
1312
+ }
1313
+ ```
1314
+
1315
+ ##### Show that a plan is on its way
1316
+
1317
+ `loading`, as the chart and the table have it: over a plan the plan stays, dims after a moment and starts no drag, tooltip or key intent until the answer is in; before the first lanes, placeholder lines stand in the lane column.
1318
+
1319
+ ```tsx
1320
+ import { useEffect, useRef, useState } from "react";
1321
+ import { Button, Text } from "@umriss-ui/core";
1322
+ import { useSchedule } from "@umriss-ui/schedule";
1323
+ import type { Subtask, Task } from "@umriss-ui/schedule";
1324
+
1325
+ const at = (hours: number, minutes = 0) => new Date(2026, 2, 17, hours, minutes).getTime();
1326
+ const DAY: readonly [number, number] = [at(6), at(16)];
1327
+
1328
+ const TOURS: readonly Task[] = [
1329
+ { id: "t-01", name: "T-01 Harbour", color: "light-dark(#2563eb, #6b9bff)" },
1330
+ { id: "t-03", name: "T-03 Old Town", color: "light-dark(#c2410c, #f08a52)" },
1331
+ ];
1332
+
1333
+ /** Each answer shifts the second tour by a quarter of an hour. */
1334
+ function legs(answer: number): readonly Subtask[] {
1335
+ const shift = (answer % 4) * 15;
1336
+ return [
1337
+ { id: "t-01-1", task: "t-01", lane: "van-1", from: at(7), to: at(9) },
1338
+ { id: "t-03-1", task: "t-03", lane: "van-2", from: at(8, shift), to: at(10, shift) },
1339
+ { id: "t-01-2", task: "t-01", lane: "e-van", from: at(11), to: at(13) },
1340
+ ];
1341
+ }
1342
+
1343
+ const LANES = [
1344
+ { id: "van-1", label: "Van FP 214 K" },
1345
+ { id: "van-2", label: "Van FP 402 R" },
1346
+ { id: "e-van", label: "E-van FP 377 K" },
1347
+ ];
1348
+
1349
+ function Plan({ answer, loading }: { answer: number; loading: boolean }) {
1350
+ const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: DAY } });
1351
+ return (
1352
+ <Schedule aria-label="Tours of the day" height={220} loading={loading} intents={["move", "lane"]}>
1353
+ {LANES.map((lane) => (
1354
+ <Lane key={lane.id} id={lane.id} label={lane.label} />
1355
+ ))}
1356
+ <Subtasks data={legs(answer)} tasks={TOURS} />
1357
+ </Schedule>
1358
+ );
1359
+ }
1360
+
1361
+ function Waiting() {
1362
+ const { Schedule } = useSchedule({ initialView: { domain: DAY } });
1363
+ return <Schedule aria-label="Tours of the day, loading" height={220} loading />;
1364
+ }
1365
+
1366
+ export default function Loading() {
1367
+ const [answer, setAnswer] = useState(0);
1368
+ const [loading, setLoading] = useState(false);
1369
+ const pending = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
1370
+ useEffect(() => () => clearTimeout(pending.current), []);
1371
+
1372
+ const reload = () => {
1373
+ setLoading(true);
1374
+ clearTimeout(pending.current);
1375
+ pending.current = setTimeout(() => {
1376
+ setAnswer((n) => n + 1);
1377
+ setLoading(false);
1378
+ }, 1500);
1379
+ };
1380
+
1381
+ return (
1382
+ <div className="pair">
1383
+ <div>
1384
+ <Text as="p" size="sm" tone="muted">
1385
+ loading over a plan - the answer takes 1.5 s
1386
+ </Text>
1387
+ <Plan answer={answer} loading={loading} />
1388
+ <div className="demo-actions">
1389
+ <Button size="sm" onClick={reload} disabled={loading}>
1390
+ Reload
1391
+ </Button>
1392
+ </div>
1393
+ </div>
1394
+ <div>
1395
+ <Text as="p" size="sm" tone="muted">
1396
+ loading, before the first lanes
1397
+ </Text>
1398
+ <Waiting />
1399
+ </div>
1400
+ </div>
1401
+ );
1402
+ }
1403
+ ```
1404
+
1247
1405
  #### Keyboard
1248
1406
 
1249
1407
  | Key | Action |
@@ -1262,7 +1420,7 @@ export default function MachinePlan() {
1262
1420
 
1263
1421
  #### Accessibility
1264
1422
 
1265
- The schedule is a figure named by `ariaLabel`, and its plot one tab stop with the role `application`, read as "schedule" (`aria-roledescription`), named alike and described by a summary: the lanes, the subtasks in view, the visible span, the findings by kind, then the keys. The canvas is hidden from a reader; the lane headers are text, and a group's fold button is named "Fold group" or "Unfold group" with the group's label. `ariaLabel` is required - name what the plan shows, "Plan of week 38".
1423
+ The schedule is a figure named by `aria-label`, and its plot one tab stop with the role `application`, read as "schedule" (`aria-roledescription`), named alike and described by a summary: the lanes, the subtasks in view, the visible span, the findings by kind, then the keys. The canvas is hidden from a reader; the lane headers are text, and a group's fold button is named "Fold group" or "Unfold group" with the group's label. `aria-label` is required - name what the plan shows, "Plan of week 38".
1266
1424
 
1267
1425
  When a key comes to rest, a polite live region beside the plot reads the active subtask: its lane, task and name, the day and the times, and every finding on it by name - an overlap, a dependency short by how much, blocked time. On a dependency it reads the task, the lag and where the line runs. A pointer announces nothing; [Findings as data](#findings-as-data) gives the application the same findings to list. The words are core's wording, so under `@umriss-ui/core/wording/de` the plot is read as "Belegungsplan".
1268
1426
 
@@ -1274,14 +1432,14 @@ Under forced colours the plot paints itself in the system colours: the work in t
1274
1432
 
1275
1433
  | Prop | Type | Default | Description |
1276
1434
  |---|---|---|---|
1277
- | `ref` | `Ref<``ScheduleHandle``>` | — | Receives the `ScheduleHandle` - not an element.<br>Shown in: [Place a mark of your own (The handle)](#the-handle) |
1278
- | `height` | `number` | `400` | Height in CSS pixels, both bands included. The width is the host's.<br>Shown in: [A day of tours](#first-schedule), [Start from an empty plan](#first-schedule), [Plan a day in the machine shop](#first-schedule) and 78 more |
1435
+ | `ref` | `Ref<HTMLDivElement> \| undefined` | — | Receives the root element. |
1436
+ | `height` | `number` | fills the frame, at least 400 px | Height in CSS pixels, both bands included, fixed. Left out, the schedule fills its frame - a stretched grid cell, a card the grid stretches, a container of definite height - and never goes below 400 px, which is also its height in a frame without one. A different floor goes through `style`'s `minHeight` ([ADR-0050](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0050-a-chart-fills-its-frame-never-below-its-floor.md)). The width is the host's.<br>Shown in: [A day of tours](#first-schedule), [Start from an empty plan](#first-schedule), [Plan a day in the machine shop](#first-schedule) and 80 more |
1279
1437
  | `laneHeight` | `number` | `44` | Height of one lane in pixels at the default font size of 16 px; the schedule multiplies it by the root font size over 16 px when that ratio is above 1, so lane names keep their room in a reader's larger font.<br>Shown in: [Scroll more lanes than fit (Lanes)](#lanes), [Measure a plan (Benchmark)](#benchmark) |
1280
1438
  | `headerWidth` | `number` | `160` | Width of the lane headers in pixels - at most 40 % of the schedule, so that on a phone the plot keeps the larger part.<br>Shown in: [What a lane header says (Lanes)](#lanes), [Scroll more lanes than fit (Lanes)](#lanes), [Group lanes under a head (Lane groups)](#lane-groups) and 1 more |
1281
1439
  | `calendar` | `CalendarInput` | `[]` | A working calendar: the intervals in which time counts. Nights and weekends outside them are cut out of the axis. Without intervals every hour counts, as on the wall clock.<br>Shown in: [Cut the nights out of the axis (Time axis and calendar)](#time-axis-and-calendar), [Plan in office hours only (Time axis and calendar)](#time-axis-and-calendar), [Plan the sprint around leave (Scenarios)](#plan-the-sprint-around-leave) |
1282
1440
  | `zoomLimits` | `ZoomLimits` | one hour to 28 days | The narrowest and widest time span zoom may reach, in milliseconds.<br>Shown in: [Move through the plan (Pan and zoom)](#pan-and-zoom) |
1283
1441
  | `snap` | `"ticks" \| number \| ``SnapRaster`` \| false` | `"ticks"` | The raster a drag lands on: `"ticks"` - the fine band's current step -, a step in milliseconds from local midnight, a step with an offset (shifts at 06:00, 14:00 and 22:00 are eight hours offset by six), or `false` for none. It shapes the ghost and the intent, never the data.<br>Shown in: [Snap to a duration (Snapping)](#snapping), [Snap to handover times (Snapping)](#snapping), [Place without snapping (Snapping)](#snapping) |
1284
- | `intents` | `readonly ``IntentKind``[]` | `[]` | The intents the caller handles. Each one enables its interaction; none leaves a read-only schedule ([ADR-0023](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0023-editing-is-controlled-the-schedule-reports-intents.md)).<br>Shown in: [Read a folded group (Lane groups)](#lane-groups), [Cut the nights out of the axis (Time axis and calendar)](#time-axis-and-calendar), [Plan work around someone's leave (Blocked time)](#blocked-time) and 18 more |
1442
+ | `intents` | `readonly ``IntentKind``[]` | `[]` | The intents the caller handles. Each one enables its interaction; none leaves a read-only schedule ([ADR-0023](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0023-editing-is-controlled-the-schedule-reports-intents.md)).<br>Shown in: [Show that a plan is on its way](#first-schedule), [Read a folded group (Lane groups)](#lane-groups), [Cut the nights out of the axis (Time axis and calendar)](#time-axis-and-calendar) and 19 more |
1285
1443
  | `canMoveTo` | `(subtask: `[`Subtask`](#subtasks)`, lane: string) => boolean` | every lane is open | Whether a subtask may go to a lane. It narrows `"lane"`, it does not enable it. Asked once per lane when a drag takes hold, and asked again at the drop. The lanes it turns down are marked from the first frame of the drag - a planner sees the refusal before meeting it - and over one of them the ghost stays on the last lane that was allowed, the cursor says no and a line ties the ghost to the pointer. A refused lane costs the lane and nothing else: a drop after it still reports the move in time. It is asked for work dragged in from outside as well, with the key and task the application declared in `placing`.<br>Shown in: [Keep work on the lanes that fit (Where a subtask may go)](#where-a-subtask-may-go), [Apply the same rule to work dragged in (Where a subtask may go)](#where-a-subtask-may-go), [Keep work out of someone's leave (Where a subtask may go)](#where-a-subtask-may-go) and 1 more |
1286
1444
  | `selectedTask` | `string \| null` | — | The selected task, controlled. Leave it out and the schedule keeps the selection itself.<br>Shown in: [Control the selection (Selection)](#selection) |
1287
1445
  | `onSelectedTaskChange` | `(task: string \| null, subtask: string \| null) => void` | — | Called when a click selects a task or clears the selection, with the subtask that was clicked - null where the click was on a dependency or on nothing. It is called again when another subtask of the same task is clicked.<br>Shown in: [Control the selection (Selection)](#selection) |
@@ -1292,7 +1450,8 @@ Under forced colours the plot paints itself in the system colours: the work in t
1292
1450
  | `label` | `(subtask: `[`Subtask`](#subtasks)`) => string` | — | What stands written in a bar: a function from a subtask to a line of text, or nothing for bars without text. The text is cut off with an ellipsis where the bar is too narrow for it and left out where even that would say nothing; a bar that began before the view keeps its text at the view's edge.<br>Shown in: [Plan a day in the machine shop](#first-schedule), [Plan a sprint for a team (Subtasks)](#subtasks), [Write into the bars (Bar labels)](#bar-labels) and 7 more |
1293
1451
  | `tooltip` | `false \| ((target: `[`ScheduleTooltipTarget`](#tooltip)`) => ReactNode)` | — | The tooltip on a hovered subtask or dependency: its order, its times, its parts and its findings. `false` switches it off; a function receives what the pointer rests on and returns content of the application's own.<br>Shown in: [Say what a milestone is in your own tooltip (Milestones)](#milestones), [Write your own tooltip content (Tooltip)](#tooltip), [Show a detail panel instead (Tooltip)](#tooltip) and 1 more |
1294
1452
  | `now` | `boolean \| number` | `false` | A line marking the present across the lanes: `true` follows the clock by the minute, an instant fixes it there.<br>Shown in: [Plan a day in the machine shop](#first-schedule), [Mark the present (Now line)](#now-line), [Follow the clock (Now line)](#now-line) and 2 more |
1295
- | `children` | `ReactNode` | — | `Lane`, `Dependencies` and `Subtasks`, in the order they are to stand and be drawn.<br>Shown in: [A day of tours](#first-schedule), [Start from an empty plan](#first-schedule), [Plan a day in the machine shop](#first-schedule) and 78 more |
1453
+ | `loading` | `boolean` | `false` | The plan is on its way, as the chart's `loading` says ([ADR-0042](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0042-a-rows-height-never-follows-what-it-shows.md)). A plan already drawn stays: it dims after a moment and takes no pointer - no new drag, no tooltip, no intent by key - until the answer is in; a drag begun before finishes and reports. The keys still walk it. With no lanes, placeholder lines stand in the lane column. The plot is `aria-busy` meanwhile.<br>Shown in: [Show that a plan is on its way](#first-schedule) |
1454
+ | `children` | `ReactNode` | — | `Lane`, `Dependencies` and `Subtasks`, in the order they are to stand and be drawn.<br>Shown in: [A day of tours](#first-schedule), [Start from an empty plan](#first-schedule), [Plan a day in the machine shop](#first-schedule) and 82 more |
1296
1455
 
1297
1456
  ###### Events
1298
1457
 
@@ -1305,28 +1464,12 @@ Under forced colours the plot paints itself in the system colours: the work in t
1305
1464
 
1306
1465
  | Prop | Type | Default | Description |
1307
1466
  |---|---|---|---|
1308
- | `ariaLabel` *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38".<br>Shown in: [A day of tours](#first-schedule), [Start from an empty plan](#first-schedule), [Plan a day in the machine shop](#first-schedule) and 78 more |
1309
-
1310
- ###### Styling
1467
+ | `aria-label` *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38".<br>Shown in: [A day of tours](#first-schedule), [Start from an empty plan](#first-schedule), [Plan a day in the machine shop](#first-schedule) and 82 more |
1311
1468
 
1312
- | Prop | Type | Default | Description |
1313
- |---|---|---|---|
1314
- | `className` | `string` | — | Goes to the root element. |
1315
- | `style` | `CSSProperties` | — | Goes to the root element. |
1469
+ Also takes every attribute of `<div>` – without `aria-label`, `children`.
1316
1470
 
1317
1471
  ##### Types on this page
1318
1472
 
1319
- ###### `ScheduleHandle`
1320
-
1321
- What a schedule offers a caller imperatively: the arithmetic between a
1322
- point on the screen and a time on a lane. Everything else is props.
1323
-
1324
- | Prop | Type | Default | Description |
1325
- |---|---|---|---|
1326
- | `positionAt` *required* | `(clientX: number, clientY: number) => { time: number; lane: string \| null } \| null` | — | The time and the lane at a client point, or null outside the plot.<br>Shown in: [Place a mark of your own (The handle)](#the-handle) |
1327
- | `clientPointOf` *required* | `(time: number, lane?: string) => { x: number; y: number } \| null` | — | The client point of a time - on the middle of a lane where one is named - or null while the schedule is not on screen.<br>Shown in: [Place a mark of your own (The handle)](#the-handle) |
1328
- | `visibleDomain` *required* | `() => readonly [number, number]` | — | The visible span, as two wall-clock instants. |
1329
-
1330
1473
  ###### `CalendarInput`
1331
1474
 
1332
1475
  From `@umriss-ui/charts`.
@@ -1523,11 +1666,11 @@ no subtask yet; the caller creates one, with its own identity, from
1523
1666
 
1524
1667
  #### Known limits
1525
1668
 
1526
- - No dependency types, critical path or auto-scheduling: the application decides where work goes, the schedule shows the result and its findings ([ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.42.0/docs/adr/0032-what-umriss-is-not.md)).
1669
+ - No dependency types, critical path or auto-scheduling: the application decides where work goes, the schedule shows the result and its findings ([ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.44.0/docs/adr/0032-what-umriss-is-not.md)).
1527
1670
  - No milestones yet: a point in time on a lane has no mark of its own.
1528
1671
  - A dependency joins two subtasks of one task; dependencies across tasks are not yet possible.
1529
1672
 
1530
- What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.42.0/docs/adr/0032-what-umriss-is-not.md).
1673
+ What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.44.0/docs/adr/0032-what-umriss-is-not.md).
1531
1674
 
1532
1675
  ## Plan
1533
1676
 
@@ -1568,7 +1711,7 @@ const LEGS: Subtask[] = [
1568
1711
  export default function LaneHeaders() {
1569
1712
  const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: [at(6), at(12)] } });
1570
1713
  return (
1571
- <Schedule ariaLabel="Two vans of the North depot" height={150} headerWidth={210}>
1714
+ <Schedule aria-label="Two vans of the North depot" height={150} headerWidth={210}>
1572
1715
  <Lane
1573
1716
  id="v1"
1574
1717
  label={
@@ -1632,7 +1775,7 @@ export default function ManyLanes() {
1632
1775
  const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: [at(6), at(14)] } });
1633
1776
  return (
1634
1777
  <Schedule
1635
- ariaLabel="Twenty vans, Tuesday morning"
1778
+ aria-label="Twenty vans, Tuesday morning"
1636
1779
  height={300}
1637
1780
  laneHeight={32}
1638
1781
  headerWidth={110}
@@ -1660,8 +1803,8 @@ The keys of [First schedule](#first-schedule) apply here.
1660
1803
 
1661
1804
  | Prop | Type | Default | Description |
1662
1805
  |---|---|---|---|
1663
- | `id` *required* | `string` | — | The identity subtasks name in their `lane`.<br>Shown in: [What a lane header says](#lanes), [Scroll more lanes than fit](#lanes), [The smallest schedule (Installation)](#installation) and 78 more |
1664
- | `label` | `ReactNode` | `id` | What the lane header says - the person, vehicle or room. Real text, read by a screen reader.<br>Shown in: [What a lane header says](#lanes), [Scroll more lanes than fit](#lanes), [The smallest schedule (Installation)](#installation) and 77 more |
1806
+ | `id` *required* | `string` | — | The identity subtasks name in their `lane`.<br>Shown in: [What a lane header says](#lanes), [Scroll more lanes than fit](#lanes), [The smallest schedule (Installation)](#installation) and 82 more |
1807
+ | `label` | `ReactNode` | `id` | What the lane header says - the person, vehicle or room. Real text, read by a screen reader.<br>Shown in: [What a lane header says](#lanes), [Scroll more lanes than fit](#lanes), [The smallest schedule (Installation)](#installation) and 81 more |
1665
1808
 
1666
1809
  ### Lane groups
1667
1810
 
@@ -1708,7 +1851,7 @@ const WORK: Subtask[] = [
1708
1851
  export default function AGroup() {
1709
1852
  const { Schedule, Lane, LaneGroup, Subtasks } = useSchedule({ initialView: { domain: [at(6), at(15)] } });
1710
1853
  return (
1711
- <Schedule ariaLabel="Two developers in a team, a designer and a tester outside it" height={240} headerWidth={200}>
1854
+ <Schedule aria-label="Two developers in a team, a designer and a tester outside it" height={240} headerWidth={200}>
1712
1855
  <LaneGroup id="developers" label="Developers">
1713
1856
  <Lane id="arjun" label="Arjun Mehta" />
1714
1857
  <Lane id="chloe" label="Chloe Durand" />
@@ -1751,7 +1894,7 @@ const LEGS: Subtask[] = [
1751
1894
  export default function Nesting() {
1752
1895
  const { Schedule, Lane, LaneGroup, Subtasks } = useSchedule({ initialView: { domain: [at(6), at(15)] } });
1753
1896
  return (
1754
- <Schedule ariaLabel="North depot with its vans and a truck, and a van from Riverside" height={330} headerWidth={200}>
1897
+ <Schedule aria-label="North depot with its vans and a truck, and a van from Riverside" height={330} headerWidth={200}>
1755
1898
  <LaneGroup id="north" label="North depot">
1756
1899
  <LaneGroup id="vans" label="Vans">
1757
1900
  <Lane id="fp-214" label={`Van ${plate("FP 214 K")}`} />
@@ -1804,7 +1947,7 @@ export default function FoldFromOutside() {
1804
1947
  Unfold everything
1805
1948
  </Button>
1806
1949
  </Stack>
1807
- <Schedule ariaLabel="Two teams rolling out a release, folded from outside" height={280}>
1950
+ <Schedule aria-label="Two teams rolling out a release, folded from outside" height={280}>
1808
1951
  <LaneGroup id="payments" label="Payments">
1809
1952
  <Lane id="priya" label="Priya Raman" />
1810
1953
  <Lane id="jonas" label="Jonas Keller" />
@@ -1876,7 +2019,7 @@ export default function TheMiniature() {
1876
2019
  return (
1877
2020
  <Stack gap={3}>
1878
2021
  <Schedule
1879
- ariaLabel="A depot that folds into one row"
2022
+ aria-label="A depot that folds into one row"
1880
2023
  height={240}
1881
2024
  intents={["move", "lane"]}
1882
2025
  onIntent={(intent) => {
@@ -1931,7 +2074,7 @@ const WORK: Subtask[] = [
1931
2074
  export default function StartWithGroupsFolded() {
1932
2075
  const { Schedule, Lane, LaneGroup, Subtasks } = useSchedule({ initialView: { domain: [at(6), at(15)], folded: ["discovery"] } });
1933
2076
  return (
1934
- <Schedule ariaLabel="Two teams rolling out a release, Discovery folded at the start" height={280}>
2077
+ <Schedule aria-label="Two teams rolling out a release, Discovery folded at the start" height={280}>
1935
2078
  <LaneGroup id="payments" label="Payments">
1936
2079
  <Lane id="priya" label="Priya Raman" />
1937
2080
  <Lane id="jonas" label="Jonas Keller" />
@@ -1969,7 +2112,7 @@ The keys of [First schedule](#first-schedule) apply here.
1969
2112
 
1970
2113
  - A folded group shows no summary - no utilisation band, no bar spanning its work: a computed claim would look like a drawn fact ([ADR-0025](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0025-a-lane-group-is-structure-over-lanes-never-a-lane.md)).
1971
2114
 
1972
- What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.42.0/docs/adr/0032-what-umriss-is-not.md).
2115
+ What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.44.0/docs/adr/0032-what-umriss-is-not.md).
1973
2116
 
1974
2117
  ### Subtasks
1975
2118
 
@@ -2005,7 +2148,7 @@ const ROUNDS: Subtask[] = [
2005
2148
  export default function LeadInAndLeadOut() {
2006
2149
  const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: [at(6), at(14, 30)] } });
2007
2150
  return (
2008
- <Schedule ariaLabel="Three rounds of one van" height={110}>
2151
+ <Schedule aria-label="Three rounds of one van" height={110}>
2009
2152
  <Lane id="van" label="Van FP 214 K" />
2010
2153
  <Subtasks data={ROUNDS} tasks={TOURS} />
2011
2154
  </Schedule>
@@ -2043,7 +2186,7 @@ const WORK: Subtask[] = [
2043
2186
  export default function TaskColours() {
2044
2187
  const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: [at(16, 6), at(20, 20)] } });
2045
2188
  return (
2046
- <Schedule ariaLabel="Projects due this week among the rest" height={150}>
2189
+ <Schedule aria-label="Projects due this week among the rest" height={150}>
2047
2190
  <Lane id="chloe" label="Chloe Durand" />
2048
2191
  <Lane id="eva" label="Eva Novak" />
2049
2192
  <Subtasks data={WORK} tasks={PROJECTS} />
@@ -2149,7 +2292,7 @@ const SPRINT_14: readonly [number, number] = [new Date(2026, 2, 9, 6).getTime(),
2149
2292
  export default function ASprint() {
2150
2293
  const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: SPRINT_14 } });
2151
2294
  return (
2152
- <Schedule ariaLabel="Sprint 14, 9 to 20 March" height={500} label={(s) => s.name ?? s.id}>
2295
+ <Schedule aria-label="Sprint 14, 9 to 20 March" height={500} label={(s) => s.name ?? s.id}>
2153
2296
  {PEOPLE.map((person) => (
2154
2297
  <Lane key={person.id} id={person.id} label={person.name} />
2155
2298
  ))}
@@ -2169,18 +2312,18 @@ The keys of [First schedule](#first-schedule) apply here.
2169
2312
 
2170
2313
  | Prop | Type | Default | Description |
2171
2314
  |---|---|---|---|
2172
- | `data` *required* | `readonly ``Subtask``[]` | — | The subtasks, as the caller holds them. The schedule never changes one.<br>Shown in: [Main time, lead-in and lead-out](#subtasks), [Colour by what the task means](#subtasks), [Plan a sprint for a team](#subtasks) and 78 more |
2173
- | `tasks` *required* | `readonly ``Task``[]` | — | The tasks the subtasks belong to - their identity and their colour. A subtask whose task is not among them is drawn in a muted colour.<br>Shown in: [Main time, lead-in and lead-out](#subtasks), [Colour by what the task means](#subtasks), [Plan a sprint for a team](#subtasks) and 78 more |
2315
+ | `data` *required* | `readonly ``Subtask``[]` | — | The subtasks, as the caller holds them. The schedule never changes one.<br>Shown in: [Main time, lead-in and lead-out](#subtasks), [Colour by what the task means](#subtasks), [Plan a sprint for a team](#subtasks) and 82 more |
2316
+ | `tasks` *required* | `readonly ``Task``[]` | — | The tasks the subtasks belong to - their identity and their colour. A subtask whose task is not among them is drawn in a muted colour.<br>Shown in: [Main time, lead-in and lead-out](#subtasks), [Colour by what the task means](#subtasks), [Plan a sprint for a team](#subtasks) and 82 more |
2174
2317
 
2175
2318
  ##### `Subtask`
2176
2319
 
2177
2320
  | Prop | Type | Default | Description |
2178
2321
  |---|---|---|---|
2179
- | `id` *required* | `string` | — | The caller's identity of the subtask.<br>Shown in: [Main time, lead-in and lead-out](#subtasks), [Colour by what the task means](#subtasks), [Plan a sprint for a team](#subtasks) and 75 more |
2180
- | `task` *required* | `string` | — | The id of the task it belongs to.<br>Shown in: [Main time, lead-in and lead-out](#subtasks), [Colour by what the task means](#subtasks), [The smallest schedule (Installation)](#installation) and 71 more |
2181
- | `lane` *required* | `string` | — | The id of the lane it sits on.<br>Shown in: [Main time, lead-in and lead-out](#subtasks), [Colour by what the task means](#subtasks), [The smallest schedule (Installation)](#installation) and 71 more |
2182
- | `from` *required* | `number` | — | Start of the main time.<br>Shown in: [Main time, lead-in and lead-out](#subtasks), [Colour by what the task means](#subtasks), [The smallest schedule (Installation)](#installation) and 71 more |
2183
- | `to` *required* | `number` | — | End of the main time.<br>Shown in: [Main time, lead-in and lead-out](#subtasks), [Colour by what the task means](#subtasks), [The smallest schedule (Installation)](#installation) and 71 more |
2322
+ | `id` *required* | `string` | — | The caller's identity of the subtask.<br>Shown in: [Main time, lead-in and lead-out](#subtasks), [Colour by what the task means](#subtasks), [Plan a sprint for a team](#subtasks) and 79 more |
2323
+ | `task` *required* | `string` | — | The id of the task it belongs to.<br>Shown in: [Main time, lead-in and lead-out](#subtasks), [Colour by what the task means](#subtasks), [The smallest schedule (Installation)](#installation) and 75 more |
2324
+ | `lane` *required* | `string` | — | The id of the lane it sits on.<br>Shown in: [Main time, lead-in and lead-out](#subtasks), [Colour by what the task means](#subtasks), [The smallest schedule (Installation)](#installation) and 75 more |
2325
+ | `from` *required* | `number` | — | Start of the main time.<br>Shown in: [Main time, lead-in and lead-out](#subtasks), [Colour by what the task means](#subtasks), [The smallest schedule (Installation)](#installation) and 75 more |
2326
+ | `to` *required* | `number` | — | End of the main time.<br>Shown in: [Main time, lead-in and lead-out](#subtasks), [Colour by what the task means](#subtasks), [The smallest schedule (Installation)](#installation) and 75 more |
2184
2327
  | `leadIn` | `number` | — | Duration of the lead-in before the main time; none is zero.<br>Shown in: [Main time, lead-in and lead-out](#subtasks), [The smallest schedule (Installation)](#installation), [A day of tours (First schedule)](#first-schedule) and 25 more |
2185
2328
  | `leadOut` | `number` | — | Duration of the lead-out after the main time; none is zero.<br>Shown in: [Main time, lead-in and lead-out](#subtasks), [The smallest schedule (Installation)](#installation), [A day of tours (First schedule)](#first-schedule) and 22 more |
2186
2329
  | `name` | `string` | — | What the subtask is called, where the schedule names it.<br>Shown in: [Plan a sprint for a team](#subtasks), [Write into the bars (Bar labels)](#bar-labels), [Join the stops of a tour (Dependencies)](#dependencies) and 28 more |
@@ -2191,9 +2334,9 @@ The keys of [First schedule](#first-schedule) apply here.
2191
2334
 
2192
2335
  | Prop | Type | Default | Description |
2193
2336
  |---|---|---|---|
2194
- | `id` *required* | `string` | — | The caller's identity of the task.<br>Shown in: [Main time, lead-in and lead-out](#subtasks), [Colour by what the task means](#subtasks), [The smallest schedule (Installation)](#installation) and 70 more |
2195
- | `color` *required* | `string` | — | Any CSS colour - a literal, a token, `light-dark(…)`. The colour means what the caller's domain needs it to mean.<br>Shown in: [Main time, lead-in and lead-out](#subtasks), [Colour by what the task means](#subtasks), [The smallest schedule (Installation)](#installation) and 70 more |
2196
- | `name` | `string` | — | What the task is called, where the schedule names it.<br>Shown in: [Main time, lead-in and lead-out](#subtasks), [Colour by what the task means](#subtasks), [The smallest schedule (Installation)](#installation) and 70 more |
2337
+ | `id` *required* | `string` | — | The caller's identity of the task.<br>Shown in: [Main time, lead-in and lead-out](#subtasks), [Colour by what the task means](#subtasks), [The smallest schedule (Installation)](#installation) and 74 more |
2338
+ | `color` *required* | `string` | — | Any CSS colour - a literal, a token, `light-dark(…)`. The colour means what the caller's domain needs it to mean.<br>Shown in: [Main time, lead-in and lead-out](#subtasks), [Colour by what the task means](#subtasks), [The smallest schedule (Installation)](#installation) and 74 more |
2339
+ | `name` | `string` | — | What the task is called, where the schedule names it.<br>Shown in: [Main time, lead-in and lead-out](#subtasks), [Colour by what the task means](#subtasks), [The smallest schedule (Installation)](#installation) and 74 more |
2197
2340
 
2198
2341
  ##### Types on this page
2199
2342
 
@@ -2256,7 +2399,7 @@ export default function BarLabels() {
2256
2399
  const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: DAY } });
2257
2400
  return (
2258
2401
  <Schedule
2259
- ariaLabel="Tuesday, 17 March, with the work items written in"
2402
+ aria-label="Tuesday, 17 March, with the work items written in"
2260
2403
  height={196}
2261
2404
  label={(subtask) => subtask.name ?? subtask.id}
2262
2405
  >
@@ -2279,7 +2422,7 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
2279
2422
 
2280
2423
  | Prop | Type | Default | Description |
2281
2424
  |---|---|---|---|
2282
- | [`height`](#first-schedule) | `number` | `400` | Height in CSS pixels, both bands included. The width is the host's. |
2425
+ | [`height`](#first-schedule) | `number` | fills the frame, at least 400 px | Height in CSS pixels, both bands included, fixed. Left out, the schedule fills its frame - a stretched grid cell, a card the grid stretches, a container of definite height - and never goes below 400 px, which is also its height in a frame without one. A different floor goes through `style`'s `minHeight` ([ADR-0050](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0050-a-chart-fills-its-frame-never-below-its-floor.md)). The width is the host's. |
2283
2426
  | [`label`](#first-schedule) | `(subtask: `[`Subtask`](#subtasks)`) => string` | — | What stands written in a bar: a function from a subtask to a line of text, or nothing for bars without text. The text is cut off with an ellipsis where the bar is too narrow for it and left out where even that would say nothing; a bar that began before the view keeps its text at the view's edge. |
2284
2427
  | [`children`](#first-schedule) | `ReactNode` | — | `Lane`, `Dependencies` and `Subtasks`, in the order they are to stand and be drawn. |
2285
2428
 
@@ -2287,7 +2430,7 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
2287
2430
 
2288
2431
  | Prop | Type | Default | Description |
2289
2432
  |---|---|---|---|
2290
- | [`ariaLabel`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
2433
+ | [`aria-label`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
2291
2434
 
2292
2435
  From `LaneProps` — the full table stands on [Lanes](#lanes).
2293
2436
 
@@ -2371,7 +2514,7 @@ const WORK: Subtask[] = [
2371
2514
  export default function Provisional() {
2372
2515
  const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: [at(6, 30), at(11)] } });
2373
2516
  return (
2374
- <Schedule ariaLabel="Agreed work above, provisional work below" height={144}>
2517
+ <Schedule aria-label="Agreed work above, provisional work below" height={144}>
2375
2518
  <Lane id="released" label="Agreed, with lead-in" />
2376
2519
  <Lane id="draft" label="Provisional" />
2377
2520
  <Subtasks data={WORK} tasks={PROJECTS} />
@@ -2401,7 +2544,7 @@ export default function Fixed() {
2401
2544
  const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: [at(6, 30), at(11)] } });
2402
2545
  return (
2403
2546
  <Schedule
2404
- ariaLabel="A round that may move above, a booked slot below"
2547
+ aria-label="A round that may move above, a booked slot below"
2405
2548
  height={144}
2406
2549
  label={(leg) => (leg.id === "nailed" ? "T-05 · booked slot" : "T-05")}
2407
2550
  >
@@ -2435,7 +2578,7 @@ export default function Muted() {
2435
2578
  const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: [at(6, 30), at(11)] } });
2436
2579
  return (
2437
2580
  <Schedule
2438
- ariaLabel="Our team's work above, another team's below"
2581
+ aria-label="Our team's work above, another team's below"
2439
2582
  height={144}
2440
2583
  label={() => "Checkout migration"}
2441
2584
  >
@@ -2468,7 +2611,7 @@ const WORK: Subtask[] = [
2468
2611
  export default function Progress() {
2469
2612
  const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: [at(6, 30), at(11)] } });
2470
2613
  return (
2471
- <Schedule ariaLabel="Work claiming nothing above, work at 65 per cent below" height={144}>
2614
+ <Schedule aria-label="Work claiming nothing above, work at 65 per cent below" height={144}>
2472
2615
  <Lane id="unclaimed" label="Says nothing" />
2473
2616
  <Lane id="started" label="65 per cent done" />
2474
2617
  <Subtasks data={WORK} tasks={PROJECTS} />
@@ -2501,7 +2644,7 @@ const WORK: Subtask[] = [
2501
2644
  export default function Open() {
2502
2645
  const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: [at(6, 30), at(11)] } });
2503
2646
  return (
2504
- <Schedule ariaLabel="Work running past the view at either edge" height={188}>
2647
+ <Schedule aria-label="Work running past the view at either edge" height={188}>
2505
2648
  <Lane id="runs-on" label="Runs on" />
2506
2649
  <Lane id="whole" label="Wholly in view" />
2507
2650
  <Lane id="began" label="Began before" />
@@ -2567,7 +2710,7 @@ export default function Combinations() {
2567
2710
  const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: [at(6, 30), at(11)] } });
2568
2711
  return (
2569
2712
  <Schedule
2570
- ariaLabel="Bars carrying several appearances at once"
2713
+ aria-label="Bars carrying several appearances at once"
2571
2714
  height={230}
2572
2715
  label={(leg) => NAMES[leg.id] ?? leg.id}
2573
2716
  >
@@ -2590,7 +2733,7 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
2590
2733
 
2591
2734
  | Prop | Type | Default | Description |
2592
2735
  |---|---|---|---|
2593
- | [`height`](#first-schedule) | `number` | `400` | Height in CSS pixels, both bands included. The width is the host's. |
2736
+ | [`height`](#first-schedule) | `number` | fills the frame, at least 400 px | Height in CSS pixels, both bands included, fixed. Left out, the schedule fills its frame - a stretched grid cell, a card the grid stretches, a container of definite height - and never goes below 400 px, which is also its height in a frame without one. A different floor goes through `style`'s `minHeight` ([ADR-0050](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0050-a-chart-fills-its-frame-never-below-its-floor.md)). The width is the host's. |
2594
2737
  | [`label`](#first-schedule) | `(subtask: `[`Subtask`](#subtasks)`) => string` | — | What stands written in a bar: a function from a subtask to a line of text, or nothing for bars without text. The text is cut off with an ellipsis where the bar is too narrow for it and left out where even that would say nothing; a bar that began before the view keeps its text at the view's edge. |
2595
2738
  | [`children`](#first-schedule) | `ReactNode` | — | `Lane`, `Dependencies` and `Subtasks`, in the order they are to stand and be drawn. |
2596
2739
 
@@ -2598,7 +2741,7 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
2598
2741
 
2599
2742
  | Prop | Type | Default | Description |
2600
2743
  |---|---|---|---|
2601
- | [`ariaLabel`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
2744
+ | [`aria-label`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
2602
2745
 
2603
2746
  From `LaneProps` — the full table stands on [Lanes](#lanes).
2604
2747
 
@@ -2684,7 +2827,7 @@ const WORK: Subtask[] = [
2684
2827
  export default function DoubleBooking() {
2685
2828
  const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: [at(8), at(16)] } });
2686
2829
  return (
2687
- <Schedule ariaLabel="Kofi booked on two projects at once" height={150}>
2830
+ <Schedule aria-label="Kofi booked on two projects at once" height={150}>
2688
2831
  <Lane id="kofi" label="Kofi Mensah" />
2689
2832
  <Lane id="hana" label="Hana Sato" />
2690
2833
  <Subtasks data={WORK} tasks={PROJECTS} />
@@ -2717,7 +2860,7 @@ const LEGS: Subtask[] = [
2717
2860
  export default function LeadOutMeetsLeadIn() {
2718
2861
  const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: [at(6, 30), at(11, 30)] } });
2719
2862
  return (
2720
- <Schedule ariaLabel="Two rounds of one van, touching at unloading and loading" height={110}>
2863
+ <Schedule aria-label="Two rounds of one van, touching at unloading and loading" height={110}>
2721
2864
  <Lane id="fp-402" label="Van FP 402 R" />
2722
2865
  <Subtasks data={LEGS} tasks={TOURS} />
2723
2866
  </Schedule>
@@ -2751,7 +2894,7 @@ const WORK: Subtask[] = [
2751
2894
  export default function ThreeAtOnce() {
2752
2895
  const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: [at(7), at(14)] } });
2753
2896
  return (
2754
- <Schedule ariaLabel="Priya on three incidents at once" height={150}>
2897
+ <Schedule aria-label="Priya on three incidents at once" height={150}>
2755
2898
  <Lane id="priya" label="Priya Raman" />
2756
2899
  <Lane id="jonas" label="Jonas Keller" />
2757
2900
  <Subtasks data={WORK} tasks={INCIDENTS} />
@@ -2774,14 +2917,14 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
2774
2917
 
2775
2918
  | Prop | Type | Default | Description |
2776
2919
  |---|---|---|---|
2777
- | [`height`](#first-schedule) | `number` | `400` | Height in CSS pixels, both bands included. The width is the host's. |
2920
+ | [`height`](#first-schedule) | `number` | fills the frame, at least 400 px | Height in CSS pixels, both bands included, fixed. Left out, the schedule fills its frame - a stretched grid cell, a card the grid stretches, a container of definite height - and never goes below 400 px, which is also its height in a frame without one. A different floor goes through `style`'s `minHeight` ([ADR-0050](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0050-a-chart-fills-its-frame-never-below-its-floor.md)). The width is the host's. |
2778
2921
  | [`children`](#first-schedule) | `ReactNode` | — | `Lane`, `Dependencies` and `Subtasks`, in the order they are to stand and be drawn. |
2779
2922
 
2780
2923
  ###### Accessibility
2781
2924
 
2782
2925
  | Prop | Type | Default | Description |
2783
2926
  |---|---|---|---|
2784
- | [`ariaLabel`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
2927
+ | [`aria-label`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
2785
2928
 
2786
2929
  From `LaneProps` — the full table stands on [Lanes](#lanes).
2787
2930
 
@@ -2833,7 +2976,7 @@ From `ScheduleView` — the full table stands on [View](#view).
2833
2976
 
2834
2977
  - No stacked layout on request yet: an overlap is always drawn offset, as the finding it is.
2835
2978
 
2836
- What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.42.0/docs/adr/0032-what-umriss-is-not.md).
2979
+ What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.44.0/docs/adr/0032-what-umriss-is-not.md).
2837
2980
 
2838
2981
  ### Dependencies
2839
2982
 
@@ -2882,7 +3025,7 @@ const DRIVES: Dependency[] = [
2882
3025
  export default function StopsOfATour() {
2883
3026
  const { Schedule, Lane, Subtasks, Dependencies } = useSchedule({ initialView: { domain: [at(7), at(10)] } });
2884
3027
  return (
2885
- <Schedule ariaLabel="Two morning tours with their stops" height={150}>
3028
+ <Schedule aria-label="Two morning tours with their stops" height={150}>
2886
3029
  <Lane id="fp-214" label="Van FP 214 K" />
2887
3030
  <Lane id="fp-377" label="E-van FP 377 K" />
2888
3031
  <Dependencies data={DRIVES} />
@@ -2926,7 +3069,7 @@ const HANDOVERS: Dependency[] = [
2926
3069
  export default function Anchors() {
2927
3070
  const { Schedule, Lane, Subtasks, Dependencies } = useSchedule({ initialView: { domain: [at(6, 30), at(16, 30)] } });
2928
3071
  return (
2929
- <Schedule ariaLabel="Two handovers with different anchors" height={150}>
3072
+ <Schedule aria-label="Two handovers with different anchors" height={150}>
2930
3073
  <Lane id="truck" label="Truck FP 520 E" />
2931
3074
  <Lane id="van" label="Van FP 290 E" />
2932
3075
  <Dependencies data={HANDOVERS} />
@@ -2974,7 +3117,7 @@ function Variant({ attach }: { attach: DependencyAttachment }) {
2974
3117
  <Text size="xs" tone="muted" mono>
2975
3118
  attach=&quot;{attach}&quot;
2976
3119
  </Text>
2977
- <Schedule ariaLabel={`A release through three engineers, lines from the ${attach}`} height={188} attach={attach}>
3120
+ <Schedule aria-label={`A release through three engineers, lines from the ${attach}`} height={188} attach={attach}>
2978
3121
  <Lane id="priya" label="Priya Raman" />
2979
3122
  <Lane id="ada" label="Ada Mwangi" />
2980
3123
  <Lane id="sam" label="Sam Okafor" />
@@ -3030,7 +3173,7 @@ function Variant({ ends }: { ends: DependencyEnds }) {
3030
3173
  <Text size="xs" tone="muted" mono>
3031
3174
  ends=&quot;{ends}&quot;
3032
3175
  </Text>
3033
- <Schedule ariaLabel={`Design, build and test, ends ${ends}`} height={188} attach="nearest" ends={ends}>
3176
+ <Schedule aria-label={`Design, build and test, ends ${ends}`} height={188} attach="nearest" ends={ends}>
3034
3177
  <Lane id="noah" label="Noah Fischer" />
3035
3178
  <Lane id="arjun" label="Arjun Mehta" />
3036
3179
  <Lane id="eva" label="Eva Novak" />
@@ -3077,7 +3220,7 @@ const HANDOVERS: Dependency[] = [{ id: "transfer", from: "line-haul", to: "round
3077
3220
  export default function ViolatedDependency() {
3078
3221
  const { Schedule, Lane, Subtasks, Dependencies } = useSchedule({ initialView: { domain: [at(6, 30), at(11)] } });
3079
3222
  return (
3080
- <Schedule ariaLabel="A handover from the truck to the van that is too short" height={150}>
3223
+ <Schedule aria-label="A handover from the truck to the van that is too short" height={150}>
3081
3224
  <Lane id="truck" label="Truck FP 118 R" />
3082
3225
  <Lane id="van" label="Van FP 214 K" />
3083
3226
  <Dependencies data={HANDOVERS} />
@@ -3120,7 +3263,7 @@ const HANDOVERS: Dependency[] = [
3120
3263
  export default function OneHandoverDrawnItsOwnWay() {
3121
3264
  const { Schedule, Lane, Subtasks, Dependencies } = useSchedule({ initialView: { domain: [at(6, 30), at(12, 30)] } });
3122
3265
  return (
3123
- <Schedule ariaLabel="Design, build, test and a sign-off drawn its own way" height={188} attach="nearest" ends="none">
3266
+ <Schedule aria-label="Design, build, test and a sign-off drawn its own way" height={188} attach="nearest" ends="none">
3124
3267
  <Lane id="noah" label="Noah Fischer" />
3125
3268
  <Lane id="arjun" label="Arjun Mehta" />
3126
3269
  <Lane id="eva" label="Eva Novak" />
@@ -3203,9 +3346,9 @@ type DependencyEnds = "dot" | "none";
3203
3346
 
3204
3347
  #### Known limits
3205
3348
 
3206
- - Only finish-to-start within one task; no dependency types and no critical path ([ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.42.0/docs/adr/0032-what-umriss-is-not.md)).
3349
+ - Only finish-to-start within one task; no dependency types and no critical path ([ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.44.0/docs/adr/0032-what-umriss-is-not.md)).
3207
3350
 
3208
- What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.42.0/docs/adr/0032-what-umriss-is-not.md).
3351
+ What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.44.0/docs/adr/0032-what-umriss-is-not.md).
3209
3352
 
3210
3353
  ### Routes
3211
3354
 
@@ -3246,7 +3389,7 @@ const HANDOVERS: Dependency[] = [
3246
3389
  export default function Curve() {
3247
3390
  const { Schedule, Lane, Subtasks, Dependencies } = useSchedule({ initialView: { domain: [at(6, 30), at(13, 30)] } });
3248
3391
  return (
3249
- <Schedule ariaLabel="Search results, from layout to polish, curved lines" height={188}>
3392
+ <Schedule aria-label="Search results, from layout to polish, curved lines" height={188}>
3250
3393
  <Lane id="noah" label="Noah Fischer" />
3251
3394
  <Lane id="chloe" label="Chloe Durand" />
3252
3395
  <Lane id="eva" label="Eva Novak" />
@@ -3286,7 +3429,7 @@ const HANDOVERS: Dependency[] = [
3286
3429
  export default function Straight() {
3287
3430
  const { Schedule, Lane, Subtasks, Dependencies } = useSchedule({ initialView: { domain: [at(6, 30), at(13, 30)] } });
3288
3431
  return (
3289
- <Schedule ariaLabel="Search results, from layout to polish, straight lines" height={188} route="straight" attach="nearest">
3432
+ <Schedule aria-label="Search results, from layout to polish, straight lines" height={188} route="straight" attach="nearest">
3290
3433
  <Lane id="noah" label="Noah Fischer" />
3291
3434
  <Lane id="chloe" label="Chloe Durand" />
3292
3435
  <Lane id="eva" label="Eva Novak" />
@@ -3326,7 +3469,7 @@ const HANDOVERS: Dependency[] = [
3326
3469
  export default function Orthogonal() {
3327
3470
  const { Schedule, Lane, Subtasks, Dependencies } = useSchedule({ initialView: { domain: [at(6, 30), at(13, 30)] } });
3328
3471
  return (
3329
- <Schedule ariaLabel="Search results, from layout to polish, orthogonal lines" height={188} route="orthogonal" attach="nearest">
3472
+ <Schedule aria-label="Search results, from layout to polish, orthogonal lines" height={188} route="orthogonal" attach="nearest">
3330
3473
  <Lane id="noah" label="Noah Fischer" />
3331
3474
  <Lane id="chloe" label="Chloe Durand" />
3332
3475
  <Lane id="eva" label="Eva Novak" />
@@ -3367,7 +3510,7 @@ const HANDOVERS: Dependency[] = [
3367
3510
  export default function PerDependency() {
3368
3511
  const { Schedule, Lane, Subtasks, Dependencies } = useSchedule({ initialView: { domain: [at(6, 30), at(13, 30)] } });
3369
3512
  return (
3370
- <Schedule ariaLabel="Search results, from layout to polish, straight lines but one" height={188} route="straight" attach="nearest">
3513
+ <Schedule aria-label="Search results, from layout to polish, straight lines but one" height={188} route="straight" attach="nearest">
3371
3514
  <Lane id="noah" label="Noah Fischer" />
3372
3515
  <Lane id="chloe" label="Chloe Durand" />
3373
3516
  <Lane id="eva" label="Eva Novak" />
@@ -3388,7 +3531,7 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
3388
3531
 
3389
3532
  | Prop | Type | Default | Description |
3390
3533
  |---|---|---|---|
3391
- | [`height`](#first-schedule) | `number` | `400` | Height in CSS pixels, both bands included. The width is the host's. |
3534
+ | [`height`](#first-schedule) | `number` | fills the frame, at least 400 px | Height in CSS pixels, both bands included, fixed. Left out, the schedule fills its frame - a stretched grid cell, a card the grid stretches, a container of definite height - and never goes below 400 px, which is also its height in a frame without one. A different floor goes through `style`'s `minHeight` ([ADR-0050](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0050-a-chart-fills-its-frame-never-below-its-floor.md)). The width is the host's. |
3392
3535
  | [`route`](#first-schedule) | [`DependencyRoute`](#first-schedule)<br>`"curve" \| "straight" \| "orthogonal"` | `"curve"` | How the dependencies are drawn: a `"curve"` that leaves and arrives forwards, a `"straight"` line, or `"orthogonal"` segments. A dependency may say otherwise for itself. |
3393
3536
  | [`attach`](#first-schedule) | [`DependencyAttachment`](#first-schedule)<br>`"centre" \| "nearest"` | `"centre"` | Where a dependency's ends sit on their bars: the `"centre"` of both, or the `"nearest"` edge - which is the shortest line between two stops. Within one lane both mean the middle. A dependency may say otherwise for itself. It changes the picture and never a finding: whether a dependency is violated follows from its `leaves` and `arrives` alone. |
3394
3537
  | [`children`](#first-schedule) | `ReactNode` | — | `Lane`, `Dependencies` and `Subtasks`, in the order they are to stand and be drawn. |
@@ -3397,7 +3540,7 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
3397
3540
 
3398
3541
  | Prop | Type | Default | Description |
3399
3542
  |---|---|---|---|
3400
- | [`ariaLabel`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
3543
+ | [`aria-label`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
3401
3544
 
3402
3545
  From `LaneProps` — the full table stands on [Lanes](#lanes).
3403
3546
 
@@ -3538,7 +3681,7 @@ const WEEK: readonly [number, number] = [new Date(2026, 2, 16).getTime(), new Da
3538
3681
  export default function AWeek() {
3539
3682
  const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: WEEK } });
3540
3683
  return (
3541
- <Schedule ariaLabel="On-call rota, 16 to 23 March" height={420}>
3684
+ <Schedule aria-label="On-call rota, 16 to 23 March" height={420}>
3542
3685
  {ENGINEERS.map((engineer) => (
3543
3686
  <Lane key={engineer.id} id={engineer.id} label={engineer.name} />
3544
3687
  ))}
@@ -3582,7 +3725,7 @@ export default function WorkingCalendar() {
3582
3725
  const [legs, setLegs] = useState<readonly Subtask[]>(LEGS);
3583
3726
  return (
3584
3727
  <Schedule
3585
- ariaLabel="Three days at East Gate depot, nights removed"
3728
+ aria-label="Three days at East Gate depot, nights removed"
3586
3729
  calendar={OPENING_HOURS}
3587
3730
  height={220}
3588
3731
  intents={["move"]}
@@ -3693,7 +3836,7 @@ export default function OfficeHours() {
3693
3836
  const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: [at(9, 9), at(20, 17)] } });
3694
3837
  return (
3695
3838
  <Schedule
3696
- ariaLabel="Sprint 14 of the web team, office hours only"
3839
+ aria-label="Sprint 14 of the web team, office hours only"
3697
3840
  calendar={OFFICE_HOURS}
3698
3841
  height={280}
3699
3842
  label={(item) => item.name ?? item.id}
@@ -3721,7 +3864,7 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
3721
3864
 
3722
3865
  | Prop | Type | Default | Description |
3723
3866
  |---|---|---|---|
3724
- | [`height`](#first-schedule) | `number` | `400` | Height in CSS pixels, both bands included. The width is the host's. |
3867
+ | [`height`](#first-schedule) | `number` | fills the frame, at least 400 px | Height in CSS pixels, both bands included, fixed. Left out, the schedule fills its frame - a stretched grid cell, a card the grid stretches, a container of definite height - and never goes below 400 px, which is also its height in a frame without one. A different floor goes through `style`'s `minHeight` ([ADR-0050](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0050-a-chart-fills-its-frame-never-below-its-floor.md)). The width is the host's. |
3725
3868
  | [`calendar`](#first-schedule) | [`CalendarInput`](#first-schedule) | `[]` | A working calendar: the intervals in which time counts. Nights and weekends outside them are cut out of the axis. Without intervals every hour counts, as on the wall clock. |
3726
3869
  | [`intents`](#first-schedule) | `readonly `[`IntentKind`](#first-schedule)`[]` | `[]` | The intents the caller handles. Each one enables its interaction; none leaves a read-only schedule ([ADR-0023](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0023-editing-is-controlled-the-schedule-reports-intents.md)). |
3727
3870
  | [`label`](#first-schedule) | `(subtask: `[`Subtask`](#subtasks)`) => string` | — | What stands written in a bar: a function from a subtask to a line of text, or nothing for bars without text. The text is cut off with an ellipsis where the bar is too narrow for it and left out where even that would say nothing; a bar that began before the view keeps its text at the view's edge. |
@@ -3737,7 +3880,7 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
3737
3880
 
3738
3881
  | Prop | Type | Default | Description |
3739
3882
  |---|---|---|---|
3740
- | [`ariaLabel`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
3883
+ | [`aria-label`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
3741
3884
 
3742
3885
  From `LaneProps` — the full table stands on [Lanes](#lanes).
3743
3886
 
@@ -3870,7 +4013,7 @@ export default function Leave() {
3870
4013
 
3871
4014
  return (
3872
4015
  <Stack gap={3}>
3873
- <Schedule ariaLabel="Team plan, week of 16 March" height={240}>
4016
+ <Schedule aria-label="Team plan, week of 16 March" height={240}>
3874
4017
  {PEOPLE.map((person) => (
3875
4018
  <Lane key={person.id} id={person.id} label={person.label} />
3876
4019
  ))}
@@ -3936,7 +4079,7 @@ export default function DragAround() {
3936
4079
  return (
3937
4080
  <Stack gap={3}>
3938
4081
  <Schedule
3939
- ariaLabel="Two people, one on leave"
4082
+ aria-label="Two people, one on leave"
3940
4083
  height={150}
3941
4084
  intents={["move", "lane"]}
3942
4085
  onIntent={onIntent}
@@ -4071,7 +4214,7 @@ export default function TeamLeave() {
4071
4214
  const clashes = findings(WORK, [], BLOCKED).inBlockedTime;
4072
4215
  return (
4073
4216
  <Stack gap={3}>
4074
- <Schedule ariaLabel="Sprint 14 with the team's leave" height={500}>
4217
+ <Schedule aria-label="Sprint 14 with the team's leave" height={500}>
4075
4218
  {PEOPLE.map((person) => (
4076
4219
  <Lane key={person.id} id={person.id} label={person.name} />
4077
4220
  ))}
@@ -4171,7 +4314,7 @@ export default function HandoversAndReleases() {
4171
4314
  const { Schedule, Lane, Subtasks, Milestones } = useSchedule({ initialView: { domain: [day(0, 6), day(5, 0)] } });
4172
4315
  return (
4173
4316
  <Stack gap={3}>
4174
- <Schedule ariaLabel="Portal and billing plan, week of 16 March" height={188}>
4317
+ <Schedule aria-label="Portal and billing plan, week of 16 March" height={188}>
4175
4318
  <Lane id="anna" label="Anna Berg" />
4176
4319
  <Lane id="ben" label="Ben Ortiz" />
4177
4320
  <Lane id="chloe" label="Chloe Lind" />
@@ -4223,7 +4366,7 @@ function Own({ target }: { target: ScheduleTooltipTarget }) {
4223
4366
  export default function SayWhatAMilestoneIs() {
4224
4367
  const { Schedule, Lane, Subtasks, Milestones } = useSchedule({ initialView: { domain: [at(6, 30), at(15, 30)] } });
4225
4368
  return (
4226
- <Schedule ariaLabel="Build and test around the code freeze" height={150} tooltip={(target) => <Own target={target} />}>
4369
+ <Schedule aria-label="Build and test around the code freeze" height={150} tooltip={(target) => <Own target={target} />}>
4227
4370
  <Lane id="arjun" label="Arjun Mehta" />
4228
4371
  <Lane id="eva" label="Eva Novak" />
4229
4372
  <Subtasks data={WORK} tasks={PROJECTS} />
@@ -4422,7 +4565,7 @@ const ROUNDS: Subtask[] = TOURS.map((tour) => ({
4422
4565
  export default function NowLine() {
4423
4566
  const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: [at(5, 30), at(18)] } });
4424
4567
  return (
4425
- <Schedule ariaLabel="Today's tours, with the present" height={420} now={NOW}>
4568
+ <Schedule aria-label="Today's tours, with the present" height={420} now={NOW}>
4426
4569
  {VEHICLES.map((vehicle) => (
4427
4570
  <Lane key={vehicle.id} id={vehicle.id} label={vehicle.plate} />
4428
4571
  ))}
@@ -4462,7 +4605,7 @@ const DUTIES: Subtask[] = [
4462
4605
  export default function FollowTheClock() {
4463
4606
  const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: [current - 6 * HOUR, current + 6 * HOUR] } });
4464
4607
  return (
4465
- <Schedule ariaLabel="Today's on-call rota, with the present" height={188} now>
4608
+ <Schedule aria-label="Today's on-call rota, with the present" height={188} now>
4466
4609
  <Lane id="ada" label="Ada Mwangi" />
4467
4610
  <Lane id="tomasz" label="Tomasz Nowak" />
4468
4611
  <Lane id="leila" label="Leila Haddad" />
@@ -4482,7 +4625,7 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
4482
4625
 
4483
4626
  | Prop | Type | Default | Description |
4484
4627
  |---|---|---|---|
4485
- | [`height`](#first-schedule) | `number` | `400` | Height in CSS pixels, both bands included. The width is the host's. |
4628
+ | [`height`](#first-schedule) | `number` | fills the frame, at least 400 px | Height in CSS pixels, both bands included, fixed. Left out, the schedule fills its frame - a stretched grid cell, a card the grid stretches, a container of definite height - and never goes below 400 px, which is also its height in a frame without one. A different floor goes through `style`'s `minHeight` ([ADR-0050](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0050-a-chart-fills-its-frame-never-below-its-floor.md)). The width is the host's. |
4486
4629
  | [`now`](#first-schedule) | `boolean \| number` | `false` | A line marking the present across the lanes: `true` follows the clock by the minute, an instant fixes it there. |
4487
4630
  | [`children`](#first-schedule) | `ReactNode` | — | `Lane`, `Dependencies` and `Subtasks`, in the order they are to stand and be drawn. |
4488
4631
 
@@ -4490,7 +4633,7 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
4490
4633
 
4491
4634
  | Prop | Type | Default | Description |
4492
4635
  |---|---|---|---|
4493
- | [`ariaLabel`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
4636
+ | [`aria-label`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
4494
4637
 
4495
4638
  From `LaneProps` — the full table stands on [Lanes](#lanes).
4496
4639
 
@@ -4587,7 +4730,7 @@ export default function PanAndZoom() {
4587
4730
  const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: [at(5, 30), at(18)] } });
4588
4731
  return (
4589
4732
  <Schedule
4590
- ariaLabel="A plan to pan and zoom"
4733
+ aria-label="A plan to pan and zoom"
4591
4734
  height={196}
4592
4735
  zoomLimits={{ min: 2 * HOUR, max: 48 * HOUR }}
4593
4736
  >
@@ -4615,7 +4758,7 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
4615
4758
 
4616
4759
  | Prop | Type | Default | Description |
4617
4760
  |---|---|---|---|
4618
- | [`height`](#first-schedule) | `number` | `400` | Height in CSS pixels, both bands included. The width is the host's. |
4761
+ | [`height`](#first-schedule) | `number` | fills the frame, at least 400 px | Height in CSS pixels, both bands included, fixed. Left out, the schedule fills its frame - a stretched grid cell, a card the grid stretches, a container of definite height - and never goes below 400 px, which is also its height in a frame without one. A different floor goes through `style`'s `minHeight` ([ADR-0050](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0050-a-chart-fills-its-frame-never-below-its-floor.md)). The width is the host's. |
4619
4762
  | [`zoomLimits`](#first-schedule) | [`ZoomLimits`](#first-schedule) | one hour to 28 days | The narrowest and widest time span zoom may reach, in milliseconds. |
4620
4763
  | [`children`](#first-schedule) | `ReactNode` | — | `Lane`, `Dependencies` and `Subtasks`, in the order they are to stand and be drawn. |
4621
4764
 
@@ -4623,7 +4766,7 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
4623
4766
 
4624
4767
  | Prop | Type | Default | Description |
4625
4768
  |---|---|---|---|
4626
- | [`ariaLabel`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
4769
+ | [`aria-label`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
4627
4770
 
4628
4771
  From `LaneProps` — the full table stands on [Lanes](#lanes).
4629
4772
 
@@ -4723,7 +4866,7 @@ const TRANSFERS: readonly Dependency[] = [
4723
4866
  export default function SelectATask() {
4724
4867
  const { Schedule, Lane, Subtasks, Dependencies } = useSchedule({ initialView: { domain: [at(5, 30), at(13, 30)] } });
4725
4868
  return (
4726
- <Schedule ariaLabel="Two consignments at North depot" height={140}>
4869
+ <Schedule aria-label="Two consignments at North depot" height={140}>
4727
4870
  <Lane id="truck-118" label="Truck FP 118 R" />
4728
4871
  <Lane id="van-214" label="Van FP 214 K" />
4729
4872
  <Dependencies data={TRANSFERS} />
@@ -4787,7 +4930,7 @@ export default function ControlTheSelection() {
4787
4930
  return (
4788
4931
  <Stack gap={3}>
4789
4932
  <Schedule
4790
- ariaLabel="Legs at North depot on Tuesday, 17 March"
4933
+ aria-label="Legs at North depot on Tuesday, 17 March"
4791
4934
  height={284}
4792
4935
  selectedTask={selected}
4793
4936
  onSelectedTaskChange={(task, subtask) => {
@@ -4832,7 +4975,7 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
4832
4975
 
4833
4976
  | Prop | Type | Default | Description |
4834
4977
  |---|---|---|---|
4835
- | [`height`](#first-schedule) | `number` | `400` | Height in CSS pixels, both bands included. The width is the host's. |
4978
+ | [`height`](#first-schedule) | `number` | fills the frame, at least 400 px | Height in CSS pixels, both bands included, fixed. Left out, the schedule fills its frame - a stretched grid cell, a card the grid stretches, a container of definite height - and never goes below 400 px, which is also its height in a frame without one. A different floor goes through `style`'s `minHeight` ([ADR-0050](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0050-a-chart-fills-its-frame-never-below-its-floor.md)). The width is the host's. |
4836
4979
  | [`selectedTask`](#first-schedule) | `string \| null` | — | The selected task, controlled. Leave it out and the schedule keeps the selection itself. |
4837
4980
  | [`onSelectedTaskChange`](#first-schedule) | `(task: string \| null, subtask: string \| null) => void` | — | Called when a click selects a task or clears the selection, with the subtask that was clicked - null where the click was on a dependency or on nothing. It is called again when another subtask of the same task is clicked. |
4838
4981
  | [`children`](#first-schedule) | `ReactNode` | — | `Lane`, `Dependencies` and `Subtasks`, in the order they are to stand and be drawn. |
@@ -4841,7 +4984,7 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
4841
4984
 
4842
4985
  | Prop | Type | Default | Description |
4843
4986
  |---|---|---|---|
4844
- | [`ariaLabel`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
4987
+ | [`aria-label`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
4845
4988
 
4846
4989
  From `LaneProps` — the full table stands on [Lanes](#lanes).
4847
4990
 
@@ -4908,7 +5051,7 @@ From `ScheduleView` — the full table stands on [View](#view).
4908
5051
 
4909
5052
  - One task at a time: there is no multiple selection.
4910
5053
 
4911
- What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.42.0/docs/adr/0032-what-umriss-is-not.md).
5054
+ What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.44.0/docs/adr/0032-what-umriss-is-not.md).
4912
5055
 
4913
5056
  ### Interactions
4914
5057
 
@@ -4970,7 +5113,7 @@ export default function ReportThePointer() {
4970
5113
  return (
4971
5114
  <Stack gap={3}>
4972
5115
  <Schedule
4973
- ariaLabel="One consignment on a truck and a van"
5116
+ aria-label="One consignment on a truck and a van"
4974
5117
  height={152}
4975
5118
  onInteraction={(interaction) => setLast(describe(interaction))}
4976
5119
  >
@@ -5070,7 +5213,7 @@ export default function OpenAContextMenu() {
5070
5213
  return (
5071
5214
  <>
5072
5215
  <Schedule
5073
- ariaLabel="On-call rota, 16 to 23 March"
5216
+ aria-label="On-call rota, 16 to 23 March"
5074
5217
  height={420}
5075
5218
  onInteraction={onInteraction}
5076
5219
  >
@@ -5085,7 +5228,7 @@ export default function OpenAContextMenu() {
5085
5228
  onOpenChange={(open) => {
5086
5229
  if (!open) setMenu(null);
5087
5230
  }}
5088
- ariaLabel={menu !== null ? `Hand over ${menu.duty.id}` : "Hand over"}
5231
+ aria-label={menu !== null ? `Hand over ${menu.duty.id}` : "Hand over"}
5089
5232
  >
5090
5233
  {menu !== null &&
5091
5234
  ENGINEERS.filter((engineer) => engineer.id !== menu.duty.lane).map((engineer) => (
@@ -5108,7 +5251,7 @@ export default function OpenAContextMenu() {
5108
5251
  #### When to use something else
5109
5252
 
5110
5253
  - You only need to know which task was picked → Selection
5111
- - You need the time at a point outside an interaction → The handle
5254
+ - You need the time at a point outside an interaction → Functions on the hook
5112
5255
 
5113
5256
  #### Keyboard
5114
5257
 
@@ -5122,8 +5265,8 @@ The keys of [First schedule](#first-schedule) apply here.
5122
5265
  |---|---|---|---|
5123
5266
  | `type` *required* | `"click" \| "contextmenu" \| "hover"` | — | `"hover"` is reported when the target under the pointer changes, not on every movement.<br>Shown in: [Report what the pointer is on](#interactions), [Open a context menu](#interactions), [Show a detail panel instead (Tooltip)](#tooltip) and 1 more |
5124
5267
  | `hit` *required* | `ScheduleHit` | — | What the pointer is on.<br>Shown in: [Report what the pointer is on](#interactions), [Open a context menu](#interactions), [Show a detail panel instead (Tooltip)](#tooltip) and 2 more |
5125
- | `clientX` *required* | `number` | — | The pointer's horizontal client coordinate - where a `ContextMenu` opens.<br>Shown in: [Open a context menu](#interactions), [Place a mark of your own (The handle)](#the-handle), [Replan the day on the line (Scenarios)](#replan-the-day-on-the-line) |
5126
- | `clientY` *required* | `number` | — | The pointer's vertical client coordinate.<br>Shown in: [Open a context menu](#interactions), [Place a mark of your own (The handle)](#the-handle), [Replan the day on the line (Scenarios)](#replan-the-day-on-the-line) |
5268
+ | `clientX` *required* | `number` | — | The pointer's horizontal client coordinate - where a `ContextMenu` opens.<br>Shown in: [Open a context menu](#interactions), [Place a mark of your own (Functions on the hook)](#functions-on-the-hook), [Replan the day on the line (Scenarios)](#replan-the-day-on-the-line) |
5269
+ | `clientY` *required* | `number` | — | The pointer's vertical client coordinate.<br>Shown in: [Open a context menu](#interactions), [Place a mark of your own (Functions on the hook)](#functions-on-the-hook), [Replan the day on the line (Scenarios)](#replan-the-day-on-the-line) |
5127
5270
  | `time` *required* | `number` | — | The wall-clock instant under the pointer.<br>Shown in: [Report what the pointer is on](#interactions) |
5128
5271
  | `lane` *required* | `string \| null` | — | The lane under the pointer, or null below the last one.<br>Shown in: [Report what the pointer is on](#interactions) |
5129
5272
 
@@ -5195,7 +5338,7 @@ const TRANSFERS: readonly Dependency[] = [
5195
5338
  export default function DefaultTooltip() {
5196
5339
  const { Schedule, Lane, Subtasks, Dependencies } = useSchedule({ initialView: { domain: [at(5, 30), at(18)] } });
5197
5340
  return (
5198
- <Schedule ariaLabel="Three vehicles with the default tooltip" height={196}>
5341
+ <Schedule aria-label="Three vehicles with the default tooltip" height={196}>
5199
5342
  {VEHICLES.map((vehicle) => (
5200
5343
  <Lane key={vehicle.id} id={vehicle.id} label={vehicle.label} />
5201
5344
  ))}
@@ -5268,7 +5411,7 @@ export default function OwnTooltip() {
5268
5411
  const { Schedule, Lane, Subtasks, Dependencies } = useSchedule({ initialView: { domain: [at(5, 30), at(18)] } });
5269
5412
  return (
5270
5413
  <Schedule
5271
- ariaLabel="Three vehicles with the load in the tooltip"
5414
+ aria-label="Three vehicles with the load in the tooltip"
5272
5415
  height={196}
5273
5416
  tooltip={(target) => <LoadTooltip target={target} />}
5274
5417
  >
@@ -5388,7 +5531,7 @@ export default function DetailPanelInstead() {
5388
5531
  return (
5389
5532
  <Stack gap={3}>
5390
5533
  <Schedule
5391
- ariaLabel="Sprint 14 of the web team"
5534
+ aria-label="Sprint 14 of the web team"
5392
5535
  height={260}
5393
5536
  tooltip={false}
5394
5537
  onInteraction={onInteraction}
@@ -5488,7 +5631,7 @@ function Covers({ target }: { target: ScheduleTooltipTarget }) {
5488
5631
  export default function SayWhatABarCoversAndALineJoins() {
5489
5632
  const { Schedule, Lane, Subtasks, Dependencies, BlockedTimes } = useSchedule({ initialView: { domain: [at(6, 30), at(15, 30)] } });
5490
5633
  return (
5491
- <Schedule ariaLabel="Design, build and test, with leave under the build" height={188} tooltip={(target) => <Covers target={target} />}>
5634
+ <Schedule aria-label="Design, build and test, with leave under the build" height={188} tooltip={(target) => <Covers target={target} />}>
5492
5635
  <Lane id="noah" label="Noah Fischer" />
5493
5636
  <Lane id="arjun" label="Arjun Mehta" />
5494
5637
  <Lane id="eva" label="Eva Novak" />
@@ -5593,7 +5736,7 @@ export default function KeepAndRestore() {
5593
5736
 
5594
5737
  return (
5595
5738
  <Stack gap={3}>
5596
- <Schedule ariaLabel="Two teams on Tuesday, 17 March" height={290}>
5739
+ <Schedule aria-label="Two teams on Tuesday, 17 March" height={290}>
5597
5740
  <LaneGroup id="product" label="Product">
5598
5741
  <Lane id="noah" label="Noah Fischer" />
5599
5742
  <Lane id="arjun" label="Arjun Mehta" />
@@ -5650,7 +5793,7 @@ The keys of [First schedule](#first-schedule) apply here.
5650
5793
 
5651
5794
  | Prop | Type | Default | Description |
5652
5795
  |---|---|---|---|
5653
- | `initialView` | `ScheduleView` | — | The view to start from, read once when the hook first runs; a different value on a later render is ignored, as React ignores a changed `defaultValue`. To go to a view later, call `setView`. The schedule remembers none: where a view is kept is the application's decision.<br>Shown in: [Keep and restore a view](#view), [The smallest schedule (Installation)](#installation), [A day of tours (First schedule)](#first-schedule) and 78 more |
5796
+ | `initialView` | `ScheduleView` | — | The view to start from, read once when the hook first runs; a different value on a later render is ignored, as React ignores a changed `defaultValue`. To go to a view later, call `setView`. The schedule remembers none: where a view is kept is the application's decision.<br>Shown in: [Keep and restore a view](#view), [The smallest schedule (Installation)](#installation), [A day of tours (First schedule)](#first-schedule) and 82 more |
5654
5797
 
5655
5798
  ###### Events
5656
5799
 
@@ -5662,7 +5805,7 @@ The keys of [First schedule](#first-schedule) apply here.
5662
5805
 
5663
5806
  | Prop | Type | Default | Description |
5664
5807
  |---|---|---|---|
5665
- | `domain` | `readonly [number, number]` | — | The span in view, as two wall-clock instants. Absent, the schedule shows the extent of its subtasks, within `zoomLimits`.<br>Shown in: [Keep and restore a view](#view), [The smallest schedule (Installation)](#installation), [A day of tours (First schedule)](#first-schedule) and 77 more |
5808
+ | `domain` | `readonly [number, number]` | — | The span in view, as two wall-clock instants. Absent, the schedule shows the extent of its subtasks, within `zoomLimits`.<br>Shown in: [Keep and restore a view](#view), [The smallest schedule (Installation)](#installation), [A day of tours (First schedule)](#first-schedule) and 81 more |
5666
5809
  | `folded` | `readonly string[]` | — | The **Lane group**s that are folded, by id. An id no group carries falls out. A folded outer group hides the inner ones without their ids leaving the list, so unfolding it gives back the view that was there.<br>Shown in: [Fold groups from outside (Lane groups)](#lane-groups), [Read a folded group (Lane groups)](#lane-groups), [Start with groups folded (Lane groups)](#lane-groups) |
5667
5810
 
5668
5811
  ### Linked schedules
@@ -5735,13 +5878,13 @@ export default function InStep() {
5735
5878
  const span = diesel.view.domain ?? start.domain;
5736
5879
  return (
5737
5880
  <Stack gap={2}>
5738
- <diesel.Schedule ariaLabel="Diesel vehicles on Tuesday, 17 March" height={240}>
5881
+ <diesel.Schedule aria-label="Diesel vehicles on Tuesday, 17 March" height={240}>
5739
5882
  {DIESEL.map((vehicle) => (
5740
5883
  <diesel.Lane key={vehicle.id} id={vehicle.id} label={vehicle.label} />
5741
5884
  ))}
5742
5885
  <diesel.Subtasks data={DIESEL_LEGS} tasks={CONSIGNMENTS} />
5743
5886
  </diesel.Schedule>
5744
- <electric.Schedule ariaLabel="Electric vans, the same hours" height={152}>
5887
+ <electric.Schedule aria-label="Electric vans, the same hours" height={152}>
5745
5888
  {ELECTRIC.map((vehicle) => (
5746
5889
  <electric.Lane key={vehicle.id} id={vehicle.id} label={vehicle.label} />
5747
5890
  ))}
@@ -5894,12 +6037,12 @@ export default function RotaBesideIncidents() {
5894
6037
  const incidents: ScheduleParts = useSchedule({ initialView: start, onViewChange: (v) => rota.setView(v) });
5895
6038
  return (
5896
6039
  <Stack gap={2}>
5897
- <rota.Schedule ariaLabel="Who was on call" height={140}>
6040
+ <rota.Schedule aria-label="Who was on call" height={140}>
5898
6041
  <rota.Lane id="primary" label="Primary" />
5899
6042
  <rota.Lane id="secondary" label="Secondary" />
5900
6043
  <rota.Subtasks data={DUTIES} tasks={PEOPLE} />
5901
6044
  </rota.Schedule>
5902
- <incidents.Schedule ariaLabel="Incidents, the same hours" height={290}>
6045
+ <incidents.Schedule aria-label="Incidents, the same hours" height={290}>
5903
6046
  {HIT.map((service) => (
5904
6047
  <incidents.Lane key={service.id} id={service.id} label={service.name} />
5905
6048
  ))}
@@ -5924,14 +6067,14 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
5924
6067
 
5925
6068
  | Prop | Type | Default | Description |
5926
6069
  |---|---|---|---|
5927
- | [`height`](#first-schedule) | `number` | `400` | Height in CSS pixels, both bands included. The width is the host's. |
6070
+ | [`height`](#first-schedule) | `number` | fills the frame, at least 400 px | Height in CSS pixels, both bands included, fixed. Left out, the schedule fills its frame - a stretched grid cell, a card the grid stretches, a container of definite height - and never goes below 400 px, which is also its height in a frame without one. A different floor goes through `style`'s `minHeight` ([ADR-0050](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0050-a-chart-fills-its-frame-never-below-its-floor.md)). The width is the host's. |
5928
6071
  | [`children`](#first-schedule) | `ReactNode` | — | `Lane`, `Dependencies` and `Subtasks`, in the order they are to stand and be drawn. |
5929
6072
 
5930
6073
  ###### Accessibility
5931
6074
 
5932
6075
  | Prop | Type | Default | Description |
5933
6076
  |---|---|---|---|
5934
- | [`ariaLabel`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
6077
+ | [`aria-label`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
5935
6078
 
5936
6079
  From `LaneProps` — the full table stands on [Lanes](#lanes).
5937
6080
 
@@ -5990,9 +6133,9 @@ From `ScheduleView` — the full table stands on [View](#view).
5990
6133
 
5991
6134
  - Only the view is shared - the span and the folded groups, not the selection, the hover or the vertical scroll.
5992
6135
 
5993
- What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.42.0/docs/adr/0032-what-umriss-is-not.md).
6136
+ What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.44.0/docs/adr/0032-what-umriss-is-not.md).
5994
6137
 
5995
- ### The handle
6138
+ ### Functions on the hook
5996
6139
 
5997
6140
  Converts between a point on the screen and a time on a lane, so marks of your own - a cut-off, a delivery window, a pin - stand at the right place beside the plan.
5998
6141
 
@@ -6000,10 +6143,12 @@ Converts between a point on the screen and a time on a lane, so marks of your ow
6000
6143
  import { useSchedule } from "@umriss-ui/schedule";
6001
6144
  ```
6002
6145
 
6003
- The handle holds three functions and nothing else: `clientPointOf(time, lane?)`, `positionAt(clientX, clientY)` and `visibleDomain()`. Everything else the schedule does is props.
6146
+ [`useSchedule`](#api-index-1) hands back three functions for what is on the screen: `clientPointOf(time, lane?)`, `positionAt(clientX, clientY)` and `visibleDomain()`. Before a schedule is mounted the first two answer `null`. The `ref` of `Schedule` is its root element.
6004
6147
 
6005
6148
  Place your marks again whenever `view.domain` changes.
6006
6149
 
6150
+ `scrollToLane(lane)`, `scrollToSubtask(id)` and `focusSubtask(id)` show a lane or a subtask picked from a list of your own, a findings list or a search. `scrollToSubtask` brings the lane and the time into view and leaves the focus where it is; `focusSubtask` also makes the subtask the active one and focuses the plot. All three answer `false`, and change nothing, for an unknown lane or subtask, a lane inside a folded group (none is unfolded) and a schedule that is not mounted.
6151
+
6007
6152
  #### Examples
6008
6153
 
6009
6154
  ##### Place a mark of your own
@@ -6014,7 +6159,7 @@ Place your marks again whenever `view.domain` changes.
6014
6159
  import { useEffect, useRef, useState } from "react";
6015
6160
  import { Stack, Text } from "@umriss-ui/core";
6016
6161
  import { useSchedule } from "@umriss-ui/schedule";
6017
- import type { ScheduleHandle, Subtask, Task } from "@umriss-ui/schedule";
6162
+ import type { Subtask, Task } from "@umriss-ui/schedule";
6018
6163
 
6019
6164
  const at = (hours: number, minutes = 0) => new Date(2026, 2, 17, hours, minutes).getTime();
6020
6165
  const min = (n: number) => n * 60_000;
@@ -6043,18 +6188,17 @@ const CUT_OFF = at(14);
6043
6188
  const clock = (time: number) => new Date(time).toLocaleTimeString("en-GB", { hour: "2-digit", minute: "2-digit" });
6044
6189
 
6045
6190
  export default function PlaceAMark() {
6046
- const { Schedule, Lane, Subtasks, view } = useSchedule({ initialView: { domain: [at(5, 30), at(18)] } });
6047
- const plan = useRef<ScheduleHandle>(null);
6191
+ const { Schedule, Lane, Subtasks, view, clientPointOf, positionAt } = useSchedule({ initialView: { domain: [at(5, 30), at(18)] } });
6048
6192
  const host = useRef<HTMLDivElement>(null);
6049
6193
  const [pin, setPin] = useState<number | null>(null);
6050
6194
  const [under, setUnder] = useState("Move the pointer over the plan");
6051
6195
 
6052
6196
  /* The client point, turned into one inside this example - anew whenever the span moves. */
6053
6197
  useEffect(() => {
6054
- const point = plan.current?.clientPointOf(CUT_OFF);
6198
+ const point = clientPointOf(CUT_OFF);
6055
6199
  const left = host.current?.getBoundingClientRect().left;
6056
6200
  setPin(point == null || left === undefined ? null : point.x - left);
6057
- }, [view.domain]);
6201
+ }, [view.domain, clientPointOf]);
6058
6202
 
6059
6203
  return (
6060
6204
  <Stack gap={2}>
@@ -6066,13 +6210,12 @@ export default function PlaceAMark() {
6066
6210
  )}
6067
6211
  </div>
6068
6212
  <Schedule
6069
- ref={plan}
6070
- ariaLabel="Three vehicles with a cut-off of the application's own"
6213
+ aria-label="Three vehicles with a cut-off of the application's own"
6071
6214
  height={196}
6072
6215
  onInteraction={(interaction) => {
6073
6216
  /* The interaction carries time and lane already; `positionAt` is for
6074
6217
  listeners of your own, which have only a point. */
6075
- const there = plan.current?.positionAt(interaction.clientX, interaction.clientY);
6218
+ const there = positionAt(interaction.clientX, interaction.clientY);
6076
6219
  setUnder(there == null ? "off the plan" : `${clock(there.time)} on ${there.lane ?? "no lane"}`);
6077
6220
  }}
6078
6221
  >
@@ -6089,6 +6232,69 @@ export default function PlaceAMark() {
6089
6232
  }
6090
6233
  ```
6091
6234
 
6235
+ ##### Show a finding
6236
+
6237
+ `scrollToSubtask` brings a subtask's lane and time into view and leaves the focus; `focusSubtask` also makes it the active subtask. Pick a finding, or press Enter on it.
6238
+
6239
+ ```tsx
6240
+ import { Button, Stack, Text } from "@umriss-ui/core";
6241
+ import { useSchedule } from "@umriss-ui/schedule";
6242
+ import type { Subtask, Task } from "@umriss-ui/schedule";
6243
+
6244
+ const at = (hours: number, minutes = 0) => new Date(2026, 2, 17, hours, minutes).getTime();
6245
+
6246
+ const STATIONS = Array.from({ length: 60 }, (_, i) => ({ id: `station-${i + 1}`, label: `Station ${String(i + 1).padStart(2, "0")}` }));
6247
+
6248
+ const ORDERS: readonly Task[] = [{ id: "order", name: "Order O-7731", color: "light-dark(#2563eb, #6b9bff)" }];
6249
+
6250
+ const STEPS: readonly Subtask[] = STATIONS.map((station, i) => ({
6251
+ id: `step-${i + 1}`,
6252
+ task: "order",
6253
+ lane: station.id,
6254
+ from: at(6 + (i % 10)),
6255
+ to: at(7 + (i % 10)),
6256
+ }));
6257
+
6258
+ const FINDINGS = [
6259
+ { subtask: "step-4", text: "Station 04 starts before its material arrives" },
6260
+ { subtask: "step-37", text: "Station 37 overlaps a shift break" },
6261
+ { subtask: "step-58", text: "Station 58 ends after the cut-off" },
6262
+ ];
6263
+
6264
+ export default function ShowAFinding() {
6265
+ const { Schedule, Lane, Subtasks, scrollToSubtask, focusSubtask } = useSchedule({ initialView: { domain: [at(5, 30), at(10)] } });
6266
+ return (
6267
+ <Stack gap={2}>
6268
+ <Stack gap={1} data-findings>
6269
+ {FINDINGS.map((finding) => (
6270
+ <Button
6271
+ key={finding.subtask}
6272
+ data-finding={finding.subtask}
6273
+ onClick={() => scrollToSubtask(finding.subtask)}
6274
+ onKeyDown={(event) => {
6275
+ if (event.key !== "Enter") return;
6276
+ event.preventDefault();
6277
+ focusSubtask(finding.subtask);
6278
+ }}
6279
+ >
6280
+ {finding.text}
6281
+ </Button>
6282
+ ))}
6283
+ </Stack>
6284
+ <Schedule aria-label="Sixty stations of one order" height={240}>
6285
+ {STATIONS.map((station) => (
6286
+ <Lane key={station.id} id={station.id} label={station.label} />
6287
+ ))}
6288
+ <Subtasks data={STEPS} tasks={ORDERS} />
6289
+ </Schedule>
6290
+ <Text size="sm" tone="muted">
6291
+ A click shows the subtask; Enter also moves the focus to the plot.
6292
+ </Text>
6293
+ </Stack>
6294
+ );
6295
+ }
6296
+ ```
6297
+
6092
6298
  #### When to use something else
6093
6299
 
6094
6300
  - You need the time under the pointer during a hover or a click → Interactions
@@ -6103,8 +6309,7 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
6103
6309
 
6104
6310
  | Prop | Type | Default | Description |
6105
6311
  |---|---|---|---|
6106
- | [`ref`](#first-schedule) | `Ref<`[`ScheduleHandle`](#first-schedule)`>` | — | Receives the `ScheduleHandle` - not an element. |
6107
- | [`height`](#first-schedule) | `number` | `400` | Height in CSS pixels, both bands included. The width is the host's. |
6312
+ | [`height`](#first-schedule) | `number` | fills the frame, at least 400 px | Height in CSS pixels, both bands included, fixed. Left out, the schedule fills its frame - a stretched grid cell, a card the grid stretches, a container of definite height - and never goes below 400 px, which is also its height in a frame without one. A different floor goes through `style`'s `minHeight` ([ADR-0050](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0050-a-chart-fills-its-frame-never-below-its-floor.md)). The width is the host's. |
6108
6313
  | [`children`](#first-schedule) | `ReactNode` | — | `Lane`, `Dependencies` and `Subtasks`, in the order they are to stand and be drawn. |
6109
6314
 
6110
6315
  ###### Events
@@ -6117,7 +6322,7 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
6117
6322
 
6118
6323
  | Prop | Type | Default | Description |
6119
6324
  |---|---|---|---|
6120
- | [`ariaLabel`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
6325
+ | [`aria-label`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
6121
6326
 
6122
6327
  From `LaneProps` — the full table stands on [Lanes](#lanes).
6123
6328
 
@@ -6223,7 +6428,7 @@ export default function ListTheFindings() {
6223
6428
 
6224
6429
  return (
6225
6430
  <Stack gap={3}>
6226
- <Schedule ariaLabel="Three people's week with its findings" height={184}>
6431
+ <Schedule aria-label="Three people's week with its findings" height={184}>
6227
6432
  <Lane id="noah" label="Noah Fischer" />
6228
6433
  <Lane id="chloe" label="Chloe Durand" />
6229
6434
  <Lane id="arjun" label="Arjun Mehta" />
@@ -6278,7 +6483,7 @@ export default function NothingToFix() {
6278
6483
 
6279
6484
  return (
6280
6485
  <Stack gap={3}>
6281
- <Schedule ariaLabel="Two developers' week, without findings" height={140}>
6486
+ <Schedule aria-label="Two developers' week, without findings" height={140}>
6282
6487
  <Lane id="hana" label="Hana Sato" />
6283
6488
  <Lane id="kofi" label="Kofi Mensah" />
6284
6489
  <Subtasks data={WORK} tasks={PROJECTS} />
@@ -6335,7 +6540,7 @@ export default function CountPerKind() {
6335
6540
  <Badge tone={doubleBooked > 0 ? "warning" : "success"}>{doubleBooked} double-booked</Badge>
6336
6541
  <Badge tone={late > 0 ? "warning" : "success"}>{late} transfers too short</Badge>
6337
6542
  </Stack>
6338
- <Schedule ariaLabel="The day's legs with their findings counted" height={196}>
6543
+ <Schedule aria-label="The day's legs with their findings counted" height={196}>
6339
6544
  {VEHICLES.map((vehicle) => (
6340
6545
  <Lane key={vehicle.id} id={vehicle.id} label={vehicle.label} />
6341
6546
  ))}
@@ -6386,7 +6591,7 @@ The keys of [First schedule](#first-schedule) apply here.
6386
6591
 
6387
6592
  - Overlapping work is marked, not packed into sub-lanes; stacked overlap is not built yet.
6388
6593
 
6389
- What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.42.0/docs/adr/0032-what-umriss-is-not.md).
6594
+ What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.44.0/docs/adr/0032-what-umriss-is-not.md).
6390
6595
 
6391
6596
  ## Editing
6392
6597
 
@@ -6431,7 +6636,7 @@ export default function MoveInTime() {
6431
6636
  const [work, setWork] = useState(START);
6432
6637
  return (
6433
6638
  <Schedule
6434
- ariaLabel="Two people's work this week"
6639
+ aria-label="Two people's work this week"
6435
6640
  height={140}
6436
6641
  intents={["move"]}
6437
6642
  onIntent={(intent) => setWork((current) => current.map((item) => applyIntent(item, intent)))}
@@ -6494,7 +6699,7 @@ export default function MoveToAnotherLane() {
6494
6699
  return (
6495
6700
  <Stack gap={3}>
6496
6701
  <Schedule
6497
- ariaLabel="Five vehicles on Tuesday, 17 March, editable"
6702
+ aria-label="Five vehicles on Tuesday, 17 March, editable"
6498
6703
  height={284}
6499
6704
  intents={["move", "lane"]}
6500
6705
  onIntent={(intent) => {
@@ -6634,7 +6839,7 @@ export default function RefuseADoubleBooking() {
6634
6839
  return (
6635
6840
  <Stack gap={3}>
6636
6841
  <Schedule
6637
- ariaLabel="Sprint 14 of the web team, editable"
6842
+ aria-label="Sprint 14 of the web team, editable"
6638
6843
  height={240}
6639
6844
  intents={["move", "lane"]}
6640
6845
  onIntent={onIntent}
@@ -6673,7 +6878,7 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
6673
6878
 
6674
6879
  | Prop | Type | Default | Description |
6675
6880
  |---|---|---|---|
6676
- | [`height`](#first-schedule) | `number` | `400` | Height in CSS pixels, both bands included. The width is the host's. |
6881
+ | [`height`](#first-schedule) | `number` | fills the frame, at least 400 px | Height in CSS pixels, both bands included, fixed. Left out, the schedule fills its frame - a stretched grid cell, a card the grid stretches, a container of definite height - and never goes below 400 px, which is also its height in a frame without one. A different floor goes through `style`'s `minHeight` ([ADR-0050](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0050-a-chart-fills-its-frame-never-below-its-floor.md)). The width is the host's. |
6677
6882
  | [`intents`](#first-schedule) | `readonly `[`IntentKind`](#first-schedule)`[]` | `[]` | The intents the caller handles. Each one enables its interaction; none leaves a read-only schedule ([ADR-0023](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0023-editing-is-controlled-the-schedule-reports-intents.md)). |
6678
6883
  | [`children`](#first-schedule) | `ReactNode` | — | `Lane`, `Dependencies` and `Subtasks`, in the order they are to stand and be drawn. |
6679
6884
 
@@ -6687,7 +6892,7 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
6687
6892
 
6688
6893
  | Prop | Type | Default | Description |
6689
6894
  |---|---|---|---|
6690
- | [`ariaLabel`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
6895
+ | [`aria-label`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
6691
6896
 
6692
6897
  From `LaneProps` — the full table stands on [Lanes](#lanes).
6693
6898
 
@@ -6761,10 +6966,10 @@ From `Overlap` — the full table stands on [Findings as data](#findings-as-data
6761
6966
 
6762
6967
  #### Known limits
6763
6968
 
6764
- - No undo: your application owns the data, and with it the history ([ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.42.0/docs/adr/0032-what-umriss-is-not.md)).
6969
+ - No undo: your application owns the data, and with it the history ([ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.44.0/docs/adr/0032-what-umriss-is-not.md)).
6765
6970
  - The keys move work in time only; a lane change needs the pointer.
6766
6971
 
6767
- What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.42.0/docs/adr/0032-what-umriss-is-not.md).
6972
+ What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.44.0/docs/adr/0032-what-umriss-is-not.md).
6768
6973
 
6769
6974
  ### Stretch, lead-in, lead-out
6770
6975
 
@@ -6803,7 +7008,7 @@ export default function StretchTheMainTime() {
6803
7008
  const [work, setWork] = useState(START);
6804
7009
  return (
6805
7010
  <Schedule
6806
- ariaLabel="Two estimates to adjust"
7011
+ aria-label="Two estimates to adjust"
6807
7012
  height={140}
6808
7013
  intents={["stretch"]}
6809
7014
  onIntent={(intent) => setWork((current) => current.map((item) => applyIntent(item, intent)))}
@@ -6842,7 +7047,7 @@ export default function ChangeLeadInAndLeadOut() {
6842
7047
 
6843
7048
  return (
6844
7049
  <Schedule
6845
- ariaLabel="Two tours from North depot"
7050
+ aria-label="Two tours from North depot"
6846
7051
  height={150}
6847
7052
  intents={["stretch", "leadIn", "leadOut"]}
6848
7053
  onIntent={(intent) => setTours((current) => current.map((tour) => applyIntent(tour, intent)))}
@@ -6869,7 +7074,7 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
6869
7074
 
6870
7075
  | Prop | Type | Default | Description |
6871
7076
  |---|---|---|---|
6872
- | [`height`](#first-schedule) | `number` | `400` | Height in CSS pixels, both bands included. The width is the host's. |
7077
+ | [`height`](#first-schedule) | `number` | fills the frame, at least 400 px | Height in CSS pixels, both bands included, fixed. Left out, the schedule fills its frame - a stretched grid cell, a card the grid stretches, a container of definite height - and never goes below 400 px, which is also its height in a frame without one. A different floor goes through `style`'s `minHeight` ([ADR-0050](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0050-a-chart-fills-its-frame-never-below-its-floor.md)). The width is the host's. |
6873
7078
  | [`intents`](#first-schedule) | `readonly `[`IntentKind`](#first-schedule)`[]` | `[]` | The intents the caller handles. Each one enables its interaction; none leaves a read-only schedule ([ADR-0023](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0023-editing-is-controlled-the-schedule-reports-intents.md)). |
6874
7079
  | [`children`](#first-schedule) | `ReactNode` | — | `Lane`, `Dependencies` and `Subtasks`, in the order they are to stand and be drawn. |
6875
7080
 
@@ -6883,7 +7088,7 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
6883
7088
 
6884
7089
  | Prop | Type | Default | Description |
6885
7090
  |---|---|---|---|
6886
- | [`ariaLabel`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
7091
+ | [`aria-label`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
6887
7092
 
6888
7093
  From `LaneProps` — the full table stands on [Lanes](#lanes).
6889
7094
 
@@ -6936,7 +7141,7 @@ From `ScheduleView` — the full table stands on [View](#view).
6936
7141
 
6937
7142
  - A main time never becomes shorter than one raster step, or a minute without a raster.
6938
7143
 
6939
- What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.42.0/docs/adr/0032-what-umriss-is-not.md).
7144
+ What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.44.0/docs/adr/0032-what-umriss-is-not.md).
6940
7145
 
6941
7146
  ### Snapping
6942
7147
 
@@ -6972,7 +7177,7 @@ export default function SnapToTheTicks() {
6972
7177
  const [tours, setTours] = useState(START);
6973
7178
  return (
6974
7179
  <Schedule
6975
- ariaLabel="A tour on the default raster"
7180
+ aria-label="A tour on the default raster"
6976
7181
  height={100}
6977
7182
  intents={["move"]}
6978
7183
  onIntent={(intent) => setTours((current) => current.map((tour) => applyIntent(tour, intent)))}
@@ -7008,7 +7213,7 @@ export default function SnapToADuration() {
7008
7213
  const [work, setWork] = useState(START);
7009
7214
  return (
7010
7215
  <Schedule
7011
- ariaLabel="Work planned in half days"
7216
+ aria-label="Work planned in half days"
7012
7217
  height={140}
7013
7218
  snap={HALF_DAY}
7014
7219
  intents={["move", "stretch"]}
@@ -7046,7 +7251,7 @@ export default function SnapToHandovers() {
7046
7251
  const [watches, setWatches] = useState(START);
7047
7252
  return (
7048
7253
  <Schedule
7049
- ariaLabel="An on-call watch on the handover raster"
7254
+ aria-label="An on-call watch on the handover raster"
7050
7255
  height={110}
7051
7256
  snap={HANDOVERS}
7052
7257
  intents={["move"]}
@@ -7079,7 +7284,7 @@ export default function NoSnapping() {
7079
7284
  const [windows, setWindows] = useState(START);
7080
7285
  return (
7081
7286
  <Schedule
7082
- ariaLabel="A maintenance window placed freely"
7287
+ aria-label="A maintenance window placed freely"
7083
7288
  height={100}
7084
7289
  snap={false}
7085
7290
  intents={["move"]}
@@ -7102,7 +7307,7 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
7102
7307
 
7103
7308
  | Prop | Type | Default | Description |
7104
7309
  |---|---|---|---|
7105
- | [`height`](#first-schedule) | `number` | `400` | Height in CSS pixels, both bands included. The width is the host's. |
7310
+ | [`height`](#first-schedule) | `number` | fills the frame, at least 400 px | Height in CSS pixels, both bands included, fixed. Left out, the schedule fills its frame - a stretched grid cell, a card the grid stretches, a container of definite height - and never goes below 400 px, which is also its height in a frame without one. A different floor goes through `style`'s `minHeight` ([ADR-0050](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0050-a-chart-fills-its-frame-never-below-its-floor.md)). The width is the host's. |
7106
7311
  | [`snap`](#first-schedule) | `"ticks" \| number \| `[`SnapRaster`](#first-schedule)` \| false` | `"ticks"` | The raster a drag lands on: `"ticks"` - the fine band's current step -, a step in milliseconds from local midnight, a step with an offset (shifts at 06:00, 14:00 and 22:00 are eight hours offset by six), or `false` for none. It shapes the ghost and the intent, never the data. |
7107
7312
  | [`intents`](#first-schedule) | `readonly `[`IntentKind`](#first-schedule)`[]` | `[]` | The intents the caller handles. Each one enables its interaction; none leaves a read-only schedule ([ADR-0023](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0023-editing-is-controlled-the-schedule-reports-intents.md)). |
7108
7313
  | [`children`](#first-schedule) | `ReactNode` | — | `Lane`, `Dependencies` and `Subtasks`, in the order they are to stand and be drawn. |
@@ -7117,7 +7322,7 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
7117
7322
 
7118
7323
  | Prop | Type | Default | Description |
7119
7324
  |---|---|---|---|
7120
- | [`ariaLabel`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
7325
+ | [`aria-label`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
7121
7326
 
7122
7327
  From `LaneProps` — the full table stands on [Lanes](#lanes).
7123
7328
 
@@ -7261,7 +7466,7 @@ export default function DragFromTheBacklog() {
7261
7466
  ))}
7262
7467
  </Stack>
7263
7468
  <Schedule
7264
- ariaLabel="Today's plan of three people, with the backlog beside it"
7469
+ aria-label="Today's plan of three people, with the backlog beside it"
7265
7470
  height={196}
7266
7471
  intents={["place", "move", "lane"]}
7267
7472
  placing={placing}
@@ -7295,7 +7500,7 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
7295
7500
 
7296
7501
  | Prop | Type | Default | Description |
7297
7502
  |---|---|---|---|
7298
- | [`height`](#first-schedule) | `number` | `400` | Height in CSS pixels, both bands included. The width is the host's. |
7503
+ | [`height`](#first-schedule) | `number` | fills the frame, at least 400 px | Height in CSS pixels, both bands included, fixed. Left out, the schedule fills its frame - a stretched grid cell, a card the grid stretches, a container of definite height - and never goes below 400 px, which is also its height in a frame without one. A different floor goes through `style`'s `minHeight` ([ADR-0050](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0050-a-chart-fills-its-frame-never-below-its-floor.md)). The width is the host's. |
7299
7504
  | [`intents`](#first-schedule) | `readonly `[`IntentKind`](#first-schedule)`[]` | `[]` | The intents the caller handles. Each one enables its interaction; none leaves a read-only schedule ([ADR-0023](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0023-editing-is-controlled-the-schedule-reports-intents.md)). |
7300
7505
  | [`placing`](#first-schedule) | [`PlacingItem`](#first-schedule)` \| null` | — | What the application is dragging in from outside while it drags it - its key, task, the length of its main time, its lead-in and lead-out. The browser hands the dragged data over only on the drop, so the ghost before it can only come from here: set it on your own `dragstart`, clear it on `dragend`. Without `"place"` in `intents` no drop is accepted. |
7301
7506
  | [`children`](#first-schedule) | `ReactNode` | — | `Lane`, `Dependencies` and `Subtasks`, in the order they are to stand and be drawn. |
@@ -7310,7 +7515,7 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
7310
7515
 
7311
7516
  | Prop | Type | Default | Description |
7312
7517
  |---|---|---|---|
7313
- | [`ariaLabel`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
7518
+ | [`aria-label`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
7314
7519
 
7315
7520
  From `LaneProps` — the full table stands on [Lanes](#lanes).
7316
7521
 
@@ -7436,7 +7641,7 @@ export default function LanesThatFit() {
7436
7641
  return (
7437
7642
  <Stack gap={3}>
7438
7643
  <Schedule
7439
- ariaLabel="Two cooled vans and a dry van"
7644
+ aria-label="Two cooled vans and a dry van"
7440
7645
  height={190}
7441
7646
  intents={["move", "lane"]}
7442
7647
  canMoveTo={mayGo}
@@ -7522,7 +7727,7 @@ export default function DraggedIn() {
7522
7727
  </div>
7523
7728
  </Card>
7524
7729
  <Schedule
7525
- ariaLabel="A chilled delivery waiting beside two cooled vans and a dry van"
7730
+ aria-label="A chilled delivery waiting beside two cooled vans and a dry van"
7526
7731
  height={190}
7527
7732
  intents={["place"]}
7528
7733
  placing={placing}
@@ -7679,7 +7884,7 @@ export default function AroundLeave() {
7679
7884
  return (
7680
7885
  <Stack gap={3}>
7681
7886
  <Schedule
7682
- ariaLabel="The apps team this week, with leave"
7887
+ aria-label="The apps team this week, with leave"
7683
7888
  height={236}
7684
7889
  intents={["move", "lane"]}
7685
7890
  canMoveTo={mayGo}
@@ -7713,7 +7918,7 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
7713
7918
 
7714
7919
  | Prop | Type | Default | Description |
7715
7920
  |---|---|---|---|
7716
- | [`height`](#first-schedule) | `number` | `400` | Height in CSS pixels, both bands included. The width is the host's. |
7921
+ | [`height`](#first-schedule) | `number` | fills the frame, at least 400 px | Height in CSS pixels, both bands included, fixed. Left out, the schedule fills its frame - a stretched grid cell, a card the grid stretches, a container of definite height - and never goes below 400 px, which is also its height in a frame without one. A different floor goes through `style`'s `minHeight` ([ADR-0050](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0050-a-chart-fills-its-frame-never-below-its-floor.md)). The width is the host's. |
7717
7922
  | [`intents`](#first-schedule) | `readonly `[`IntentKind`](#first-schedule)`[]` | `[]` | The intents the caller handles. Each one enables its interaction; none leaves a read-only schedule ([ADR-0023](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0023-editing-is-controlled-the-schedule-reports-intents.md)). |
7718
7923
  | [`canMoveTo`](#first-schedule) | `(subtask: `[`Subtask`](#subtasks)`, lane: string) => boolean` | every lane is open | Whether a subtask may go to a lane. It narrows `"lane"`, it does not enable it. Asked once per lane when a drag takes hold, and asked again at the drop. The lanes it turns down are marked from the first frame of the drag - a planner sees the refusal before meeting it - and over one of them the ghost stays on the last lane that was allowed, the cursor says no and a line ties the ghost to the pointer. A refused lane costs the lane and nothing else: a drop after it still reports the move in time. It is asked for work dragged in from outside as well, with the key and task the application declared in `placing`. |
7719
7924
  | [`placing`](#first-schedule) | [`PlacingItem`](#first-schedule)` \| null` | — | What the application is dragging in from outside while it drags it - its key, task, the length of its main time, its lead-in and lead-out. The browser hands the dragged data over only on the drop, so the ghost before it can only come from here: set it on your own `dragstart`, clear it on `dragend`. Without `"place"` in `intents` no drop is accepted. |
@@ -7729,7 +7934,7 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
7729
7934
 
7730
7935
  | Prop | Type | Default | Description |
7731
7936
  |---|---|---|---|
7732
- | [`ariaLabel`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
7937
+ | [`aria-label`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
7733
7938
 
7734
7939
  From `LaneProps` — the full table stands on [Lanes](#lanes).
7735
7940
 
@@ -7795,7 +8000,7 @@ From `ScheduleView` — the full table stands on [View](#view).
7795
8000
 
7796
8001
  - A rule that changes while a drag runs is noticed at the drop, not before.
7797
8002
 
7798
- What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.42.0/docs/adr/0032-what-umriss-is-not.md).
8003
+ What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.44.0/docs/adr/0032-what-umriss-is-not.md).
7799
8004
 
7800
8005
  ### Ripple
7801
8006
 
@@ -7852,7 +8057,7 @@ export default function PushTheLaterStops() {
7852
8057
 
7853
8058
  return (
7854
8059
  <Stack gap={3}>
7855
- <Schedule ariaLabel="Tour T-01 on van FP 214 K" height={100}>
8060
+ <Schedule aria-label="Tour T-01 on van FP 214 K" height={100}>
7856
8061
  <Lane id="van-214" label="Van FP 214 K" />
7857
8062
  <Dependencies data={DRIVES} />
7858
8063
  <Subtasks data={stops} tasks={TOURS} />
@@ -7921,7 +8126,7 @@ export default function MoveAWholeTour() {
7921
8126
 
7922
8127
  return (
7923
8128
  <Stack gap={3}>
7924
- <Schedule ariaLabel="Two tours from North depot" height={140}>
8129
+ <Schedule aria-label="Two tours from North depot" height={140}>
7925
8130
  <Lane id="van-214" label="Van FP 214 K" />
7926
8131
  <Lane id="van-377" label="E-van FP 377 K" />
7927
8132
  <Dependencies data={DRIVES} />
@@ -7977,7 +8182,7 @@ export default function RippleOnEveryDrag() {
7977
8182
 
7978
8183
  return (
7979
8184
  <Schedule
7980
- ariaLabel="Design, build and test of the shop search, rippling"
8185
+ aria-label="Design, build and test of the shop search, rippling"
7981
8186
  height={184}
7982
8187
  intents={["move", "stretch"]}
7983
8188
  onIntent={onIntent}
@@ -8002,7 +8207,7 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
8002
8207
 
8003
8208
  | Prop | Type | Default | Description |
8004
8209
  |---|---|---|---|
8005
- | [`height`](#first-schedule) | `number` | `400` | Height in CSS pixels, both bands included. The width is the host's. |
8210
+ | [`height`](#first-schedule) | `number` | fills the frame, at least 400 px | Height in CSS pixels, both bands included, fixed. Left out, the schedule fills its frame - a stretched grid cell, a card the grid stretches, a container of definite height - and never goes below 400 px, which is also its height in a frame without one. A different floor goes through `style`'s `minHeight` ([ADR-0050](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0050-a-chart-fills-its-frame-never-below-its-floor.md)). The width is the host's. |
8006
8211
  | [`intents`](#first-schedule) | `readonly `[`IntentKind`](#first-schedule)`[]` | `[]` | The intents the caller handles. Each one enables its interaction; none leaves a read-only schedule ([ADR-0023](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0023-editing-is-controlled-the-schedule-reports-intents.md)). |
8007
8212
  | [`children`](#first-schedule) | `ReactNode` | — | `Lane`, `Dependencies` and `Subtasks`, in the order they are to stand and be drawn. |
8008
8213
 
@@ -8016,7 +8221,7 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
8016
8221
 
8017
8222
  | Prop | Type | Default | Description |
8018
8223
  |---|---|---|---|
8019
- | [`ariaLabel`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
8224
+ | [`aria-label`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
8020
8225
 
8021
8226
  From `LaneProps` — the full table stands on [Lanes](#lanes).
8022
8227
 
@@ -8080,10 +8285,10 @@ From `ScheduleView` — the full table stands on [View](#view).
8080
8285
 
8081
8286
  #### Known limits
8082
8287
 
8083
- - No dependency types, slack or critical path: the schedule is no project-planning engine ([ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.42.0/docs/adr/0032-what-umriss-is-not.md)).
8288
+ - No dependency types, slack or critical path: the schedule is no project-planning engine ([ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.44.0/docs/adr/0032-what-umriss-is-not.md)).
8084
8289
  - A dependency joins subtasks of one task, so work of other tasks is never pushed.
8085
8290
 
8086
- What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.42.0/docs/adr/0032-what-umriss-is-not.md).
8291
+ What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.44.0/docs/adr/0032-what-umriss-is-not.md).
8087
8292
 
8088
8293
  ## Measuring
8089
8294
 
@@ -8298,7 +8503,7 @@ const RUNS = [
8298
8503
  function Plot({ data, onInteraction }: { data: Plan; onInteraction: (e: ScheduleInteraction) => void }) {
8299
8504
  const { Schedule, Lane, Subtasks, Dependencies } = useSchedule({ initialView: { domain: VIEW } });
8300
8505
  return (
8301
- <Schedule ariaLabel="Benchmark plan" height={HEIGHT} laneHeight={LANE_HEIGHT} onInteraction={onInteraction}>
8506
+ <Schedule aria-label="Benchmark plan" height={HEIGHT} laneHeight={LANE_HEIGHT} onInteraction={onInteraction}>
8302
8507
  {data.lanes.map((id) => (
8303
8508
  <Lane key={id} id={id} />
8304
8509
  ))}
@@ -8415,7 +8620,7 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
8415
8620
 
8416
8621
  | Prop | Type | Default | Description |
8417
8622
  |---|---|---|---|
8418
- | [`height`](#first-schedule) | `number` | `400` | Height in CSS pixels, both bands included. The width is the host's. |
8623
+ | [`height`](#first-schedule) | `number` | fills the frame, at least 400 px | Height in CSS pixels, both bands included, fixed. Left out, the schedule fills its frame - a stretched grid cell, a card the grid stretches, a container of definite height - and never goes below 400 px, which is also its height in a frame without one. A different floor goes through `style`'s `minHeight` ([ADR-0050](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0050-a-chart-fills-its-frame-never-below-its-floor.md)). The width is the host's. |
8419
8624
  | [`laneHeight`](#first-schedule) | `number` | `44` | Height of one lane in pixels at the default font size of 16 px; the schedule multiplies it by the root font size over 16 px when that ratio is above 1, so lane names keep their room in a reader's larger font. |
8420
8625
  | [`children`](#first-schedule) | `ReactNode` | — | `Lane`, `Dependencies` and `Subtasks`, in the order they are to stand and be drawn. |
8421
8626
 
@@ -8429,7 +8634,7 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
8429
8634
 
8430
8635
  | Prop | Type | Default | Description |
8431
8636
  |---|---|---|---|
8432
- | [`ariaLabel`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
8637
+ | [`aria-label`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
8433
8638
 
8434
8639
  From `LaneProps` — the full table stands on [Lanes](#lanes).
8435
8640
 
@@ -8511,13 +8716,14 @@ Everything `@umriss-ui/schedule` exports, with its signature. A component's prop
8511
8716
  Declares a schedule: hands back `Schedule` and the parts declared inside it
8512
8717
  - `Lane`, `LaneGroup`, `Subtasks`, `Dependencies`, `BlockedTimes`, `Milestones` -, its
8513
8718
  `view` and the setters `setView`, `setDomain`, `toggleGroup`, `foldAll` and
8514
- `unfoldAll`. A ref on `Schedule` still gives the `ScheduleHandle`.
8719
+ `unfoldAll`, `positionAt`, `clientPointOf` and `visibleDomain`, and `scrollToLane`, `scrollToSubtask` and
8720
+ `focusSubtask`.
8515
8721
 
8516
8722
  ```ts
8517
8723
  function useSchedule(options?: ScheduleOptions): ScheduleParts;
8518
8724
  ```
8519
8725
 
8520
- Used on [Scenarios](#scenarios), [Installation](#installation), [First schedule](#first-schedule), [Lanes](#lanes), [Lane groups](#lane-groups), [Subtasks](#subtasks), [Bar labels](#bar-labels), [Appearances](#appearances), [Overlap](#overlap), [Dependencies](#dependencies), [Routes](#routes), [Time axis and calendar](#time-axis-and-calendar), [Blocked time](#blocked-time), [Milestones](#milestones), [Now line](#now-line), [Pan and zoom](#pan-and-zoom), [Selection](#selection), [Interactions](#interactions), [Tooltip](#tooltip), [View](#view), [Linked schedules](#linked-schedules), [The handle](#the-handle), [Findings as data](#findings-as-data), [Move and lane](#move-and-lane), [Stretch, lead-in, lead-out](#stretch-lead-in-lead-out), [Snapping](#snapping), [Placing from outside](#placing-from-outside), [Where a subtask may go](#where-a-subtask-may-go), [Ripple](#ripple) and [Benchmark](#benchmark).
8726
+ Used on [Scenarios](#scenarios), [Installation](#installation), [First schedule](#first-schedule), [Lanes](#lanes), [Lane groups](#lane-groups), [Subtasks](#subtasks), [Bar labels](#bar-labels), [Appearances](#appearances), [Overlap](#overlap), [Dependencies](#dependencies), [Routes](#routes), [Time axis and calendar](#time-axis-and-calendar), [Blocked time](#blocked-time), [Milestones](#milestones), [Now line](#now-line), [Pan and zoom](#pan-and-zoom), [Selection](#selection), [Interactions](#interactions), [Tooltip](#tooltip), [View](#view), [Linked schedules](#linked-schedules), [Functions on the hook](#functions-on-the-hook), [Findings as data](#findings-as-data), [Move and lane](#move-and-lane), [Stretch, lead-in, lead-out](#stretch-lead-in-lead-out), [Snapping](#snapping), [Placing from outside](#placing-from-outside), [Where a subtask may go](#where-a-subtask-may-go), [Ripple](#ripple) and [Benchmark](#benchmark).
8521
8727
 
8522
8728
  #### Functions
8523
8729
 
@@ -8560,7 +8766,7 @@ Every finding at once. Without blocked time there is none in it.
8560
8766
  function findings(subtasks: readonly Subtask[], dependencies: readonly Dependency[], blocked?: readonly BlockedTime[]): Findings;
8561
8767
  ```
8562
8768
 
8563
- Used on [Scenarios](#scenarios), [First schedule](#first-schedule), [Lane groups](#lane-groups), [Blocked time](#blocked-time), [Tooltip](#tooltip) and [Findings as data](#findings-as-data).
8769
+ Used on [Scenarios](#scenarios), [First schedule](#first-schedule), [Lane groups](#lane-groups), [Blocked time](#blocked-time), [Tooltip](#tooltip), [Functions on the hook](#functions-on-the-hook) and [Findings as data](#findings-as-data).
8564
8770
 
8565
8771
  ##### `inBlockedTime`
8566
8772
 
@@ -8595,7 +8801,7 @@ nothing.
8595
8801
  function overlaps(subtasks: readonly Subtask[]): Overlap[];
8596
8802
  ```
8597
8803
 
8598
- Used on [Scenarios](#scenarios), [Lane groups](#lane-groups), [Findings as data](#findings-as-data) and [Move and lane](#move-and-lane).
8804
+ Used on [Scenarios](#scenarios), [Lane groups](#lane-groups), [Functions on the hook](#functions-on-the-hook), [Findings as data](#findings-as-data) and [Move and lane](#move-and-lane).
8599
8805
 
8600
8806
  ##### `resolveAppearance`
8601
8807
 
@@ -8883,19 +9089,6 @@ the channel each one owns stands beside it.
8883
9089
  | `muted` *required* | `boolean` | — | `muted`: the SATURATION - the task colour mixed half into the surface, opaque and at full height. Another team's, another crew's. |
8884
9090
  | `open` *required* | `boolean` | — | `open`: the FADE, where the bar passes the edge of the view. It goes on past what is drawn. |
8885
9091
 
8886
- ##### `ScheduleHandle`
8887
-
8888
- What a schedule offers a caller imperatively: the arithmetic between a
8889
- point on the screen and a time on a lane. Everything else is props.
8890
-
8891
- | Prop | Type | Default | Description |
8892
- |---|---|---|---|
8893
- | `positionAt` *required* | `(clientX: number, clientY: number) => { time: number; lane: string \| null } \| null` | — | The time and the lane at a client point, or null outside the plot.<br>Shown in: [Place a mark of your own (The handle)](#the-handle) |
8894
- | `clientPointOf` *required* | `(time: number, lane?: string) => { x: number; y: number } \| null` | — | The client point of a time - on the middle of a lane where one is named - or null while the schedule is not on screen.<br>Shown in: [Place a mark of your own (The handle)](#the-handle) |
8895
- | `visibleDomain` *required* | `() => readonly [number, number]` | — | The visible span, as two wall-clock instants. |
8896
-
8897
- Used on [The handle](#the-handle).
8898
-
8899
9092
  ##### `ScheduleHit`
8900
9093
 
8901
9094
  What a pointer is on.
@@ -8926,10 +9119,10 @@ it, its view and the setters that change it.
8926
9119
 
8927
9120
  | Prop | Type | Default | Description |
8928
9121
  |---|---|---|---|
8929
- | `Schedule` *required* | `(props: `[`ScheduleProps`](#first-schedule)`) => ReactNode` | — | The schedule. One per `useSchedule`: a second schedule is a second call.<br>Shown in: [The smallest schedule (Installation)](#installation), [A day of tours (First schedule)](#first-schedule), [Start from an empty plan (First schedule)](#first-schedule) and 78 more |
8930
- | `Lane` *required* | `typeof Lane` | — | <br>Shown in: [The smallest schedule (Installation)](#installation), [A day of tours (First schedule)](#first-schedule), [Start from an empty plan (First schedule)](#first-schedule) and 78 more |
9122
+ | `Schedule` *required* | `(props: `[`ScheduleProps`](#first-schedule)`) => ReactNode` | — | The schedule. One per `useSchedule`: a second schedule is a second call.<br>Shown in: [The smallest schedule (Installation)](#installation), [A day of tours (First schedule)](#first-schedule), [Start from an empty plan (First schedule)](#first-schedule) and 82 more |
9123
+ | `Lane` *required* | `typeof Lane` | — | <br>Shown in: [The smallest schedule (Installation)](#installation), [A day of tours (First schedule)](#first-schedule), [Start from an empty plan (First schedule)](#first-schedule) and 82 more |
8931
9124
  | `LaneGroup` *required* | `typeof LaneGroup` | — | <br>Shown in: [Group lanes under a head (Lane groups)](#lane-groups), [Nest groups to any depth (Lane groups)](#lane-groups), [Fold groups from outside (Lane groups)](#lane-groups) and 5 more |
8932
- | `Subtasks` *required* | `typeof Subtasks` | — | <br>Shown in: [The smallest schedule (Installation)](#installation), [A day of tours (First schedule)](#first-schedule), [Start from an empty plan (First schedule)](#first-schedule) and 78 more |
9125
+ | `Subtasks` *required* | `typeof Subtasks` | — | <br>Shown in: [The smallest schedule (Installation)](#installation), [A day of tours (First schedule)](#first-schedule), [Start from an empty plan (First schedule)](#first-schedule) and 82 more |
8933
9126
  | `Dependencies` *required* | `typeof Dependencies` | — | <br>Shown in: [The smallest schedule (Installation)](#installation), [A day of tours (First schedule)](#first-schedule), [Plan a day in the machine shop (First schedule)](#first-schedule) and 28 more |
8934
9127
  | `BlockedTimes` *required* | `typeof BlockedTimes` | — | <br>Shown in: [Block out leave and maintenance (Blocked time)](#blocked-time), [Plan work around someone's leave (Blocked time)](#blocked-time), [Check a sprint against the leave calendar (Blocked time)](#blocked-time) and 5 more |
8935
9128
  | `Milestones` *required* | `typeof Milestones` | — | <br>Shown in: [Mark a handover and a release (Milestones)](#milestones), [Say what a milestone is in your own tooltip (Milestones)](#milestones) |
@@ -8937,6 +9130,12 @@ it, its view and the setters that change it.
8937
9130
  | `setView` *required* | `(view: `[`ScheduleView`](#view)`) => void` | — | Goes to a whole view; what it leaves out is shown whole again. Not reported through `onViewChange`: two schedules kept in step by `onViewChange: (v) => other.setView(v)` in both directions do not answer each other. A view with the content already shown changes nothing.<br>Shown in: [Keep and restore a view (View)](#view), [Keep two schedules in step (Linked schedules)](#linked-schedules), [Read a rota beside the incidents (Linked schedules)](#linked-schedules) |
8938
9131
  | `setDomain` *required* | `(span: readonly [number, number] \| null) => void` | — | Puts a span of two wall-clock instants in view; `null` shows the subtasks' extent.<br>Shown in: [Keep and restore a view (View)](#view) |
8939
9132
  | `toggleGroup` *required* | `(id: string) => void` | — | Folds or unfolds one **Lane group**. |
9133
+ | `positionAt` *required* | `(clientX: number, clientY: number) => { time: number; lane: string \| null } \| null` | — | The time and the lane at a client point, or null outside the plot - and null while no schedule is mounted.<br>Shown in: [Place a mark of your own (Functions on the hook)](#functions-on-the-hook) |
9134
+ | `clientPointOf` *required* | `(time: number, lane?: string) => { x: number; y: number } \| null` | — | The client point of a time - on the middle of a lane where one is named - or null while the schedule is not on screen.<br>Shown in: [Place a mark of your own (Functions on the hook)](#functions-on-the-hook) |
9135
+ | `visibleDomain` *required* | `() => readonly [number, number]` | — | The visible span, as two wall-clock instants. |
9136
+ | `scrollToLane` *required* | `(lane: string) => boolean` | — | Scrolls the lanes until the lane's row is in view, and returns `true`; `false` and nothing changed for an unknown lane, one inside a folded **Lane group** (none is unfolded), or while no schedule is mounted. |
9137
+ | `scrollToSubtask` *required* | `(id: string) => boolean` | — | Brings the subtask's lane and time into view, the span moving to it and keeping its width as `setDomain` would, and leaves the focus where it is; `false` and nothing changed in the cases of `scrollToLane`.<br>Shown in: [Show a finding (Functions on the hook)](#functions-on-the-hook) |
9138
+ | `focusSubtask` *required* | `(id: string) => boolean` | — | Does what `scrollToSubtask` does, then makes the subtask the **Active subtask** and moves the focus to the plot, where the live region reads it out as the arrow keys do; `false` and nothing changed in the cases of `scrollToLane`.<br>Shown in: [Show a finding (Functions on the hook)](#functions-on-the-hook) |
8940
9139
  | `foldAll` *required* | `() => void` | — | Folds every group.<br>Shown in: [Fold groups from outside (Lane groups)](#lane-groups) |
8941
9140
  | `unfoldAll` *required* | `() => void` | — | Unfolds every group.<br>Shown in: [Fold groups from outside (Lane groups)](#lane-groups) |
8942
9141
 
@@ -8984,7 +9183,7 @@ Stretch the main time at one or both of its edges.
8984
9183
 
8985
9184
  Its table stands on [Subtasks](#subtasks).
8986
9185
 
8987
- Used on [Scenarios](#scenarios), [Installation](#installation), [First schedule](#first-schedule), [Lanes](#lanes), [Lane groups](#lane-groups), [Subtasks](#subtasks), [Bar labels](#bar-labels), [Appearances](#appearances), [Overlap](#overlap), [Dependencies](#dependencies), [Routes](#routes), [Time axis and calendar](#time-axis-and-calendar), [Blocked time](#blocked-time), [Milestones](#milestones), [Now line](#now-line), [Pan and zoom](#pan-and-zoom), [Selection](#selection), [Interactions](#interactions), [Tooltip](#tooltip), [View](#view), [Linked schedules](#linked-schedules), [The handle](#the-handle), [Findings as data](#findings-as-data), [Move and lane](#move-and-lane), [Stretch, lead-in, lead-out](#stretch-lead-in-lead-out), [Snapping](#snapping), [Placing from outside](#placing-from-outside), [Where a subtask may go](#where-a-subtask-may-go), [Ripple](#ripple) and [Benchmark](#benchmark).
9186
+ Used on [Scenarios](#scenarios), [Installation](#installation), [First schedule](#first-schedule), [Lanes](#lanes), [Lane groups](#lane-groups), [Subtasks](#subtasks), [Bar labels](#bar-labels), [Appearances](#appearances), [Overlap](#overlap), [Dependencies](#dependencies), [Routes](#routes), [Time axis and calendar](#time-axis-and-calendar), [Blocked time](#blocked-time), [Milestones](#milestones), [Now line](#now-line), [Pan and zoom](#pan-and-zoom), [Selection](#selection), [Interactions](#interactions), [Tooltip](#tooltip), [View](#view), [Linked schedules](#linked-schedules), [Functions on the hook](#functions-on-the-hook), [Findings as data](#findings-as-data), [Move and lane](#move-and-lane), [Stretch, lead-in, lead-out](#stretch-lead-in-lead-out), [Snapping](#snapping), [Placing from outside](#placing-from-outside), [Where a subtask may go](#where-a-subtask-may-go), [Ripple](#ripple) and [Benchmark](#benchmark).
8988
9187
 
8989
9188
  ##### `SubtaskAppearance`
8990
9189
 
@@ -9002,7 +9201,7 @@ Its table stands on [Subtasks](#subtasks).
9002
9201
 
9003
9202
  Its table stands on [Subtasks](#subtasks).
9004
9203
 
9005
- Used on [Scenarios](#scenarios), [Installation](#installation), [First schedule](#first-schedule), [Lanes](#lanes), [Lane groups](#lane-groups), [Subtasks](#subtasks), [Bar labels](#bar-labels), [Appearances](#appearances), [Overlap](#overlap), [Dependencies](#dependencies), [Routes](#routes), [Time axis and calendar](#time-axis-and-calendar), [Blocked time](#blocked-time), [Milestones](#milestones), [Now line](#now-line), [Pan and zoom](#pan-and-zoom), [Selection](#selection), [Interactions](#interactions), [Tooltip](#tooltip), [View](#view), [Linked schedules](#linked-schedules), [The handle](#the-handle), [Findings as data](#findings-as-data), [Move and lane](#move-and-lane), [Stretch, lead-in, lead-out](#stretch-lead-in-lead-out), [Snapping](#snapping), [Placing from outside](#placing-from-outside), [Where a subtask may go](#where-a-subtask-may-go), [Ripple](#ripple) and [Benchmark](#benchmark).
9204
+ Used on [Scenarios](#scenarios), [Installation](#installation), [First schedule](#first-schedule), [Lanes](#lanes), [Lane groups](#lane-groups), [Subtasks](#subtasks), [Bar labels](#bar-labels), [Appearances](#appearances), [Overlap](#overlap), [Dependencies](#dependencies), [Routes](#routes), [Time axis and calendar](#time-axis-and-calendar), [Blocked time](#blocked-time), [Milestones](#milestones), [Now line](#now-line), [Pan and zoom](#pan-and-zoom), [Selection](#selection), [Interactions](#interactions), [Tooltip](#tooltip), [View](#view), [Linked schedules](#linked-schedules), [Functions on the hook](#functions-on-the-hook), [Findings as data](#findings-as-data), [Move and lane](#move-and-lane), [Stretch, lead-in, lead-out](#stretch-lead-in-lead-out), [Snapping](#snapping), [Placing from outside](#placing-from-outside), [Where a subtask may go](#where-a-subtask-may-go), [Ripple](#ripple) and [Benchmark](#benchmark).
9006
9205
 
9007
9206
  ##### `ViolatedDependency`
9008
9207