@umriss-ui/schedule 0.41.1 → 0.43.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.41.1
1
+ # @umriss-ui/schedule 0.43.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,15 +964,9 @@ 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
- ###### Accessibility
971
-
972
- | Prop | Type | Default | Description |
973
- |---|---|---|---|
974
- | [`ariaLabel`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
975
-
976
970
  From `LaneProps` — the full table stands on [Lanes](#lanes).
977
971
 
978
972
  | Prop | Type | Default | Description |
@@ -1101,7 +1095,7 @@ const HANDOVERS: readonly Dependency[] = [
1101
1095
  export default function FirstSchedule() {
1102
1096
  const { Schedule, Lane, Subtasks, Dependencies } = useSchedule({ initialView: { domain: DAY } });
1103
1097
  return (
1104
- <Schedule ariaLabel="Tours of Tuesday, 17 March" height={240}>
1098
+ <Schedule aria-label="Tours of Tuesday, 17 March" height={240}>
1105
1099
  {VEHICLES.map((vehicle) => (
1106
1100
  <Lane key={vehicle.id} id={vehicle.id} label={vehicle.label} />
1107
1101
  ))}
@@ -1134,7 +1128,7 @@ const PEOPLE = [
1134
1128
  export default function NoWorkYet() {
1135
1129
  const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: [at(23, 8), at(27, 18)] } });
1136
1130
  return (
1137
- <Schedule ariaLabel="Sprint 15, nothing planned yet" height={170}>
1131
+ <Schedule aria-label="Sprint 15, nothing planned yet" height={170}>
1138
1132
  {PEOPLE.map((person) => (
1139
1133
  <Lane key={person.id} id={person.id} label={person.label} />
1140
1134
  ))}
@@ -1229,7 +1223,7 @@ export default function MachinePlan() {
1229
1223
  const { Schedule, Lane, Subtasks, Dependencies } = useSchedule({ initialView: { domain: DAY_OF_PLAN } });
1230
1224
  return (
1231
1225
  <Schedule
1232
- ariaLabel="Machine shop, Tuesday 17 March"
1226
+ aria-label="Machine shop, Tuesday 17 March"
1233
1227
  height={380}
1234
1228
  now={HALF_PAST_TEN}
1235
1229
  label={(step) => NAMES.get(step.task) ?? step.task}
@@ -1244,6 +1238,164 @@ export default function MachinePlan() {
1244
1238
  }
1245
1239
  ```
1246
1240
 
1241
+ ##### Fill a pane of fixed height
1242
+
1243
+ 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.
1244
+
1245
+ ```tsx
1246
+ import { useSchedule } from "@umriss-ui/schedule";
1247
+ import type { Subtask, Task } from "@umriss-ui/schedule";
1248
+
1249
+ const at = (hours: number) => new Date(2026, 2, 17, hours).getTime();
1250
+
1251
+ const ORDERS: Task[] = [{ id: "a-2041", name: "A-2041 Housing", color: "light-dark(#2563eb, #6b9bff)" }];
1252
+
1253
+ const STEPS: Subtask[] = [
1254
+ { id: "a-2041-1", task: "a-2041", lane: "saw", from: at(6), to: at(7) },
1255
+ { id: "a-2041-2", task: "a-2041", lane: "mill", from: at(8), to: at(10) },
1256
+ { id: "a-2041-3", task: "a-2041", lane: "qa", from: at(11), to: at(12) },
1257
+ ];
1258
+
1259
+ export default function FillsItsPane() {
1260
+ const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: [at(5), at(13)] } });
1261
+ return (
1262
+ <div data-pane="" style={{ height: 480, resize: "vertical", overflow: "hidden" }}>
1263
+ <Schedule aria-label="Plan of Tuesday in a pane">
1264
+ <Lane id="saw" label="Saw" />
1265
+ <Lane id="mill" label="Mill" />
1266
+ <Lane id="qa" label="Inspection" />
1267
+ <Subtasks data={STEPS} tasks={ORDERS} />
1268
+ </Schedule>
1269
+ </div>
1270
+ );
1271
+ }
1272
+ ```
1273
+
1274
+ ##### Thirty lanes, a floor of 400 px
1275
+
1276
+ 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.
1277
+
1278
+ ```tsx
1279
+ import { useSchedule } from "@umriss-ui/schedule";
1280
+ import type { Subtask, Task } from "@umriss-ui/schedule";
1281
+
1282
+ const at = (hours: number) => new Date(2026, 2, 17, hours).getTime();
1283
+
1284
+ const ORDERS: Task[] = [{ id: "round", name: "Round", color: "light-dark(#0d9488, #3cc7b8)" }];
1285
+
1286
+ const STATIONS = Array.from({ length: 30 }, (_, index) => ({ id: `s-${index + 1}`, label: `Station ${index + 1}` }));
1287
+
1288
+ const STEPS: Subtask[] = STATIONS.map((station, index) => ({
1289
+ id: `round-${index + 1}`,
1290
+ task: "round",
1291
+ lane: station.id,
1292
+ from: at(6 + (index % 6)),
1293
+ to: at(7 + (index % 6)),
1294
+ }));
1295
+
1296
+ export default function ThirtyLanes() {
1297
+ const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: [at(5), at(14)] } });
1298
+ return (
1299
+ <Schedule aria-label="Round of thirty stations">
1300
+ {STATIONS.map((station) => (
1301
+ <Lane key={station.id} id={station.id} label={station.label} />
1302
+ ))}
1303
+ <Subtasks data={STEPS} tasks={ORDERS} />
1304
+ </Schedule>
1305
+ );
1306
+ }
1307
+ ```
1308
+
1309
+ ##### Show that a plan is on its way
1310
+
1311
+ `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.
1312
+
1313
+ ```tsx
1314
+ import { useEffect, useRef, useState } from "react";
1315
+ import { Button, Text } from "@umriss-ui/core";
1316
+ import { useSchedule } from "@umriss-ui/schedule";
1317
+ import type { Subtask, Task } from "@umriss-ui/schedule";
1318
+
1319
+ const at = (hours: number, minutes = 0) => new Date(2026, 2, 17, hours, minutes).getTime();
1320
+ const DAY: readonly [number, number] = [at(6), at(16)];
1321
+
1322
+ const TOURS: readonly Task[] = [
1323
+ { id: "t-01", name: "T-01 Harbour", color: "light-dark(#2563eb, #6b9bff)" },
1324
+ { id: "t-03", name: "T-03 Old Town", color: "light-dark(#c2410c, #f08a52)" },
1325
+ ];
1326
+
1327
+ /** Each answer shifts the second tour by a quarter of an hour. */
1328
+ function legs(answer: number): readonly Subtask[] {
1329
+ const shift = (answer % 4) * 15;
1330
+ return [
1331
+ { id: "t-01-1", task: "t-01", lane: "van-1", from: at(7), to: at(9) },
1332
+ { id: "t-03-1", task: "t-03", lane: "van-2", from: at(8, shift), to: at(10, shift) },
1333
+ { id: "t-01-2", task: "t-01", lane: "e-van", from: at(11), to: at(13) },
1334
+ ];
1335
+ }
1336
+
1337
+ const LANES = [
1338
+ { id: "van-1", label: "Van FP 214 K" },
1339
+ { id: "van-2", label: "Van FP 402 R" },
1340
+ { id: "e-van", label: "E-van FP 377 K" },
1341
+ ];
1342
+
1343
+ function Plan({ answer, loading }: { answer: number; loading: boolean }) {
1344
+ const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: DAY } });
1345
+ return (
1346
+ <Schedule aria-label="Tours of the day" height={220} loading={loading} intents={["move", "lane"]}>
1347
+ {LANES.map((lane) => (
1348
+ <Lane key={lane.id} id={lane.id} label={lane.label} />
1349
+ ))}
1350
+ <Subtasks data={legs(answer)} tasks={TOURS} />
1351
+ </Schedule>
1352
+ );
1353
+ }
1354
+
1355
+ function Waiting() {
1356
+ const { Schedule } = useSchedule({ initialView: { domain: DAY } });
1357
+ return <Schedule aria-label="Tours of the day, loading" height={220} loading />;
1358
+ }
1359
+
1360
+ export default function Loading() {
1361
+ const [answer, setAnswer] = useState(0);
1362
+ const [loading, setLoading] = useState(false);
1363
+ const pending = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
1364
+ useEffect(() => () => clearTimeout(pending.current), []);
1365
+
1366
+ const reload = () => {
1367
+ setLoading(true);
1368
+ clearTimeout(pending.current);
1369
+ pending.current = setTimeout(() => {
1370
+ setAnswer((n) => n + 1);
1371
+ setLoading(false);
1372
+ }, 1500);
1373
+ };
1374
+
1375
+ return (
1376
+ <div className="pair">
1377
+ <div>
1378
+ <Text as="p" size="sm" tone="muted">
1379
+ loading over a plan - the answer takes 1.5 s
1380
+ </Text>
1381
+ <Plan answer={answer} loading={loading} />
1382
+ <div className="demo-actions">
1383
+ <Button size="sm" onClick={reload} disabled={loading}>
1384
+ Reload
1385
+ </Button>
1386
+ </div>
1387
+ </div>
1388
+ <div>
1389
+ <Text as="p" size="sm" tone="muted">
1390
+ loading, before the first lanes
1391
+ </Text>
1392
+ <Waiting />
1393
+ </div>
1394
+ </div>
1395
+ );
1396
+ }
1397
+ ```
1398
+
1247
1399
  #### Keyboard
1248
1400
 
1249
1401
  | Key | Action |
@@ -1262,7 +1414,7 @@ export default function MachinePlan() {
1262
1414
 
1263
1415
  #### Accessibility
1264
1416
 
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".
1417
+ 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
1418
 
1267
1419
  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
1420
 
@@ -1274,14 +1426,14 @@ Under forced colours the plot paints itself in the system colours: the work in t
1274
1426
 
1275
1427
  | Prop | Type | Default | Description |
1276
1428
  |---|---|---|---|
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 |
1429
+ | `ref` | `Ref<HTMLDivElement> \| RefObject<``ScheduleHandle`` \| null> \| undefined` | — | Receives the root element. A ref to the deprecated `ScheduleHandle` still works for one more minor release: the element carries its three functions. |
1430
+ | `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
1431
  | `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
1432
  | `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
1433
  | `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
1434
  | `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
1435
  | `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 |
1436
+ | `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
1437
  | `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
1438
  | `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
1439
  | `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 +1444,8 @@ Under forced colours the plot paints itself in the system colours: the work in t
1292
1444
  | `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
1445
  | `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
1446
  | `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 |
1447
+ | `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) |
1448
+ | `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
1449
 
1297
1450
  ###### Events
1298
1451
 
@@ -1301,18 +1454,7 @@ Under forced colours the plot paints itself in the system colours: the work in t
1301
1454
  | `onIntent` | `(intent: ``Intent``) => void` | — | Called once per intent when a drag ends. The data changes only if the caller changes it.<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 |
1302
1455
  | `onInteraction` | `(interaction: `[`ScheduleInteraction`](#interactions)`) => void` | — | Click, context menu and hover, with what the pointer is on and where. While it is set, a right-click opens no browser menu.<br>Shown in: [Report what the pointer is on (Interactions)](#interactions), [Open a context menu (Interactions)](#interactions), [Show a detail panel instead (Tooltip)](#tooltip) and 3 more |
1303
1456
 
1304
- ###### Accessibility
1305
-
1306
- | Prop | Type | Default | Description |
1307
- |---|---|---|---|
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
1311
-
1312
- | Prop | Type | Default | Description |
1313
- |---|---|---|---|
1314
- | `className` | `string` | — | Goes to the root element. |
1315
- | `style` | `CSSProperties` | — | Goes to the root element. |
1457
+ Also takes every attribute of `<div>` – without `aria-label`, `children`.
1316
1458
 
1317
1459
  ##### Types on this page
1318
1460
 
@@ -1323,8 +1465,8 @@ point on the screen and a time on a lane. Everything else is props.
1323
1465
 
1324
1466
  | Prop | Type | Default | Description |
1325
1467
  |---|---|---|---|
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) |
1468
+ | `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. |
1469
+ | `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. |
1328
1470
  | `visibleDomain` *required* | `() => readonly [number, number]` | — | The visible span, as two wall-clock instants. |
1329
1471
 
1330
1472
  ###### `CalendarInput`
@@ -1523,11 +1665,11 @@ no subtask yet; the caller creates one, with its own identity, from
1523
1665
 
1524
1666
  #### Known limits
1525
1667
 
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.41.1/docs/adr/0032-what-umriss-is-not.md)).
1668
+ - 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.43.0/docs/adr/0032-what-umriss-is-not.md)).
1527
1669
  - No milestones yet: a point in time on a lane has no mark of its own.
1528
1670
  - A dependency joins two subtasks of one task; dependencies across tasks are not yet possible.
1529
1671
 
1530
- What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.41.1/docs/adr/0032-what-umriss-is-not.md).
1672
+ What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.43.0/docs/adr/0032-what-umriss-is-not.md).
1531
1673
 
1532
1674
  ## Plan
1533
1675
 
@@ -1568,7 +1710,7 @@ const LEGS: Subtask[] = [
1568
1710
  export default function LaneHeaders() {
1569
1711
  const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: [at(6), at(12)] } });
1570
1712
  return (
1571
- <Schedule ariaLabel="Two vans of the North depot" height={150} headerWidth={210}>
1713
+ <Schedule aria-label="Two vans of the North depot" height={150} headerWidth={210}>
1572
1714
  <Lane
1573
1715
  id="v1"
1574
1716
  label={
@@ -1632,7 +1774,7 @@ export default function ManyLanes() {
1632
1774
  const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: [at(6), at(14)] } });
1633
1775
  return (
1634
1776
  <Schedule
1635
- ariaLabel="Twenty vans, Tuesday morning"
1777
+ aria-label="Twenty vans, Tuesday morning"
1636
1778
  height={300}
1637
1779
  laneHeight={32}
1638
1780
  headerWidth={110}
@@ -1660,8 +1802,8 @@ The keys of [First schedule](#first-schedule) apply here.
1660
1802
 
1661
1803
  | Prop | Type | Default | Description |
1662
1804
  |---|---|---|---|
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 |
1805
+ | `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 |
1806
+ | `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
1807
 
1666
1808
  ### Lane groups
1667
1809
 
@@ -1708,7 +1850,7 @@ const WORK: Subtask[] = [
1708
1850
  export default function AGroup() {
1709
1851
  const { Schedule, Lane, LaneGroup, Subtasks } = useSchedule({ initialView: { domain: [at(6), at(15)] } });
1710
1852
  return (
1711
- <Schedule ariaLabel="Two developers in a team, a designer and a tester outside it" height={240} headerWidth={200}>
1853
+ <Schedule aria-label="Two developers in a team, a designer and a tester outside it" height={240} headerWidth={200}>
1712
1854
  <LaneGroup id="developers" label="Developers">
1713
1855
  <Lane id="arjun" label="Arjun Mehta" />
1714
1856
  <Lane id="chloe" label="Chloe Durand" />
@@ -1751,7 +1893,7 @@ const LEGS: Subtask[] = [
1751
1893
  export default function Nesting() {
1752
1894
  const { Schedule, Lane, LaneGroup, Subtasks } = useSchedule({ initialView: { domain: [at(6), at(15)] } });
1753
1895
  return (
1754
- <Schedule ariaLabel="North depot with its vans and a truck, and a van from Riverside" height={330} headerWidth={200}>
1896
+ <Schedule aria-label="North depot with its vans and a truck, and a van from Riverside" height={330} headerWidth={200}>
1755
1897
  <LaneGroup id="north" label="North depot">
1756
1898
  <LaneGroup id="vans" label="Vans">
1757
1899
  <Lane id="fp-214" label={`Van ${plate("FP 214 K")}`} />
@@ -1804,7 +1946,7 @@ export default function FoldFromOutside() {
1804
1946
  Unfold everything
1805
1947
  </Button>
1806
1948
  </Stack>
1807
- <Schedule ariaLabel="Two teams rolling out a release, folded from outside" height={280}>
1949
+ <Schedule aria-label="Two teams rolling out a release, folded from outside" height={280}>
1808
1950
  <LaneGroup id="payments" label="Payments">
1809
1951
  <Lane id="priya" label="Priya Raman" />
1810
1952
  <Lane id="jonas" label="Jonas Keller" />
@@ -1876,7 +2018,7 @@ export default function TheMiniature() {
1876
2018
  return (
1877
2019
  <Stack gap={3}>
1878
2020
  <Schedule
1879
- ariaLabel="A depot that folds into one row"
2021
+ aria-label="A depot that folds into one row"
1880
2022
  height={240}
1881
2023
  intents={["move", "lane"]}
1882
2024
  onIntent={(intent) => {
@@ -1931,7 +2073,7 @@ const WORK: Subtask[] = [
1931
2073
  export default function StartWithGroupsFolded() {
1932
2074
  const { Schedule, Lane, LaneGroup, Subtasks } = useSchedule({ initialView: { domain: [at(6), at(15)], folded: ["discovery"] } });
1933
2075
  return (
1934
- <Schedule ariaLabel="Two teams rolling out a release, Discovery folded at the start" height={280}>
2076
+ <Schedule aria-label="Two teams rolling out a release, Discovery folded at the start" height={280}>
1935
2077
  <LaneGroup id="payments" label="Payments">
1936
2078
  <Lane id="priya" label="Priya Raman" />
1937
2079
  <Lane id="jonas" label="Jonas Keller" />
@@ -1969,7 +2111,7 @@ The keys of [First schedule](#first-schedule) apply here.
1969
2111
 
1970
2112
  - 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
2113
 
1972
- What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.41.1/docs/adr/0032-what-umriss-is-not.md).
2114
+ What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.43.0/docs/adr/0032-what-umriss-is-not.md).
1973
2115
 
1974
2116
  ### Subtasks
1975
2117
 
@@ -2005,7 +2147,7 @@ const ROUNDS: Subtask[] = [
2005
2147
  export default function LeadInAndLeadOut() {
2006
2148
  const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: [at(6), at(14, 30)] } });
2007
2149
  return (
2008
- <Schedule ariaLabel="Three rounds of one van" height={110}>
2150
+ <Schedule aria-label="Three rounds of one van" height={110}>
2009
2151
  <Lane id="van" label="Van FP 214 K" />
2010
2152
  <Subtasks data={ROUNDS} tasks={TOURS} />
2011
2153
  </Schedule>
@@ -2043,7 +2185,7 @@ const WORK: Subtask[] = [
2043
2185
  export default function TaskColours() {
2044
2186
  const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: [at(16, 6), at(20, 20)] } });
2045
2187
  return (
2046
- <Schedule ariaLabel="Projects due this week among the rest" height={150}>
2188
+ <Schedule aria-label="Projects due this week among the rest" height={150}>
2047
2189
  <Lane id="chloe" label="Chloe Durand" />
2048
2190
  <Lane id="eva" label="Eva Novak" />
2049
2191
  <Subtasks data={WORK} tasks={PROJECTS} />
@@ -2149,7 +2291,7 @@ const SPRINT_14: readonly [number, number] = [new Date(2026, 2, 9, 6).getTime(),
2149
2291
  export default function ASprint() {
2150
2292
  const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: SPRINT_14 } });
2151
2293
  return (
2152
- <Schedule ariaLabel="Sprint 14, 9 to 20 March" height={500} label={(s) => s.name ?? s.id}>
2294
+ <Schedule aria-label="Sprint 14, 9 to 20 March" height={500} label={(s) => s.name ?? s.id}>
2153
2295
  {PEOPLE.map((person) => (
2154
2296
  <Lane key={person.id} id={person.id} label={person.name} />
2155
2297
  ))}
@@ -2169,18 +2311,18 @@ The keys of [First schedule](#first-schedule) apply here.
2169
2311
 
2170
2312
  | Prop | Type | Default | Description |
2171
2313
  |---|---|---|---|
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 |
2314
+ | `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 |
2315
+ | `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
2316
 
2175
2317
  ##### `Subtask`
2176
2318
 
2177
2319
  | Prop | Type | Default | Description |
2178
2320
  |---|---|---|---|
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 |
2321
+ | `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 |
2322
+ | `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 |
2323
+ | `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 |
2324
+ | `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 |
2325
+ | `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
2326
  | `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
2327
  | `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
2328
  | `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 +2333,9 @@ The keys of [First schedule](#first-schedule) apply here.
2191
2333
 
2192
2334
  | Prop | Type | Default | Description |
2193
2335
  |---|---|---|---|
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 |
2336
+ | `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 |
2337
+ | `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 |
2338
+ | `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
2339
 
2198
2340
  ##### Types on this page
2199
2341
 
@@ -2256,7 +2398,7 @@ export default function BarLabels() {
2256
2398
  const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: DAY } });
