Stretch, lead-in, lead-out

Change how long work takes, or how long its preparation and its wrap-up take, by dragging an edge (also called resizing). Each part changes on its own and is reported for your application to apply.

import { useSchedule, applyIntent } from "@umriss-ui/schedule";

stretch enables the two edges of the main time. leadIn and leadOut add a grip at the outer edge of each; the grips show on the selected subtask only, so click a bar first.

Stretching the main time leaves the lead-in and the lead-out as long as they were.

Examples

Stretch the main time

With stretch in intents, either edge of a bar drags to a new start or end; the other edge stays put.

import { useState } from "react";
import { applyIntent, useSchedule } from "@umriss-ui/schedule";
import type { Subtask, Task } from "@umriss-ui/schedule";

const day = (d: number, hours = 9) => new Date(2026, 2, d, hours).getTime();

const PROJECTS: readonly Task[] = [{ id: "booking", name: "Booking app", color: "light-dark(#0d9488, #3cc7b8)" }];

const START: readonly Subtask[] = [
  { id: "w-111", task: "booking", lane: "hana", from: day(16), to: day(18, 17), name: "Push notifications" },
  { id: "w-116", task: "booking", lane: "david", from: day(16), to: day(19, 13), name: "Device test matrix" },
];

export default function StretchTheMainTime() {
  const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: [day(16, 0), day(21, 0)] } });
  const [work, setWork] = useState(START);
  return (
    <Schedule
      ariaLabel="Two estimates to adjust"
      height={140}
      intents={["stretch"]}
      onIntent={(intent) => setWork((current) => current.map((item) => applyIntent(item, intent)))}
    >
      <Lane id="hana" label="Hana Sato" />
      <Lane id="david" label="David Kowalski" />
      <Subtasks data={work} tasks={PROJECTS} />
    </Schedule>
  );
}

Change lead-in and lead-out

Add leadIn and leadOut: a selected tour shows a grip for its loading and its unloading, each changed on its own.

import { useState } from "react";
import { applyIntent, useSchedule } from "@umriss-ui/schedule";
import type { 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 TOURS: Task[] = [{ id: "north", name: "North depot tours", color: "light-dark(#0d9488, #3cc7b8)" }];

/* Lead-in is loading at the depot, lead-out unloading the returns. */
const START: Subtask[] = [
  { id: "T-01", task: "north", lane: "van-214", from: at(8), to: at(10), leadIn: min(30), leadOut: min(15) },
  { id: "T-02", task: "north", lane: "van-377", from: at(11), to: at(12, 30), leadIn: min(15) },
];

export default function ChangeLeadInAndLeadOut() {
  const { Schedule, Lane, Subtasks } = useSchedule({ initialView: { domain: [at(6), at(13, 30)] } });
  const [tours, setTours] = useState(START);

  return (
    <Schedule
      ariaLabel="Two tours from North depot"
      height={150}
      intents={["stretch", "leadIn", "leadOut"]}
      onIntent={(intent) => setTours((current) => current.map((tour) => applyIntent(tour, intent)))}
    >
      <Lane id="van-214" label="Van FP 214 K" />
      <Lane id="van-377" label="E-van FP 377 K" />
      <Subtasks data={tours} tasks={TOURS} />
    </Schedule>
  );
}

Keyboard

Key Action
Alt+Shift+← Alt+Shift+→ Proposes ending the active subtask one raster step earlier or later.

The keys of First schedule apply here.

Props on this page

From ScheduleProps — the full table stands on First schedule.

NameTypeDefaultDescription
heightnumber400Height in CSS pixels, both bands included. The width is the host's.
intentsreadonly IntentKind[][]The intents the caller handles. Each one enables its interaction; none leaves a read-only schedule (ADR-0023).
childrenReactNode—Lane, Dependencies and Subtasks, in the order they are to stand and be drawn.

Events

NameTypeDefaultDescription
onIntent(intent: Intent) => void—Called once per intent when a drag ends. The data changes only if the caller changes it.

Accessibility

NameTypeDefaultDescription
ariaLabelrequiredstring—What the schedule shows, for a screen reader: "Plan of week 38".

From LaneProps — the full table stands on Lanes.

NameTypeDefaultDescription
idrequiredstring—The identity subtasks name in their lane.
labelReactNodeidWhat the lane header says - the person, vehicle or room. Real text, read by a screen reader.

From SubtasksProps — the full table stands on Subtasks.

NameTypeDefaultDescription
datarequiredreadonly Subtask[]—The subtasks, as the caller holds them. The schedule never changes one.
tasksrequiredreadonly 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.

From Subtask — the full table stands on Subtasks.

NameTypeDefaultDescription
idrequiredstring—The caller's identity of the subtask.
taskrequiredstring—The id of the task it belongs to.
lanerequiredstring—The id of the lane it sits on.
fromrequirednumber—Start of the main time.
torequirednumber—End of the main time.
leadInnumber—Duration of the lead-in before the main time; none is zero.
leadOutnumber—Duration of the lead-out after the main time; none is zero.
namestring—What the subtask is called, where the schedule names it.

From Task — the full table stands on Subtasks.

NameTypeDefaultDescription
idrequiredstring—The caller's identity of the task.
colorrequiredstring—Any CSS colour - a literal, a token, light-dark(…). The colour means what the caller's domain needs it to mean.
namestring—What the task is called, where the schedule names it.

From ScheduleOptions — the full table stands on View.

NameTypeDefaultDescription
initialViewScheduleView—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.

From ScheduleView — the full table stands on View.

NameTypeDefaultDescription
domainreadonly [number, number]—The span in view, as two wall-clock instants. Absent, the schedule shows the extent of its subtasks, within zoomLimits.

Known limits

What umriss deliberately does not build, and why: ADR-0032.

Every page of @umriss-ui/schedule

Getting started

Plan

Time

Reading

Editing

Measuring

API index