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