The shape of a dependency's line: a curve that leaves and arrives forwards, a straight line, or orthogonal segments. Set it for the schedule, or for one dependency.
import { useSchedule } from "@umriss-ui/schedule";
The default: each line leaves forwards and arrives forwards, so it shows which way the work goes, even when it loops back up.
import { useSchedule } from "@umriss-ui/schedule";
import type { Dependency, Subtask, Task } from "@umriss-ui/schedule";
const at = (hours: number, minutes = 0) => new Date(2026, 2, 17, hours, minutes).getTime();
const min = (n: number) => n * 60_000;
const PROJECTS: Task[] = [{ id: "shop", name: "Online shop relaunch", color: "light-dark(#2563eb, #6b9bff)" }];
const WORK: Subtask[] = [
{ id: "layout", task: "shop", lane: "noah", from: at(7), to: at(8, 30), name: "Search results layout" },
{ id: "build", task: "shop", lane: "chloe", from: at(9), to: at(10, 30), name: "Search results page" },
{ id: "test", task: "shop", lane: "eva", from: at(11), to: at(12), name: "Test" },
{ id: "polish", task: "shop", lane: "noah", from: at(12, 30), to: at(13), name: "Polish" },
];
const HANDOVERS: Dependency[] = [
{ id: "to-build", from: "layout", to: "build", lag: min(20) },
{ id: "to-test", from: "build", to: "test", lag: min(20) },
{ id: "to-polish", from: "test", to: "polish", lag: min(20) },
];
export default function Curve() {
const { Schedule, Lane, Subtasks, Dependencies } = useSchedule({ initialView: { domain: [at(6, 30), at(13, 30)] } });
return (
<Schedule ariaLabel="Search results, from layout to polish, curved lines" height={188}>
<Lane id="noah" label="Noah Fischer" />
<Lane id="chloe" label="Chloe Durand" />
<Lane id="eva" label="Eva Novak" />
<Dependencies data={HANDOVERS} />
<Subtasks data={WORK} tasks={PROJECTS} />
</Schedule>
);
}
route="straight" draws the shortest line between the two ends, calm in a plan read from a distance.
import { useSchedule } from "@umriss-ui/schedule";
import type { Dependency, Subtask, Task } from "@umriss-ui/schedule";
const at = (hours: number, minutes = 0) => new Date(2026, 2, 17, hours, minutes).getTime();
const min = (n: number) => n * 60_000;
const PROJECTS: Task[] = [{ id: "shop", name: "Online shop relaunch", color: "light-dark(#2563eb, #6b9bff)" }];
const WORK: Subtask[] = [
{ id: "layout", task: "shop", lane: "noah", from: at(7), to: at(8, 30), name: "Search results layout" },
{ id: "build", task: "shop", lane: "chloe", from: at(9), to: at(10, 30), name: "Search results page" },
{ id: "test", task: "shop", lane: "eva", from: at(11), to: at(12), name: "Test" },
{ id: "polish", task: "shop", lane: "noah", from: at(12, 30), to: at(13), name: "Polish" },
];
const HANDOVERS: Dependency[] = [
{ id: "to-build", from: "layout", to: "build", lag: min(20) },
{ id: "to-test", from: "build", to: "test", lag: min(20) },
{ id: "to-polish", from: "test", to: "polish", lag: min(20) },
];
export default function Straight() {
const { Schedule, Lane, Subtasks, Dependencies } = useSchedule({ initialView: { domain: [at(6, 30), at(13, 30)] } });
return (
<Schedule ariaLabel="Search results, from layout to polish, straight lines" height={188} route="straight" attach="nearest">
<Lane id="noah" label="Noah Fischer" />
<Lane id="chloe" label="Chloe Durand" />
<Lane id="eva" label="Eva Novak" />
<Dependencies data={HANDOVERS} />
<Subtasks data={WORK} tasks={PROJECTS} />
</Schedule>
);
}
route="orthogonal" leaves the bar, runs across and comes in again, like a flow chart.
import { useSchedule } from "@umriss-ui/schedule";
import type { Dependency, Subtask, Task } from "@umriss-ui/schedule";
const at = (hours: number, minutes = 0) => new Date(2026, 2, 17, hours, minutes).getTime();
const min = (n: number) => n * 60_000;
const PROJECTS: Task[] = [{ id: "shop", name: "Online shop relaunch", color: "light-dark(#2563eb, #6b9bff)" }];
const WORK: Subtask[] = [
{ id: "layout", task: "shop", lane: "noah", from: at(7), to: at(8, 30), name: "Search results layout" },
{ id: "build", task: "shop", lane: "chloe", from: at(9), to: at(10, 30), name: "Search results page" },
{ id: "test", task: "shop", lane: "eva", from: at(11), to: at(12), name: "Test" },
{ id: "polish", task: "shop", lane: "noah", from: at(12, 30), to: at(13), name: "Polish" },
];
const HANDOVERS: Dependency[] = [
{ id: "to-build", from: "layout", to: "build", lag: min(20) },
{ id: "to-test", from: "build", to: "test", lag: min(20) },
{ id: "to-polish", from: "test", to: "polish", lag: min(20) },
];
export default function Orthogonal() {
const { Schedule, Lane, Subtasks, Dependencies } = useSchedule({ initialView: { domain: [at(6, 30), at(13, 30)] } });
return (
<Schedule ariaLabel="Search results, from layout to polish, orthogonal lines" height={188} route="orthogonal" attach="nearest">
<Lane id="noah" label="Noah Fischer" />
<Lane id="chloe" label="Chloe Durand" />
<Lane id="eva" label="Eva Novak" />
<Dependencies data={HANDOVERS} />
<Subtasks data={WORK} tasks={PROJECTS} />
</Schedule>
);
}
A route on a single dependency overrides the schedule's: here only the loop back up to the designer is drawn orthogonal.
import { useSchedule } from "@umriss-ui/schedule";
import type { Dependency, Subtask, Task } from "@umriss-ui/schedule";
const at = (hours: number, minutes = 0) => new Date(2026, 2, 17, hours, minutes).getTime();
const min = (n: number) => n * 60_000;
const PROJECTS: Task[] = [{ id: "shop", name: "Online shop relaunch", color: "light-dark(#2563eb, #6b9bff)" }];
const WORK: Subtask[] = [
{ id: "layout", task: "shop", lane: "noah", from: at(7), to: at(8, 30), name: "Search results layout" },
{ id: "build", task: "shop", lane: "chloe", from: at(9), to: at(10, 30), name: "Search results page" },
{ id: "test", task: "shop", lane: "eva", from: at(11), to: at(12), name: "Test" },
{ id: "polish", task: "shop", lane: "noah", from: at(12, 30), to: at(13), name: "Polish" },
];
const HANDOVERS: Dependency[] = [
{ id: "to-build", from: "layout", to: "build", lag: min(20) },
{ id: "to-test", from: "build", to: "test", lag: min(20) },
/* Its own route, whatever the schedule says. */
{ id: "to-polish", from: "test", to: "polish", lag: min(20), route: "orthogonal" },
];
export default function PerDependency() {
const { Schedule, Lane, Subtasks, Dependencies } = useSchedule({ initialView: { domain: [at(6, 30), at(13, 30)] } });
return (
<Schedule ariaLabel="Search results, from layout to polish, straight lines but one" height={188} route="straight" attach="nearest">
<Lane id="noah" label="Noah Fischer" />
<Lane id="chloe" label="Chloe Durand" />
<Lane id="eva" label="Eva Novak" />
<Dependencies data={HANDOVERS} />
<Subtasks data={WORK} tasks={PROJECTS} />
</Schedule>
);
}
The keys of First schedule apply here.
From ScheduleProps — the full table stands on First schedule.
| Name | Type | Default | Description |
|---|---|---|---|
height | number | 400 | Height in CSS pixels, both bands included. The width is the host's. |
route | DependencyRoute| "curve" | "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. |
attach | DependencyAttachment"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. |
children | ReactNode | — | Lane, Dependencies and Subtasks, in the order they are to stand and
be drawn. |
| Name | Type | Default | Description |
|---|---|---|---|
ariaLabelrequired | string | — | What the schedule shows, for a screen reader: "Plan of week 38". |
From LaneProps — the full table stands on Lanes.
From SubtasksProps — the full table stands on Subtasks.
From Subtask — the full table stands on Subtasks.
| Name | Type | Default | Description |
|---|---|---|---|
idrequired | string | — | The caller's identity of the subtask. |
taskrequired | string | — | The id of the task it belongs to. |
lanerequired | string | — | The id of the lane it sits on. |
fromrequired | number | — | Start of the main time. |
torequired | number | — | End of the main time. |
name | string | — | What the subtask is called, where the schedule names it. |
From Task — the full table stands on Subtasks.
From DependenciesProps — the full table stands on Dependencies.
| Name | Type | Default | Description |
|---|---|---|---|
datarequired | readonly Dependency[] | — | The dependencies, as the caller holds them. |
From Dependency — the full table stands on Dependencies.
| Name | Type | Default | Description |
|---|---|---|---|
idrequired | string | — | The caller's identity of the dependency. |
fromrequired | string | — | The id of the subtask it leaves. |
torequired | string | — | The id of the subtask it arrives at. |
lagrequired | number | — | The least time between the two anchors; zero where the successor may begin as soon as the predecessor ends. |
route | DependencyRoute| "curve" | — | How this one is drawn, where it is not drawn like the rest. It changes
the picture and never the finding: whether a dependency is violated follows
from leaves and arrives alone. |
From ScheduleOptions — the full table stands on View.
| Name | Type | Default | Description |
|---|---|---|---|
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. |