2257
2399
  return (
2258
2400
  <Schedule
2259
- ariaLabel="Tuesday, 17 March, with the work items written in"
2401
+ aria-label="Tuesday, 17 March, with the work items written in"
2260
2402
  height={196}
2261
2403
  label={(subtask) => subtask.name ?? subtask.id}
2262
2404
  >
@@ -2279,16 +2421,10 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
2279
2421
 
2280
2422
  | Prop | Type | Default | Description |
2281
2423
  |---|---|---|---|
2282
- | [`height`](#first-schedule) | `number` | `400` | Height in CSS pixels, both bands included. The width is the host's. |
2424
+ | [`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
2425
  | [`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
2426
  | [`children`](#first-schedule) | `ReactNode` | — | `Lane`, `Dependencies` and `Subtasks`, in the order they are to stand and be drawn. |
2285
2427
 
2286
- ###### Accessibility
2287
-
2288
- | Prop | Type | Default | Description |
2289
- |---|---|---|---|
2290
- | [`ariaLabel`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
2291
-
2292
2428
  From `LaneProps` — the full table stands on [Lanes](#lanes).
2293
2429
 
2294
2430
  | Prop | Type | Default | Description |
@@ -2371,7 +2507,7 @@ const WORK: Subtask[] = [
2371
2507
  export default function Provisional() {
2372
2508
  const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: [at(6, 30), at(11)] } });
2373
2509
  return (
2374
- <Schedule ariaLabel="Agreed work above, provisional work below" height={144}>
2510
+ <Schedule aria-label="Agreed work above, provisional work below" height={144}>
2375
2511
  <Lane id="released" label="Agreed, with lead-in" />
2376
2512
  <Lane id="draft" label="Provisional" />
2377
2513
  <Subtasks data={WORK} tasks={PROJECTS} />
@@ -2401,7 +2537,7 @@ export default function Fixed() {
2401
2537
  const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: [at(6, 30), at(11)] } });
2402
2538
  return (
2403
2539
  <Schedule
2404
- ariaLabel="A round that may move above, a booked slot below"
2540
+ aria-label="A round that may move above, a booked slot below"
2405
2541
  height={144}
2406
2542
  label={(leg) => (leg.id === "nailed" ? "T-05 · booked slot" : "T-05")}
2407
2543
  >
@@ -2435,7 +2571,7 @@ export default function Muted() {
2435
2571
  const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: [at(6, 30), at(11)] } });
2436
2572
  return (
2437
2573
  <Schedule
2438
- ariaLabel="Our team's work above, another team's below"
2574
+ aria-label="Our team's work above, another team's below"
2439
2575
  height={144}
2440
2576
  label={() => "Checkout migration"}
2441
2577
  >
@@ -2468,7 +2604,7 @@ const WORK: Subtask[] = [
2468
2604
  export default function Progress() {
2469
2605
  const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: [at(6, 30), at(11)] } });
2470
2606
  return (
2471
- <Schedule ariaLabel="Work claiming nothing above, work at 65 per cent below" height={144}>
2607
+ <Schedule aria-label="Work claiming nothing above, work at 65 per cent below" height={144}>
2472
2608
  <Lane id="unclaimed" label="Says nothing" />
2473
2609
  <Lane id="started" label="65 per cent done" />
2474
2610
  <Subtasks data={WORK} tasks={PROJECTS} />
@@ -2501,7 +2637,7 @@ const WORK: Subtask[] = [
2501
2637
  export default function Open() {
2502
2638
  const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: [at(6, 30), at(11)] } });
2503
2639
  return (
2504
- <Schedule ariaLabel="Work running past the view at either edge" height={188}>
2640
+ <Schedule aria-label="Work running past the view at either edge" height={188}>
2505
2641
  <Lane id="runs-on" label="Runs on" />
2506
2642
  <Lane id="whole" label="Wholly in view" />
2507
2643
  <Lane id="began" label="Began before" />
@@ -2567,7 +2703,7 @@ export default function Combinations() {
2567
2703
  const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: [at(6, 30), at(11)] } });
2568
2704
  return (
2569
2705
  <Schedule
2570
- ariaLabel="Bars carrying several appearances at once"
2706
+ aria-label="Bars carrying several appearances at once"
2571
2707
  height={230}
2572
2708
  label={(leg) => NAMES[leg.id] ?? leg.id}
2573
2709
  >
@@ -2590,16 +2726,10 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
2590
2726
 
2591
2727
  | Prop | Type | Default | Description |
2592
2728
  |---|---|---|---|
2593
- | [`height`](#first-schedule) | `number` | `400` | Height in CSS pixels, both bands included. The width is the host's. |
2729
+ | [`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
2730
  | [`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
2731
  | [`children`](#first-schedule) | `ReactNode` | — | `Lane`, `Dependencies` and `Subtasks`, in the order they are to stand and be drawn. |
2596
2732
 
2597
- ###### Accessibility
2598
-
2599
- | Prop | Type | Default | Description |
2600
- |---|---|---|---|
2601
- | [`ariaLabel`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
2602
-
2603
2733
  From `LaneProps` — the full table stands on [Lanes](#lanes).
2604
2734
 
2605
2735
  | Prop | Type | Default | Description |
@@ -2684,7 +2814,7 @@ const WORK: Subtask[] = [
2684
2814
  export default function DoubleBooking() {
2685
2815
  const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: [at(8), at(16)] } });
2686
2816
  return (
2687
- <Schedule ariaLabel="Kofi booked on two projects at once" height={150}>
2817
+ <Schedule aria-label="Kofi booked on two projects at once" height={150}>
2688
2818
  <Lane id="kofi" label="Kofi Mensah" />
2689
2819
  <Lane id="hana" label="Hana Sato" />
2690
2820
  <Subtasks data={WORK} tasks={PROJECTS} />
@@ -2717,7 +2847,7 @@ const LEGS: Subtask[] = [
2717
2847
  export default function LeadOutMeetsLeadIn() {
2718
2848
  const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: [at(6, 30), at(11, 30)] } });
2719
2849
  return (
2720
- <Schedule ariaLabel="Two rounds of one van, touching at unloading and loading" height={110}>
2850
+ <Schedule aria-label="Two rounds of one van, touching at unloading and loading" height={110}>
2721
2851
  <Lane id="fp-402" label="Van FP 402 R" />
2722
2852
  <Subtasks data={LEGS} tasks={TOURS} />
2723
2853
  </Schedule>
@@ -2751,7 +2881,7 @@ const WORK: Subtask[] = [
2751
2881
  export default function ThreeAtOnce() {
2752
2882
  const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: [at(7), at(14)] } });
2753
2883
  return (
2754
- <Schedule ariaLabel="Priya on three incidents at once" height={150}>
2884
+ <Schedule aria-label="Priya on three incidents at once" height={150}>
2755
2885
  <Lane id="priya" label="Priya Raman" />
2756
2886
  <Lane id="jonas" label="Jonas Keller" />
2757
2887
  <Subtasks data={WORK} tasks={INCIDENTS} />
@@ -2774,15 +2904,9 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
2774
2904
 
2775
2905
  | Prop | Type | Default | Description |
2776
2906
  |---|---|---|---|
2777
- | [`height`](#first-schedule) | `number` | `400` | Height in CSS pixels, both bands included. The width is the host's. |
2907
+ | [`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
2908
  | [`children`](#first-schedule) | `ReactNode` | — | `Lane`, `Dependencies` and `Subtasks`, in the order they are to stand and be drawn. |
2779
2909
 
2780
- ###### Accessibility
2781
-
2782
- | Prop | Type | Default | Description |
2783
- |---|---|---|---|
2784
- | [`ariaLabel`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
2785
-
2786
2910
  From `LaneProps` — the full table stands on [Lanes](#lanes).
2787
2911
 
2788
2912
  | Prop | Type | Default | Description |
@@ -2833,7 +2957,7 @@ From `ScheduleView` — the full table stands on [View](#view).
2833
2957
 
2834
2958
  - No stacked layout on request yet: an overlap is always drawn offset, as the finding it is.
2835
2959
 
2836
- What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.41.1/docs/adr/0032-what-umriss-is-not.md).
2960
+ What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.43.0/docs/adr/0032-what-umriss-is-not.md).
2837
2961
 
2838
2962
  ### Dependencies
2839
2963
 
@@ -2882,7 +3006,7 @@ const DRIVES: Dependency[] = [
2882
3006
  export default function StopsOfATour() {
2883
3007
  const { Schedule, Lane, Subtasks, Dependencies } = useSchedule({ initialView: { domain: [at(7), at(10)] } });
2884
3008
  return (
2885
- <Schedule ariaLabel="Two morning tours with their stops" height={150}>
3009
+ <Schedule aria-label="Two morning tours with their stops" height={150}>
2886
3010
  <Lane id="fp-214" label="Van FP 214 K" />
2887
3011
  <Lane id="fp-377" label="E-van FP 377 K" />
2888
3012
  <Dependencies data={DRIVES} />
@@ -2926,7 +3050,7 @@ const HANDOVERS: Dependency[] = [
2926
3050
  export default function Anchors() {
2927
3051
  const { Schedule, Lane, Subtasks, Dependencies } = useSchedule({ initialView: { domain: [at(6, 30), at(16, 30)] } });
2928
3052
  return (
2929
- <Schedule ariaLabel="Two handovers with different anchors" height={150}>
3053
+ <Schedule aria-label="Two handovers with different anchors" height={150}>
2930
3054
  <Lane id="truck" label="Truck FP 520 E" />
2931
3055
  <Lane id="van" label="Van FP 290 E" />
2932
3056
  <Dependencies data={HANDOVERS} />
@@ -2974,7 +3098,7 @@ function Variant({ attach }: { attach: DependencyAttachment }) {
2974
3098
  <Text size="xs" tone="muted" mono>
2975
3099
  attach=&quot;{attach}&quot;
2976
3100
  </Text>
2977
- <Schedule ariaLabel={`A release through three engineers, lines from the ${attach}`} height={188} attach={attach}>
3101
+ <Schedule aria-label={`A release through three engineers, lines from the ${attach}`} height={188} attach={attach}>
2978
3102
  <Lane id="priya" label="Priya Raman" />
2979
3103
  <Lane id="ada" label="Ada Mwangi" />
2980
3104
  <Lane id="sam" label="Sam Okafor" />
@@ -3030,7 +3154,7 @@ function Variant({ ends }: { ends: DependencyEnds }) {
3030
3154
  <Text size="xs" tone="muted" mono>
3031
3155
  ends=&quot;{ends}&quot;
3032
3156
  </Text>
3033
- <Schedule ariaLabel={`Design, build and test, ends ${ends}`} height={188} attach="nearest" ends={ends}>
3157
+ <Schedule aria-label={`Design, build and test, ends ${ends}`} height={188} attach="nearest" ends={ends}>
3034
3158
  <Lane id="noah" label="Noah Fischer" />
3035
3159
  <Lane id="arjun" label="Arjun Mehta" />
3036
3160
  <Lane id="eva" label="Eva Novak" />
@@ -3077,7 +3201,7 @@ const HANDOVERS: Dependency[] = [{ id: "transfer", from: "line-haul", to: "round
3077
3201
  export default function ViolatedDependency() {
3078
3202
  const { Schedule, Lane, Subtasks, Dependencies } = useSchedule({ initialView: { domain: [at(6, 30), at(11)] } });
3079
3203
  return (
3080
- <Schedule ariaLabel="A handover from the truck to the van that is too short" height={150}>
3204
+ <Schedule aria-label="A handover from the truck to the van that is too short" height={150}>
3081
3205
  <Lane id="truck" label="Truck FP 118 R" />
3082
3206
  <Lane id="van" label="Van FP 214 K" />
3083
3207
  <Dependencies data={HANDOVERS} />
@@ -3120,7 +3244,7 @@ const HANDOVERS: Dependency[] = [
3120
3244
  export default function OneHandoverDrawnItsOwnWay() {
3121
3245
  const { Schedule, Lane, Subtasks, Dependencies } = useSchedule({ initialView: { domain: [at(6, 30), at(12, 30)] } });
3122
3246
  return (
3123
- <Schedule ariaLabel="Design, build, test and a sign-off drawn its own way" height={188} attach="nearest" ends="none">
3247
+ <Schedule aria-label="Design, build, test and a sign-off drawn its own way" height={188} attach="nearest" ends="none">
3124
3248
  <Lane id="noah" label="Noah Fischer" />
3125
3249
  <Lane id="arjun" label="Arjun Mehta" />
3126
3250
  <Lane id="eva" label="Eva Novak" />
@@ -3203,9 +3327,9 @@ type DependencyEnds = "dot" | "none";
3203
3327
 
3204
3328
  #### Known limits
3205
3329
 
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.41.1/docs/adr/0032-what-umriss-is-not.md)).
3330
+ - 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.43.0/docs/adr/0032-what-umriss-is-not.md)).
3207
3331
 
3208
- What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.41.1/docs/adr/0032-what-umriss-is-not.md).
3332
+ What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.43.0/docs/adr/0032-what-umriss-is-not.md).
3209
3333
 
3210
3334
  ### Routes
3211
3335
 
@@ -3246,7 +3370,7 @@ const HANDOVERS: Dependency[] = [
3246
3370
  export default function Curve() {
3247
3371
  const { Schedule, Lane, Subtasks, Dependencies } = useSchedule({ initialView: { domain: [at(6, 30), at(13, 30)] } });
3248
3372
  return (
3249
- <Schedule ariaLabel="Search results, from layout to polish, curved lines" height={188}>
3373
+ <Schedule aria-label="Search results, from layout to polish, curved lines" height={188}>
3250
3374
  <Lane id="noah" label="Noah Fischer" />
3251
3375
  <Lane id="chloe" label="Chloe Durand" />
3252
3376
  <Lane id="eva" label="Eva Novak" />
@@ -3286,7 +3410,7 @@ const HANDOVERS: Dependency[] = [
3286
3410
  export default function Straight() {
3287
3411
  const { Schedule, Lane, Subtasks, Dependencies } = useSchedule({ initialView: { domain: [at(6, 30), at(13, 30)] } });
3288
3412
  return (
3289
- <Schedule ariaLabel="Search results, from layout to polish, straight lines" height={188} route="straight" attach="nearest">
3413
+ <Schedule aria-label="Search results, from layout to polish, straight lines" height={188} route="straight" attach="nearest">
3290
3414
  <Lane id="noah" label="Noah Fischer" />
3291
3415
  <Lane id="chloe" label="Chloe Durand" />
3292
3416
  <Lane id="eva" label="Eva Novak" />
@@ -3326,7 +3450,7 @@ const HANDOVERS: Dependency[] = [
3326
3450
  export default function Orthogonal() {
3327
3451
  const { Schedule, Lane, Subtasks, Dependencies } = useSchedule({ initialView: { domain: [at(6, 30), at(13, 30)] } });
3328
3452
  return (
3329
- <Schedule ariaLabel="Search results, from layout to polish, orthogonal lines" height={188} route="orthogonal" attach="nearest">
3453
+ <Schedule aria-label="Search results, from layout to polish, orthogonal lines" height={188} route="orthogonal" attach="nearest">
3330
3454
  <Lane id="noah" label="Noah Fischer" />
3331
3455
  <Lane id="chloe" label="Chloe Durand" />
3332
3456
  <Lane id="eva" label="Eva Novak" />
@@ -3367,7 +3491,7 @@ const HANDOVERS: Dependency[] = [
3367
3491
  export default function PerDependency() {
3368
3492
  const { Schedule, Lane, Subtasks, Dependencies } = useSchedule({ initialView: { domain: [at(6, 30), at(13, 30)] } });
3369
3493
  return (
3370
- <Schedule ariaLabel="Search results, from layout to polish, straight lines but one" height={188} route="straight" attach="nearest">
3494
+ <Schedule aria-label="Search results, from layout to polish, straight lines but one" height={188} route="straight" attach="nearest">
3371
3495
  <Lane id="noah" label="Noah Fischer" />
3372
3496
  <Lane id="chloe" label="Chloe Durand" />
3373
3497
  <Lane id="eva" label="Eva Novak" />
@@ -3388,17 +3512,11 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
3388
3512
 
3389
3513
  | Prop | Type | Default | Description |
3390
3514
  |---|---|---|---|
3391
- | [`height`](#first-schedule) | `number` | `400` | Height in CSS pixels, both bands included. The width is the host's. |
3515
+ | [`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
3516
  | [`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
3517
  | [`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
3518
  | [`children`](#first-schedule) | `ReactNode` | — | `Lane`, `Dependencies` and `Subtasks`, in the order they are to stand and be drawn. |
3395
3519
 
3396
- ###### Accessibility
3397
-
3398
- | Prop | Type | Default | Description |
3399
- |---|---|---|---|
3400
- | [`ariaLabel`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
3401
-
3402
3520
  From `LaneProps` — the full table stands on [Lanes](#lanes).
3403
3521
 
3404
3522
  | Prop | Type | Default | Description |
@@ -3538,7 +3656,7 @@ const WEEK: readonly [number, number] = [new Date(2026, 2, 16).getTime(), new Da
3538
3656
  export default function AWeek() {
3539
3657
  const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: WEEK } });
3540
3658
  return (
3541
- <Schedule ariaLabel="On-call rota, 16 to 23 March" height={420}>
3659
+ <Schedule aria-label="On-call rota, 16 to 23 March" height={420}>
3542
3660
  {ENGINEERS.map((engineer) => (
3543
3661
  <Lane key={engineer.id} id={engineer.id} label={engineer.name} />
3544
3662
  ))}
@@ -3582,7 +3700,7 @@ export default function WorkingCalendar() {
3582
3700
  const [legs, setLegs] = useState<readonly Subtask[]>(LEGS);
3583
3701
  return (
3584
3702
  <Schedule
3585
- ariaLabel="Three days at East Gate depot, nights removed"
3703
+ aria-label="Three days at East Gate depot, nights removed"
3586
3704
  calendar={OPENING_HOURS}
3587
3705
  height={220}
3588
3706
  intents={["move"]}
@@ -3693,7 +3811,7 @@ export default function OfficeHours() {
3693
3811
  const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: [at(9, 9), at(20, 17)] } });
3694
3812
  return (
3695
3813
  <Schedule
3696
- ariaLabel="Sprint 14 of the web team, office hours only"
3814
+ aria-label="Sprint 14 of the web team, office hours only"
3697
3815
  calendar={OFFICE_HOURS}
3698
3816
  height={280}
3699
3817
  label={(item) => item.name ?? item.id}
@@ -3721,7 +3839,7 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
3721
3839
 
3722
3840
  | Prop | Type | Default | Description |
3723
3841
  |---|---|---|---|
3724
- | [`height`](#first-schedule) | `number` | `400` | Height in CSS pixels, both bands included. The width is the host's. |
3842
+ | [`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
3843
  | [`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
3844
  | [`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
3845
  | [`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. |
@@ -3733,12 +3851,6 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
3733
3851
  |---|---|---|---|
3734
3852
  | [`onIntent`](#first-schedule) | `(intent: `[`Intent`](#first-schedule)`) => void` | — | Called once per intent when a drag ends. The data changes only if the caller changes it. |
3735
3853
 
3736
- ###### Accessibility
3737
-
3738
- | Prop | Type | Default | Description |
3739
- |---|---|---|---|
3740
- | [`ariaLabel`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
3741
-
3742
3854
  From `LaneProps` — the full table stands on [Lanes](#lanes).
3743
3855
 
3744
3856
  | Prop | Type | Default | Description |
@@ -3870,7 +3982,7 @@ export default function Leave() {
3870
3982
 
3871
3983
  return (
3872
3984
  <Stack gap={3}>
3873
- <Schedule ariaLabel="Team plan, week of 16 March" height={240}>
3985
+ <Schedule aria-label="Team plan, week of 16 March" height={240}>
3874
3986
  {PEOPLE.map((person) => (
3875
3987
  <Lane key={person.id} id={person.id} label={person.label} />
3876
3988
  ))}
@@ -3936,7 +4048,7 @@ export default function DragAround() {
3936
4048
  return (
3937
4049
  <Stack gap={3}>
3938
4050
  <Schedule
3939
- ariaLabel="Two people, one on leave"
4051
+ aria-label="Two people, one on leave"
3940
4052
  height={150}
3941
4053
  intents={["move", "lane"]}
3942
4054
  onIntent={onIntent}
@@ -4071,7 +4183,7 @@ export default function TeamLeave() {
4071
4183
  const clashes = findings(WORK, [], BLOCKED).inBlockedTime;
4072
4184
  return (
4073
4185
  <Stack gap={3}>
4074
- <Schedule ariaLabel="Sprint 14 with the team's leave" height={500}>
4186
+ <Schedule aria-label="Sprint 14 with the team's leave" height={500}>
4075
4187
  {PEOPLE.map((person) => (
4076
4188
  <Lane key={person.id} id={person.id} label={person.name} />
4077
4189
  ))}
@@ -4171,7 +4283,7 @@ export default function HandoversAndReleases() {
4171
4283
  const { Schedule, Lane, Subtasks, Milestones } = useSchedule({ initialView: { domain: [day(0, 6), day(5, 0)] } });
4172
4284
  return (
4173
4285
  <Stack gap={3}>
4174
- <Schedule ariaLabel="Portal and billing plan, week of 16 March" height={188}>
4286
+ <Schedule aria-label="Portal and billing plan, week of 16 March" height={188}>
4175
4287
  <Lane id="anna" label="Anna Berg" />
4176
4288
  <Lane id="ben" label="Ben Ortiz" />
4177
4289
  <Lane id="chloe" label="Chloe Lind" />
@@ -4223,7 +4335,7 @@ function Own({ target }: { target: ScheduleTooltipTarget }) {
4223
4335
  export default function SayWhatAMilestoneIs() {
4224
4336
  const { Schedule, Lane, Subtasks, Milestones } = useSchedule({ initialView: { domain: [at(6, 30), at(15, 30)] } });
4225
4337
  return (
4226
- <Schedule ariaLabel="Build and test around the code freeze" height={150} tooltip={(target) => <Own target={target} />}>
4338
+ <Schedule aria-label="Build and test around the code freeze" height={150} tooltip={(target) => <Own target={target} />}>
4227
4339
  <Lane id="arjun" label="Arjun Mehta" />
4228
4340
  <Lane id="eva" label="Eva Novak" />
4229
4341
  <Subtasks data={WORK} tasks={PROJECTS} />
@@ -4422,7 +4534,7 @@ const ROUNDS: Subtask[] = TOURS.map((tour) => ({
4422
4534
  export default function NowLine() {
4423
4535
  const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: [at(5, 30), at(18)] } });
4424
4536
  return (
4425
- <Schedule ariaLabel="Today's tours, with the present" height={420} now={NOW}>
4537
+ <Schedule aria-label="Today's tours, with the present" height={420} now={NOW}>
4426
4538
  {VEHICLES.map((vehicle) => (
4427
4539
  <Lane key={vehicle.id} id={vehicle.id} label={vehicle.plate} />
4428
4540
  ))}
@@ -4462,7 +4574,7 @@ const DUTIES: Subtask[] = [
4462
4574
  export default function FollowTheClock() {
4463
4575
  const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: [current - 6 * HOUR, current + 6 * HOUR] } });
4464
4576
  return (
4465
- <Schedule ariaLabel="Today's on-call rota, with the present" height={188} now>
4577
+ <Schedule aria-label="Today's on-call rota, with the present" height={188} now>
4466
4578
  <Lane id="ada" label="Ada Mwangi" />
4467
4579
  <Lane id="tomasz" label="Tomasz Nowak" />
4468
4580
  <Lane id="leila" label="Leila Haddad" />
@@ -4482,16 +4594,10 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
4482
4594
 
4483
4595
  | Prop | Type | Default | Description |
4484
4596
  |---|---|---|---|
4485
- | [`height`](#first-schedule) | `number` | `400` | Height in CSS pixels, both bands included. The width is the host's. |
4597
+ | [`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
4598
  | [`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
4599
  | [`children`](#first-schedule) | `ReactNode` | — | `Lane`, `Dependencies` and `Subtasks`, in the order they are to stand and be drawn. |
4488
4600
 
4489
- ###### Accessibility
4490
-
4491
- | Prop | Type | Default | Description |
4492
- |---|---|---|---|
4493
- | [`ariaLabel`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
4494
-
4495
4601
  From `LaneProps` — the full table stands on [Lanes](#lanes).
4496
4602
 
4497
4603
  | Prop | Type | Default | Description |
@@ -4587,7 +4693,7 @@ export default function PanAndZoom() {
4587
4693
  const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: [at(5, 30), at(18)] } });
4588
4694
  return (
4589
4695
  <Schedule
4590
- ariaLabel="A plan to pan and zoom"
4696
+ aria-label="A plan to pan and zoom"
4591
4697
  height={196}
4592
4698
  zoomLimits={{ min: 2 * HOUR, max: 48 * HOUR }}
4593
4699
  >
@@ -4615,16 +4721,10 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
4615
4721
 
4616
4722
  | Prop | Type | Default | Description |
4617
4723
  |---|---|---|---|
4618
- | [`height`](#first-schedule) | `number` | `400` | Height in CSS pixels, both bands included. The width is the host's. |
4724
+ | [`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
4725
  | [`zoomLimits`](#first-schedule) | [`ZoomLimits`](#first-schedule) | one hour to 28 days | The narrowest and widest time span zoom may reach, in milliseconds. |
4620
4726
  | [`children`](#first-schedule) | `ReactNode` | — | `Lane`, `Dependencies` and `Subtasks`, in the order they are to stand and be drawn. |
4621
4727
 
4622
- ###### Accessibility
4623
-
4624
- | Prop | Type | Default | Description |
4625
- |---|---|---|---|
4626
- | [`ariaLabel`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
4627
-
4628
4728
  From `LaneProps` — the full table stands on [Lanes](#lanes).
4629
4729
 
4630
4730
  | Prop | Type | Default | Description |
@@ -4723,7 +4823,7 @@ const TRANSFERS: readonly Dependency[] = [
4723
4823
  export default function SelectATask() {
4724
4824
  const { Schedule, Lane, Subtasks, Dependencies } = useSchedule({ initialView: { domain: [at(5, 30), at(13, 30)] } });
4725
4825
  return (
4726
- <Schedule ariaLabel="Two consignments at North depot" height={140}>
4826
+ <Schedule aria-label="Two consignments at North depot" height={140}>
4727
4827
  <Lane id="truck-118" label="Truck FP 118 R" />
4728
4828
  <Lane id="van-214" label="Van FP 214 K" />
4729
4829
  <Dependencies data={TRANSFERS} />
@@ -4787,7 +4887,7 @@ export default function ControlTheSelection() {
4787
4887
  return (
4788
4888
  <Stack gap={3}>
4789
4889
  <Schedule
4790
- ariaLabel="Legs at North depot on Tuesday, 17 March"
4890
+ aria-label="Legs at North depot on Tuesday, 17 March"
4791
4891
  height={284}
4792
4892
  selectedTask={selected}
4793
4893
  onSelectedTaskChange={(task, subtask) => {
@@ -4832,17 +4932,11 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
4832
4932
 
4833
4933
  | Prop | Type | Default | Description |
4834
4934
  |---|---|---|---|
4835
- | [`height`](#first-schedule) | `number` | `400` | Height in CSS pixels, both bands included. The width is the host's. |
4935
+ | [`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
4936
  | [`selectedTask`](#first-schedule) | `string \| null` | — | The selected task, controlled. Leave it out and the schedule keeps the selection itself. |
4837
4937
  | [`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
4938
  | [`children`](#first-schedule) | `ReactNode` | — | `Lane`, `Dependencies` and `Subtasks`, in the order they are to stand and be drawn. |
4839
4939
 
4840
- ###### Accessibility
4841
-
4842
- | Prop | Type | Default | Description |
4843
- |---|---|---|---|
4844
- | [`ariaLabel`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
4845
-
4846
4940
  From `LaneProps` — the full table stands on [Lanes](#lanes).
4847
4941
 
4848
4942
  | Prop | Type | Default | Description |
@@ -4908,7 +5002,7 @@ From `ScheduleView` — the full table stands on [View](#view).
4908
5002
 
4909
5003
  - One task at a time: there is no multiple selection.
4910
5004
 
4911
- What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.41.1/docs/adr/0032-what-umriss-is-not.md).
5005
+ What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.43.0/docs/adr/0032-what-umriss-is-not.md).
4912
5006
 
4913
5007
  ### Interactions
4914
5008
 
@@ -4970,7 +5064,7 @@ export default function ReportThePointer() {
4970
5064
  return (
4971
5065
  <Stack gap={3}>
4972
5066
  <Schedule
4973
- ariaLabel="One consignment on a truck and a van"
5067
+ aria-label="One consignment on a truck and a van"
4974
5068
  height={152}
4975
5069
  onInteraction={(interaction) => setLast(describe(interaction))}
4976
5070
  >
@@ -5070,7 +5164,7 @@ export default function OpenAContextMenu() {
5070
5164
  return (
5071
5165
  <>
5072
5166
  <Schedule
5073
- ariaLabel="On-call rota, 16 to 23 March"
5167
+ aria-label="On-call rota, 16 to 23 March"
5074
5168
  height={420}
5075
5169
  onInteraction={onInteraction}
5076
5170
  >
@@ -5085,7 +5179,7 @@ export default function OpenAContextMenu() {
5085
5179
  onOpenChange={(open) => {
5086
5180
  if (!open) setMenu(null);
5087
5181
  }}
5088
- ariaLabel={menu !== null ? `Hand over ${menu.duty.id}` : "Hand over"}
5182
+ aria-label={menu !== null ? `Hand over ${menu.duty.id}` : "Hand over"}
5089
5183
  >
5090
5184
  {menu !== null &&
5091
5185
  ENGINEERS.filter((engineer) => engineer.id !== menu.duty.lane).map((engineer) => (
@@ -5108,7 +5202,7 @@ export default function OpenAContextMenu() {
5108
5202
  #### When to use something else
5109
5203
 
5110
5204
  - You only need to know which task was picked → Selection
5111
- - You need the time at a point outside an interaction → The handle
5205
+ - You need the time at a point outside an interaction → Functions on the hook
5112
5206
 
5113
5207
  #### Keyboard
5114
5208
 
@@ -5122,8 +5216,8 @@ The keys of [First schedule](#first-schedule) apply here.
5122
5216
  |---|---|---|---|
5123
5217
  | `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
5218
  | `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) |
5219
+ | `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) |
5220
+ | `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
5221
  | `time` *required* | `number` | — | The wall-clock instant under the pointer.<br>Shown in: [Report what the pointer is on](#interactions) |
5128
5222
  | `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
5223
 
@@ -5195,7 +5289,7 @@ const TRANSFERS: readonly Dependency[] = [
5195
5289
  export default function DefaultTooltip() {
5196
5290
  const { Schedule, Lane, Subtasks, Dependencies } = useSchedule({ initialView: { domain: [at(5, 30), at(18)] } });
5197
5291
  return (
5198
- <Schedule ariaLabel="Three vehicles with the default tooltip" height={196}>
5292
+ <Schedule aria-label="Three vehicles with the default tooltip" height={196}>
5199
5293
  {VEHICLES.map((vehicle) => (
5200
5294
  <Lane key={vehicle.id} id={vehicle.id} label={vehicle.label} />
5201
5295
  ))}
@@ -5268,7 +5362,7 @@ export default function OwnTooltip() {
5268
5362
  const { Schedule, Lane, Subtasks, Dependencies } = useSchedule({ initialView: { domain: [at(5, 30), at(18)] } });
5269
5363
  return (
5270
5364
  <Schedule
5271
- ariaLabel="Three vehicles with the load in the tooltip"
5365
+ aria-label="Three vehicles with the load in the tooltip"
5272
5366
  height={196}
5273
5367
  tooltip={(target) => <LoadTooltip target={target} />}
5274
5368
  >
@@ -5388,7 +5482,7 @@ export default function DetailPanelInstead() {
5388
5482
  return (
5389
5483
  <Stack gap={3}>
5390
5484
  <Schedule
5391
- ariaLabel="Sprint 14 of the web team"
5485
+ aria-label="Sprint 14 of the web team"
5392
5486
  height={260}
5393
5487
  tooltip={false}
5394
5488
  onInteraction={onInteraction}
@@ -5488,7 +5582,7 @@ function Covers({ target }: { target: ScheduleTooltipTarget }) {
5488
5582
  export default function SayWhatABarCoversAndALineJoins() {
5489
5583
  const { Schedule, Lane, Subtasks, Dependencies, BlockedTimes } = useSchedule({ initialView: { domain: [at(6, 30), at(15, 30)] } });
5490
5584
  return (
5491
- <Schedule ariaLabel="Design, build and test, with leave under the build" height={188} tooltip={(target) => <Covers target={target} />}>
5585
+ <Schedule aria-label="Design, build and test, with leave under the build" height={188} tooltip={(target) => <Covers target={target} />}>
5492
5586
  <Lane id="noah" label="Noah Fischer" />
5493
5587
  <Lane id="arjun" label="Arjun Mehta" />
5494
5588
  <Lane id="eva" label="Eva Novak" />
@@ -5593,7 +5687,7 @@ export default function KeepAndRestore() {
5593
5687
 
5594
5688
  return (
5595
5689
  <Stack gap={3}>
5596
- <Schedule ariaLabel="Two teams on Tuesday, 17 March" height={290}>
5690
+ <Schedule aria-label="Two teams on Tuesday, 17 March" height={290}>
5597
5691
  <LaneGroup id="product" label="Product">
5598
5692
  <Lane id="noah" label="Noah Fischer" />
5599
5693
  <Lane id="arjun" label="Arjun Mehta" />
@@ -5650,7 +5744,7 @@ The keys of [First schedule](#first-schedule) apply here.
5650
5744
 
5651
5745
  | Prop | Type | Default | Description |
5652
5746
  |---|---|---|---|
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 |
5747
+ | `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
5748
 
5655
5749
  ###### Events
5656
5750
 
@@ -5662,7 +5756,7 @@ The keys of [First schedule](#first-schedule) apply here.
5662
5756
 
5663
5757
  | Prop | Type | Default | Description |
5664
5758
  |---|---|---|---|
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 |
5759
+ | `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
5760
  | `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
5761
 
5668
5762
  ### Linked schedules
@@ -5735,13 +5829,13 @@ export default function InStep() {
5735
5829
  const span = diesel.view.domain ?? start.domain;
5736
5830
  return (
5737
5831
  <Stack gap={2}>
5738
- <diesel.Schedule ariaLabel="Diesel vehicles on Tuesday, 17 March" height={240}>
5832
+ <diesel.Schedule aria-label="Diesel vehicles on Tuesday, 17 March" height={240}>
5739
5833
  {DIESEL.map((vehicle) => (
5740
5834
  <diesel.Lane key={vehicle.id} id={vehicle.id} label={vehicle.label} />
5741
5835
  ))}
5742
5836
  <diesel.Subtasks data={DIESEL_LEGS} tasks={CONSIGNMENTS} />
5743
5837
  </diesel.Schedule>
5744
- <electric.Schedule ariaLabel="Electric vans, the same hours" height={152}>
5838
+ <electric.Schedule aria-label="Electric vans, the same hours" height={152}>
5745
5839
  {ELECTRIC.map((vehicle) => (
5746
5840
  <electric.Lane key={vehicle.id} id={vehicle.id} label={vehicle.label} />
5747
5841
  ))}
@@ -5894,12 +5988,12 @@ export default function RotaBesideIncidents() {
5894
5988
  const incidents: ScheduleParts = useSchedule({ initialView: start, onViewChange: (v) => rota.setView(v) });
5895
5989
  return (
5896
5990
  <Stack gap={2}>
5897
- <rota.Schedule ariaLabel="Who was on call" height={140}>
5991
+ <rota.Schedule aria-label="Who was on call" height={140}>
5898
5992
  <rota.Lane id="primary" label="Primary" />
5899
5993
  <rota.Lane id="secondary" label="Secondary" />
5900
5994
  <rota.Subtasks data={DUTIES} tasks={PEOPLE} />
5901
5995
  </rota.Schedule>
5902
- <incidents.Schedule ariaLabel="Incidents, the same hours" height={290}>
5996
+ <incidents.Schedule aria-label="Incidents, the same hours" height={290}>
5903
5997
  {HIT.map((service) => (
5904
5998
  <incidents.Lane key={service.id} id={service.id} label={service.name} />
5905
5999
  ))}
@@ -5924,15 +6018,9 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
5924
6018
 
5925
6019
  | Prop | Type | Default | Description |
5926
6020
  |---|---|---|---|
5927
- | [`height`](#first-schedule) | `number` | `400` | Height in CSS pixels, both bands included. The width is the host's. |
6021
+ | [`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
6022
  | [`children`](#first-schedule) | `ReactNode` | — | `Lane`, `Dependencies` and `Subtasks`, in the order they are to stand and be drawn. |
5929
6023
 
5930
- ###### Accessibility
5931
-
5932
- | Prop | Type | Default | Description |
5933
- |---|---|---|---|
5934
- | [`ariaLabel`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
5935
-
5936
6024
  From `LaneProps` — the full table stands on [Lanes](#lanes).
5937
6025
 
5938
6026
  | Prop | Type | Default | Description |
@@ -5990,9 +6078,9 @@ From `ScheduleView` — the full table stands on [View](#view).
5990
6078
 
5991
6079
  - Only the view is shared - the span and the folded groups, not the selection, the hover or the vertical scroll.
5992
6080
 
5993
- What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.41.1/docs/adr/0032-what-umriss-is-not.md).
6081
+ What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.43.0/docs/adr/0032-what-umriss-is-not.md).
5994
6082
 
5995
- ### The handle
6083
+ ### Functions on the hook
5996
6084
 
5997
6085
  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
6086
 
@@ -6000,10 +6088,12 @@ Converts between a point on the screen and a time on a lane, so marks of your ow
6000
6088
  import { useSchedule } from "@umriss-ui/schedule";
6001
6089
  ```
6002
6090
 
6003
- The handle holds three functions and nothing else: `clientPointOf(time, lane?)`, `positionAt(clientX, clientY)` and `visibleDomain()`. Everything else the schedule does is props.
6091
+ [`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
6092
 
6005
6093
  Place your marks again whenever `view.domain` changes.
6006
6094
 
6095
+ `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.
6096
+
6007
6097
  #### Examples
6008
6098
 
6009
6099
  ##### Place a mark of your own
@@ -6014,7 +6104,7 @@ Place your marks again whenever `view.domain` changes.
6014
6104
  import { useEffect, useRef, useState } from "react";
6015
6105
  import { Stack, Text } from "@umriss-ui/core";
6016
6106
  import { useSchedule } from "@umriss-ui/schedule";
6017
- import type { ScheduleHandle, Subtask, Task } from "@umriss-ui/schedule";
6107
+ import type { Subtask, Task } from "@umriss-ui/schedule";
6018
6108
 
6019
6109
  const at = (hours: number, minutes = 0) => new Date(2026, 2, 17, hours, minutes).getTime();
6020
6110
  const min = (n: number) => n * 60_000;
@@ -6043,18 +6133,17 @@ const CUT_OFF = at(14);
6043
6133
  const clock = (time: number) => new Date(time).toLocaleTimeString("en-GB", { hour: "2-digit", minute: "2-digit" });
6044
6134
 
6045
6135
  export default function PlaceAMark() {
6046
- const { Schedule, Lane, Subtasks, view } = useSchedule({ initialView: { domain: [at(5, 30), at(18)] } });
6047
- const plan = useRef<ScheduleHandle>(null);
6136
+ const { Schedule, Lane, Subtasks, view, clientPointOf, positionAt } = useSchedule({ initialView: { domain: [at(5, 30), at(18)] } });
6048
6137
  const host = useRef<HTMLDivElement>(null);
6049
6138
  const [pin, setPin] = useState<number | null>(null);
6050
6139
  const [under, setUnder] = useState("Move the pointer over the plan");
6051
6140
 
6052
6141
  /* The client point, turned into one inside this example - anew whenever the span moves. */
6053
6142
  useEffect(() => {
6054
- const point = plan.current?.clientPointOf(CUT_OFF);
6143
+ const point = clientPointOf(CUT_OFF);
6055
6144
  const left = host.current?.getBoundingClientRect().left;
6056
6145
  setPin(point == null || left === undefined ? null : point.x - left);
6057
- }, [view.domain]);
6146
+ }, [view.domain, clientPointOf]);
6058
6147
 
6059
6148
  return (
6060
6149
  <Stack gap={2}>
@@ -6066,13 +6155,12 @@ export default function PlaceAMark() {
6066
6155
  )}
6067
6156
  </div>
6068
6157
  <Schedule
6069
- ref={plan}
6070
- ariaLabel="Three vehicles with a cut-off of the application's own"
6158
+ aria-label="Three vehicles with a cut-off of the application's own"
6071
6159
  height={196}
6072
6160
  onInteraction={(interaction) => {
6073
6161
  /* The interaction carries time and lane already; `positionAt` is for
6074
6162
  listeners of your own, which have only a point. */
6075
- const there = plan.current?.positionAt(interaction.clientX, interaction.clientY);
6163
+ const there = positionAt(interaction.clientX, interaction.clientY);
6076
6164
  setUnder(there == null ? "off the plan" : `${clock(there.time)} on ${there.lane ?? "no lane"}`);
6077
6165
  }}
6078
6166
  >
@@ -6089,6 +6177,69 @@ export default function PlaceAMark() {
6089
6177
  }
6090
6178
  ```
6091
6179
 
6180
+ ##### Show a finding
6181
+
6182
+ `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.
6183
+
6184
+ ```tsx
6185
+ import { Button, Stack, Text } from "@umriss-ui/core";
6186
+ import { useSchedule } from "@umriss-ui/schedule";
6187
+ import type { Subtask, Task } from "@umriss-ui/schedule";
6188
+
6189
+ const at = (hours: number, minutes = 0) => new Date(2026, 2, 17, hours, minutes).getTime();
6190
+
6191
+ const STATIONS = Array.from({ length: 60 }, (_, i) => ({ id: `station-${i + 1}`, label: `Station ${String(i + 1).padStart(2, "0")}` }));
6192
+
6193
+ const ORDERS: readonly Task[] = [{ id: "order", name: "Order O-7731", color: "light-dark(#2563eb, #6b9bff)" }];
6194
+
6195
+ const STEPS: readonly Subtask[] = STATIONS.map((station, i) => ({
6196
+ id: `step-${i + 1}`,
6197
+ task: "order",
6198
+ lane: station.id,
6199
+ from: at(6 + (i % 10)),
6200
+ to: at(7 + (i % 10)),
6201
+ }));
6202
+
6203
+ const FINDINGS = [
6204
+ { subtask: "step-4", text: "Station 04 starts before its material arrives" },
6205
+ { subtask: "step-37", text: "Station 37 overlaps a shift break" },
6206
+ { subtask: "step-58", text: "Station 58 ends after the cut-off" },
6207
+ ];
6208
+
6209
+ export default function ShowAFinding() {
6210
+ const { Schedule, Lane, Subtasks, scrollToSubtask, focusSubtask } = useSchedule({ initialView: { domain: [at(5, 30), at(10)] } });
6211
+ return (
6212
+ <Stack gap={2}>
6213
+ <Stack gap={1} data-findings>
6214
+ {FINDINGS.map((finding) => (
6215
+ <Button
6216
+ key={finding.subtask}
6217
+ data-finding={finding.subtask}
6218
+ onClick={() => scrollToSubtask(finding.subtask)}
6219
+ onKeyDown={(event) => {
6220
+ if (event.key !== "Enter") return;
6221
+ event.preventDefault();
6222
+ focusSubtask(finding.subtask);
6223
+ }}
6224
+ >
6225
+ {finding.text}
6226
+ </Button>
6227
+ ))}
6228
+ </Stack>
6229
+ <Schedule aria-label="Sixty stations of one order" height={240}>
6230
+ {STATIONS.map((station) => (
6231
+ <Lane key={station.id} id={station.id} label={station.label} />
6232
+ ))}
6233
+ <Subtasks data={STEPS} tasks={ORDERS} />
6234
+ </Schedule>
6235
+ <Text size="sm" tone="muted">
6236
+ A click shows the subtask; Enter also moves the focus to the plot.
6237
+ </Text>
6238
+ </Stack>
6239
+ );
6240
+ }
6241
+ ```
6242
+
6092
6243
  #### When to use something else
6093
6244
 
6094
6245
  - You need the time under the pointer during a hover or a click → Interactions
@@ -6103,8 +6254,7 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
6103
6254
 
6104
6255
  | Prop | Type | Default | Description |
6105
6256
  |---|---|---|---|
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. |
6257
+ | [`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
6258
  | [`children`](#first-schedule) | `ReactNode` | — | `Lane`, `Dependencies` and `Subtasks`, in the order they are to stand and be drawn. |
6109
6259
 
6110
6260
  ###### Events
@@ -6113,12 +6263,6 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
6113
6263
  |---|---|---|---|
6114
6264
  | [`onInteraction`](#first-schedule) | `(interaction: `[`ScheduleInteraction`](#interactions)`) => void` | — | Click, context menu and hover, with what the pointer is on and where. While it is set, a right-click opens no browser menu. |
6115
6265
 
6116
- ###### Accessibility
6117
-
6118
- | Prop | Type | Default | Description |
6119
- |---|---|---|---|
6120
- | [`ariaLabel`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
6121
-
6122
6266
  From `LaneProps` — the full table stands on [Lanes](#lanes).
6123
6267
 
6124
6268
  | Prop | Type | Default | Description |
@@ -6223,7 +6367,7 @@ export default function ListTheFindings() {
6223
6367
 
6224
6368
  return (
6225
6369
  <Stack gap={3}>
6226
- <Schedule ariaLabel="Three people's week with its findings" height={184}>
6370
+ <Schedule aria-label="Three people's week with its findings" height={184}>
6227
6371
  <Lane id="noah" label="Noah Fischer" />
6228
6372
  <Lane id="chloe" label="Chloe Durand" />
6229
6373
  <Lane id="arjun" label="Arjun Mehta" />
@@ -6278,7 +6422,7 @@ export default function NothingToFix() {
6278
6422
 
6279
6423
  return (
6280
6424
  <Stack gap={3}>
6281
- <Schedule ariaLabel="Two developers' week, without findings" height={140}>
6425
+ <Schedule aria-label="Two developers' week, without findings" height={140}>
6282
6426
  <Lane id="hana" label="Hana Sato" />
6283
6427
  <Lane id="kofi" label="Kofi Mensah" />
6284
6428
  <Subtasks data={WORK} tasks={PROJECTS} />
@@ -6335,7 +6479,7 @@ export default function CountPerKind() {
6335
6479
  <Badge tone={doubleBooked > 0 ? "warning" : "success"}>{doubleBooked} double-booked</Badge>
6336
6480
  <Badge tone={late > 0 ? "warning" : "success"}>{late} transfers too short</Badge>
6337
6481
  </Stack>
6338
- <Schedule ariaLabel="The day's legs with their findings counted" height={196}>
6482
+ <Schedule aria-label="The day's legs with their findings counted" height={196}>
6339
6483
  {VEHICLES.map((vehicle) => (
6340
6484
  <Lane key={vehicle.id} id={vehicle.id} label={vehicle.label} />
6341
6485
  ))}
@@ -6386,7 +6530,7 @@ The keys of [First schedule](#first-schedule) apply here.
6386
6530
 
6387
6531
  - Overlapping work is marked, not packed into sub-lanes; stacked overlap is not built yet.
6388
6532
 
6389
- What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.41.1/docs/adr/0032-what-umriss-is-not.md).
6533
+ What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.43.0/docs/adr/0032-what-umriss-is-not.md).
6390
6534
 
6391
6535
  ## Editing
6392
6536
 
@@ -6431,7 +6575,7 @@ export default function MoveInTime() {
6431
6575
  const [work, setWork] = useState(START);
6432
6576
  return (
6433
6577
  <Schedule
6434
- ariaLabel="Two people's work this week"
6578
+ aria-label="Two people's work this week"
6435
6579
  height={140}
6436
6580
  intents={["move"]}
6437
6581
  onIntent={(intent) => setWork((current) => current.map((item) => applyIntent(item, intent)))}
@@ -6494,7 +6638,7 @@ export default function MoveToAnotherLane() {
6494
6638
  return (
6495
6639
  <Stack gap={3}>
6496
6640
  <Schedule
6497
- ariaLabel="Five vehicles on Tuesday, 17 March, editable"
6641
+ aria-label="Five vehicles on Tuesday, 17 March, editable"
6498
6642
  height={284}
6499
6643
  intents={["move", "lane"]}
6500
6644
  onIntent={(intent) => {
@@ -6634,7 +6778,7 @@ export default function RefuseADoubleBooking() {
6634
6778
  return (
6635
6779
  <Stack gap={3}>
6636
6780
  <Schedule
6637
- ariaLabel="Sprint 14 of the web team, editable"
6781
+ aria-label="Sprint 14 of the web team, editable"
6638
6782
  height={240}
6639
6783
  intents={["move", "lane"]}
6640
6784
  onIntent={onIntent}
@@ -6673,7 +6817,7 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
6673
6817
 
6674
6818
  | Prop | Type | Default | Description |
6675
6819
  |---|---|---|---|
6676
- | [`height`](#first-schedule) | `number` | `400` | Height in CSS pixels, both bands included. The width is the host's. |
6820
+ | [`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
6821
  | [`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
6822
  | [`children`](#first-schedule) | `ReactNode` | — | `Lane`, `Dependencies` and `Subtasks`, in the order they are to stand and be drawn. |
6679
6823
 
@@ -6683,12 +6827,6 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
6683
6827
  |---|---|---|---|
6684
6828
  | [`onIntent`](#first-schedule) | `(intent: `[`Intent`](#first-schedule)`) => void` | — | Called once per intent when a drag ends. The data changes only if the caller changes it. |
6685
6829
 
6686
- ###### Accessibility
6687
-
6688
- | Prop | Type | Default | Description |
6689
- |---|---|---|---|
6690
- | [`ariaLabel`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
6691
-
6692
6830
  From `LaneProps` — the full table stands on [Lanes](#lanes).
6693
6831
 
6694
6832
  | Prop | Type | Default | Description |
@@ -6761,10 +6899,10 @@ From `Overlap` — the full table stands on [Findings as data](#findings-as-data
6761
6899
 
6762
6900
  #### Known limits
6763
6901
 
6764
- - No undo: your application owns the data, and with it the history ([ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.41.1/docs/adr/0032-what-umriss-is-not.md)).
6902
+ - No undo: your application owns the data, and with it the history ([ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.43.0/docs/adr/0032-what-umriss-is-not.md)).
6765
6903
  - The keys move work in time only; a lane change needs the pointer.
6766
6904
 
6767
- What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.41.1/docs/adr/0032-what-umriss-is-not.md).
6905
+ What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.43.0/docs/adr/0032-what-umriss-is-not.md).
6768
6906
 
6769
6907
  ### Stretch, lead-in, lead-out
6770
6908
 
@@ -6803,7 +6941,7 @@ export default function StretchTheMainTime() {
6803
6941
  const [work, setWork] = useState(START);
6804
6942
  return (
6805
6943
  <Schedule
6806
- ariaLabel="Two estimates to adjust"
6944
+ aria-label="Two estimates to adjust"
6807
6945
  height={140}
6808
6946
  intents={["stretch"]}
6809
6947
  onIntent={(intent) => setWork((current) => current.map((item) => applyIntent(item, intent)))}
@@ -6842,7 +6980,7 @@ export default function ChangeLeadInAndLeadOut() {
6842
6980
 
6843
6981
  return (
6844
6982
  <Schedule
6845
- ariaLabel="Two tours from North depot"
6983
+ aria-label="Two tours from North depot"
6846
6984
  height={150}
6847
6985
  intents={["stretch", "leadIn", "leadOut"]}
6848
6986
  onIntent={(intent) => setTours((current) => current.map((tour) => applyIntent(tour, intent)))}
@@ -6869,7 +7007,7 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
6869
7007
 
6870
7008
  | Prop | Type | Default | Description |
6871
7009
  |---|---|---|---|
6872
- | [`height`](#first-schedule) | `number` | `400` | Height in CSS pixels, both bands included. The width is the host's. |
7010
+ | [`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
7011
  | [`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
7012
  | [`children`](#first-schedule) | `ReactNode` | — | `Lane`, `Dependencies` and `Subtasks`, in the order they are to stand and be drawn. |
6875
7013
 
@@ -6879,12 +7017,6 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
6879
7017
  |---|---|---|---|
6880
7018
  | [`onIntent`](#first-schedule) | `(intent: `[`Intent`](#first-schedule)`) => void` | — | Called once per intent when a drag ends. The data changes only if the caller changes it. |
6881
7019
 
6882
- ###### Accessibility
6883
-
6884
- | Prop | Type | Default | Description |
6885
- |---|---|---|---|
6886
- | [`ariaLabel`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
6887
-
6888
7020
  From `LaneProps` — the full table stands on [Lanes](#lanes).
6889
7021
 
6890
7022
  | Prop | Type | Default | Description |
@@ -6936,7 +7068,7 @@ From `ScheduleView` — the full table stands on [View](#view).
6936
7068
 
6937
7069
  - A main time never becomes shorter than one raster step, or a minute without a raster.
6938
7070
 
6939
- What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.41.1/docs/adr/0032-what-umriss-is-not.md).
7071
+ What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.43.0/docs/adr/0032-what-umriss-is-not.md).
6940
7072
 
6941
7073
  ### Snapping
6942
7074
 
@@ -6972,7 +7104,7 @@ export default function SnapToTheTicks() {
6972
7104
  const [tours, setTours] = useState(START);
6973
7105
  return (
6974
7106
  <Schedule
6975
- ariaLabel="A tour on the default raster"
7107
+ aria-label="A tour on the default raster"
6976
7108
  height={100}
6977
7109
  intents={["move"]}
6978
7110
  onIntent={(intent) => setTours((current) => current.map((tour) => applyIntent(tour, intent)))}
@@ -7008,7 +7140,7 @@ export default function SnapToADuration() {
7008
7140
  const [work, setWork] = useState(START);
7009
7141
  return (
7010
7142
  <Schedule
7011
- ariaLabel="Work planned in half days"
7143
+ aria-label="Work planned in half days"
7012
7144
  height={140}
7013
7145
  snap={HALF_DAY}
7014
7146
  intents={["move", "stretch"]}
@@ -7046,7 +7178,7 @@ export default function SnapToHandovers() {
7046
7178
  const [watches, setWatches] = useState(START);
7047
7179
  return (
7048
7180
  <Schedule
7049
- ariaLabel="An on-call watch on the handover raster"
7181
+ aria-label="An on-call watch on the handover raster"
7050
7182
  height={110}
7051
7183
  snap={HANDOVERS}
7052
7184
  intents={["move"]}
@@ -7079,7 +7211,7 @@ export default function NoSnapping() {
7079
7211
  const [windows, setWindows] = useState(START);
7080
7212
  return (
7081
7213
  <Schedule
7082
- ariaLabel="A maintenance window placed freely"
7214
+ aria-label="A maintenance window placed freely"
7083
7215
  height={100}
7084
7216
  snap={false}
7085
7217
  intents={["move"]}
@@ -7102,7 +7234,7 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
7102
7234
 
7103
7235
  | Prop | Type | Default | Description |
7104
7236
  |---|---|---|---|
7105
- | [`height`](#first-schedule) | `number` | `400` | Height in CSS pixels, both bands included. The width is the host's. |
7237
+ | [`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
7238
  | [`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
7239
  | [`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
7240
  | [`children`](#first-schedule) | `ReactNode` | — | `Lane`, `Dependencies` and `Subtasks`, in the order they are to stand and be drawn. |
@@ -7113,12 +7245,6 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
7113
7245
  |---|---|---|---|
7114
7246
  | [`onIntent`](#first-schedule) | `(intent: `[`Intent`](#first-schedule)`) => void` | — | Called once per intent when a drag ends. The data changes only if the caller changes it. |
7115
7247
 
7116
- ###### Accessibility
7117
-
7118
- | Prop | Type | Default | Description |
7119
- |---|---|---|---|
7120
- | [`ariaLabel`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
7121
-
7122
7248
  From `LaneProps` — the full table stands on [Lanes](#lanes).
7123
7249
 
7124
7250
  | Prop | Type | Default | Description |
@@ -7261,7 +7387,7 @@ export default function DragFromTheBacklog() {
7261
7387
  ))}
7262
7388
  </Stack>
7263
7389
  <Schedule
7264
- ariaLabel="Today's plan of three people, with the backlog beside it"
7390
+ aria-label="Today's plan of three people, with the backlog beside it"
7265
7391
  height={196}
7266
7392
  intents={["place", "move", "lane"]}
7267
7393
  placing={placing}
@@ -7295,7 +7421,7 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
7295
7421
 
7296
7422
  | Prop | Type | Default | Description |
7297
7423
  |---|---|---|---|
7298
- | [`height`](#first-schedule) | `number` | `400` | Height in CSS pixels, both bands included. The width is the host's. |
7424
+ | [`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
7425
  | [`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
7426
  | [`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
7427
  | [`children`](#first-schedule) | `ReactNode` | — | `Lane`, `Dependencies` and `Subtasks`, in the order they are to stand and be drawn. |
@@ -7306,12 +7432,6 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
7306
7432
  |---|---|---|---|
7307
7433
  | [`onIntent`](#first-schedule) | `(intent: `[`Intent`](#first-schedule)`) => void` | — | Called once per intent when a drag ends. The data changes only if the caller changes it. |
7308
7434
 
7309
- ###### Accessibility
7310
-
7311
- | Prop | Type | Default | Description |
7312
- |---|---|---|---|
7313
- | [`ariaLabel`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
7314
-
7315
7435
  From `LaneProps` — the full table stands on [Lanes](#lanes).
7316
7436
 
7317
7437
  | Prop | Type | Default | Description |
@@ -7436,7 +7556,7 @@ export default function LanesThatFit() {
7436
7556
  return (
7437
7557
  <Stack gap={3}>
7438
7558
  <Schedule
7439
- ariaLabel="Two cooled vans and a dry van"
7559
+ aria-label="Two cooled vans and a dry van"
7440
7560
  height={190}
7441
7561
  intents={["move", "lane"]}
7442
7562
  canMoveTo={mayGo}
@@ -7522,7 +7642,7 @@ export default function DraggedIn() {
7522
7642
  </div>
7523
7643
  </Card>
7524
7644
  <Schedule
7525
- ariaLabel="A chilled delivery waiting beside two cooled vans and a dry van"
7645
+ aria-label="A chilled delivery waiting beside two cooled vans and a dry van"
7526
7646
  height={190}
7527
7647
  intents={["place"]}
7528
7648
  placing={placing}
@@ -7679,7 +7799,7 @@ export default function AroundLeave() {
7679
7799
  return (
7680
7800
  <Stack gap={3}>
7681
7801
  <Schedule
7682
- ariaLabel="The apps team this week, with leave"
7802
+ aria-label="The apps team this week, with leave"
7683
7803
  height={236}
7684
7804
  intents={["move", "lane"]}
7685
7805
  canMoveTo={mayGo}
@@ -7713,7 +7833,7 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
7713
7833
 
7714
7834
  | Prop | Type | Default | Description |
7715
7835
  |---|---|---|---|
7716
- | [`height`](#first-schedule) | `number` | `400` | Height in CSS pixels, both bands included. The width is the host's. |
7836
+ | [`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
7837
  | [`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
7838
  | [`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
7839
  | [`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. |
@@ -7725,12 +7845,6 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
7725
7845
  |---|---|---|---|
7726
7846
  | [`onIntent`](#first-schedule) | `(intent: `[`Intent`](#first-schedule)`) => void` | — | Called once per intent when a drag ends. The data changes only if the caller changes it. |
7727
7847
 
7728
- ###### Accessibility
7729
-
7730
- | Prop | Type | Default | Description |
7731
- |---|---|---|---|
7732
- | [`ariaLabel`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
7733
-
7734
7848
  From `LaneProps` — the full table stands on [Lanes](#lanes).
7735
7849
 
7736
7850
  | Prop | Type | Default | Description |
@@ -7795,7 +7909,7 @@ From `ScheduleView` — the full table stands on [View](#view).
7795
7909
 
7796
7910
  - A rule that changes while a drag runs is noticed at the drop, not before.
7797
7911
 
7798
- What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.41.1/docs/adr/0032-what-umriss-is-not.md).
7912
+ What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.43.0/docs/adr/0032-what-umriss-is-not.md).
7799
7913
 
7800
7914
  ### Ripple
7801
7915
 
@@ -7852,7 +7966,7 @@ export default function PushTheLaterStops() {
7852
7966
 
7853
7967
  return (
7854
7968
  <Stack gap={3}>
7855
- <Schedule ariaLabel="Tour T-01 on van FP 214 K" height={100}>
7969
+ <Schedule aria-label="Tour T-01 on van FP 214 K" height={100}>
7856
7970
  <Lane id="van-214" label="Van FP 214 K" />
7857
7971
  <Dependencies data={DRIVES} />
7858
7972
  <Subtasks data={stops} tasks={TOURS} />
@@ -7921,7 +8035,7 @@ export default function MoveAWholeTour() {
7921
8035
 
7922
8036
  return (
7923
8037
  <Stack gap={3}>
7924
- <Schedule ariaLabel="Two tours from North depot" height={140}>
8038
+ <Schedule aria-label="Two tours from North depot" height={140}>
7925
8039
  <Lane id="van-214" label="Van FP 214 K" />
7926
8040
  <Lane id="van-377" label="E-van FP 377 K" />
7927
8041
  <Dependencies data={DRIVES} />
@@ -7977,7 +8091,7 @@ export default function RippleOnEveryDrag() {
7977
8091
 
7978
8092
  return (
7979
8093
  <Schedule
7980
- ariaLabel="Design, build and test of the shop search, rippling"
8094
+ aria-label="Design, build and test of the shop search, rippling"
7981
8095
  height={184}
7982
8096
  intents={["move", "stretch"]}
7983
8097
  onIntent={onIntent}
@@ -8002,7 +8116,7 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
8002
8116
 
8003
8117
  | Prop | Type | Default | Description |
8004
8118
  |---|---|---|---|
8005
- | [`height`](#first-schedule) | `number` | `400` | Height in CSS pixels, both bands included. The width is the host's. |
8119
+ | [`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
8120
  | [`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
8121
  | [`children`](#first-schedule) | `ReactNode` | — | `Lane`, `Dependencies` and `Subtasks`, in the order they are to stand and be drawn. |
8008
8122
 
@@ -8012,12 +8126,6 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
8012
8126
  |---|---|---|---|
8013
8127
  | [`onIntent`](#first-schedule) | `(intent: `[`Intent`](#first-schedule)`) => void` | — | Called once per intent when a drag ends. The data changes only if the caller changes it. |
8014
8128
 
8015
- ###### Accessibility
8016
-
8017
- | Prop | Type | Default | Description |
8018
- |---|---|---|---|
8019
- | [`ariaLabel`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
8020
-
8021
8129
  From `LaneProps` — the full table stands on [Lanes](#lanes).
8022
8130
 
8023
8131
  | Prop | Type | Default | Description |
@@ -8080,10 +8188,10 @@ From `ScheduleView` — the full table stands on [View](#view).
8080
8188
 
8081
8189
  #### Known limits
8082
8190
 
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.41.1/docs/adr/0032-what-umriss-is-not.md)).
8191
+ - 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.43.0/docs/adr/0032-what-umriss-is-not.md)).
8084
8192
  - A dependency joins subtasks of one task, so work of other tasks is never pushed.
8085
8193
 
8086
- What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.41.1/docs/adr/0032-what-umriss-is-not.md).
8194
+ What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/schedule-v0.43.0/docs/adr/0032-what-umriss-is-not.md).
8087
8195
 
8088
8196
  ## Measuring
8089
8197
 
@@ -8298,7 +8406,7 @@ const RUNS = [
8298
8406
  function Plot({ data, onInteraction }: { data: Plan; onInteraction: (e: ScheduleInteraction) => void }) {
8299
8407
  const { Schedule, Lane, Subtasks, Dependencies } = useSchedule({ initialView: { domain: VIEW } });
8300
8408
  return (
8301
- <Schedule ariaLabel="Benchmark plan" height={HEIGHT} laneHeight={LANE_HEIGHT} onInteraction={onInteraction}>
8409
+ <Schedule aria-label="Benchmark plan" height={HEIGHT} laneHeight={LANE_HEIGHT} onInteraction={onInteraction}>
8302
8410
  {data.lanes.map((id) => (
8303
8411
  <Lane key={id} id={id} />
8304
8412
  ))}
@@ -8415,7 +8523,7 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
8415
8523
 
8416
8524
  | Prop | Type | Default | Description |
8417
8525
  |---|---|---|---|
8418
- | [`height`](#first-schedule) | `number` | `400` | Height in CSS pixels, both bands included. The width is the host's. |
8526
+ | [`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
8527
  | [`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
8528
  | [`children`](#first-schedule) | `ReactNode` | — | `Lane`, `Dependencies` and `Subtasks`, in the order they are to stand and be drawn. |
8421
8529
 
@@ -8425,12 +8533,6 @@ From `ScheduleProps` — the full table stands on [First schedule](#first-schedu
8425
8533
  |---|---|---|---|
8426
8534
  | [`onInteraction`](#first-schedule) | `(interaction: `[`ScheduleInteraction`](#interactions)`) => void` | — | Click, context menu and hover, with what the pointer is on and where. While it is set, a right-click opens no browser menu. |
8427
8535
 
8428
- ###### Accessibility
8429
-
8430
- | Prop | Type | Default | Description |
8431
- |---|---|---|---|
8432
- | [`ariaLabel`](#first-schedule) *required* | `string` | — | What the schedule shows, for a screen reader: "Plan of week 38". |
8433
-
8434
8536
  From `LaneProps` — the full table stands on [Lanes](#lanes).
8435
8537
 
8436
8538
  | Prop | Type | Default | Description |
@@ -8511,13 +8613,14 @@ Everything `@umriss-ui/schedule` exports, with its signature. A component's prop
8511
8613
  Declares a schedule: hands back `Schedule` and the parts declared inside it
8512
8614
  - `Lane`, `LaneGroup`, `Subtasks`, `Dependencies`, `BlockedTimes`, `Milestones` -, its
8513
8615
  `view` and the setters `setView`, `setDomain`, `toggleGroup`, `foldAll` and
8514
- `unfoldAll`. A ref on `Schedule` still gives the `ScheduleHandle`.
8616
+ `unfoldAll`, `positionAt`, `clientPointOf` and `visibleDomain`, and `scrollToLane`, `scrollToSubtask` and
8617
+ `focusSubtask`.
8515
8618
 
8516
8619
  ```ts
8517
8620
  function useSchedule(options?: ScheduleOptions): ScheduleParts;
8518
8621
  ```
8519
8622
 
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).
8623
+ 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
8624
 
8522
8625
  #### Functions
8523
8626
 
@@ -8560,7 +8663,7 @@ Every finding at once. Without blocked time there is none in it.
8560
8663
  function findings(subtasks: readonly Subtask[], dependencies: readonly Dependency[], blocked?: readonly BlockedTime[]): Findings;
8561
8664
  ```
8562
8665
 
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).
8666
+ 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
8667
 
8565
8668
  ##### `inBlockedTime`
8566
8669
 
@@ -8595,7 +8698,7 @@ nothing.
8595
8698
  function overlaps(subtasks: readonly Subtask[]): Overlap[];
8596
8699
  ```
8597
8700
 
8598
- Used on [Scenarios](#scenarios), [Lane groups](#lane-groups), [Findings as data](#findings-as-data) and [Move and lane](#move-and-lane).
8701
+ 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
8702
 
8600
8703
  ##### `resolveAppearance`
8601
8704
 
@@ -8890,11 +8993,11 @@ point on the screen and a time on a lane. Everything else is props.
8890
8993
 
8891
8994
  | Prop | Type | Default | Description |
8892
8995
  |---|---|---|---|
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) |
8996
+ | `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. |
8997
+ | `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. |
8895
8998
  | `visibleDomain` *required* | `() => readonly [number, number]` | — | The visible span, as two wall-clock instants. |
8896
8999
 
8897
- Used on [The handle](#the-handle).
9000
+ *Deprecated* since 0.43 - use the functions on [`useSchedule`](#api-index-1)'s return.
8898
9001
 
8899
9002
  ##### `ScheduleHit`
8900
9003
 
@@ -8926,10 +9029,10 @@ it, its view and the setters that change it.
8926
9029
 
8927
9030
  | Prop | Type | Default | Description |
8928
9031
  |---|---|---|---|
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 |
9032
+ | `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 |
9033
+ | `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
9034
  | `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 |
9035
+ | `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
9036
  | `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
9037
  | `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
9038
  | `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 +9040,12 @@ it, its view and the setters that change it.
8937
9040
  | `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
9041
  | `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
9042
  | `toggleGroup` *required* | `(id: string) => void` | — | Folds or unfolds one **Lane group**. |
9043
+ | `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) |
9044
+ | `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) |
9045
+ | `visibleDomain` *required* | `() => readonly [number, number]` | — | The visible span, as two wall-clock instants. |
9046
+ | `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. |
9047
+ | `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) |
9048
+ | `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
9049
  | `foldAll` *required* | `() => void` | — | Folds every group.<br>Shown in: [Fold groups from outside (Lane groups)](#lane-groups) |
8941
9050
  | `unfoldAll` *required* | `() => void` | — | Unfolds every group.<br>Shown in: [Fold groups from outside (Lane groups)](#lane-groups) |
8942
9051
 
@@ -8984,7 +9093,7 @@ Stretch the main time at one or both of its edges.
8984
9093
 
8985
9094
  Its table stands on [Subtasks](#subtasks).
8986
9095
 
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).
9096
+ 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
9097
 
8989
9098
  ##### `SubtaskAppearance`
8990
9099
 
@@ -9002,7 +9111,7 @@ Its table stands on [Subtasks](#subtasks).
9002
9111
 
9003
9112
  Its table stands on [Subtasks](#subtasks).
9004
9113
 
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).
9114
+ 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
9115
 
9007
9116
  ##### `ViolatedDependency`
9008
9117