npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@umriss-ui/schedule

v0.3.16

Published

React Gantt-style schedule for data-dense applications - work on lanes over time, with dependencies, blocked time, findings and controlled editing.

Readme

@umriss-ui/schedule

A React Gantt-style schedule for data-dense applications — work planned over time: subtasks on lanes — people, vehicles, rooms or machines. One task runs across several lanes in a fixed order, each stop has a main time with a lead-in before it and a lead-out after it, between the stops lie dependencies with lags of their own, and a lane can be blocked for leave or maintenance. The schedule draws all of it, marks what cannot work — two subtasks on one lane at once, a dependency whose lag does not fit, work in blocked time — and lets a planner move things by hand without ever changing the plan underneath them.

Install

pnpm add @umriss-ui/schedule @umriss-ui/core @umriss-ui/charts

Both @umriss-ui/core and @umriss-ui/charts are peer dependencies (ADR-0022): the schedule takes its styling regime, provider, formats and wording from core, and its time arithmetic — the affine scale, the time steps, the working calendar, the canvas colour resolution — from charts. React 18 or 19 as a peer as well.

The smallest schedule that runs

import { Lane, Schedule, Subtasks, Dependencies } from "@umriss-ui/schedule";
import type { Subtask, Task, Dependency } from "@umriss-ui/schedule";

const at = (h: number, m = 0) => new Date(2026, 2, 17, h, m).getTime();

const tasks: Task[] = [{ id: "a-2041", color: "#2563eb" }];
const subtasks: Subtask[] = [
  { id: "cut", task: "a-2041", lane: "saw", from: at(6), to: at(7), leadOut: 10 * 60_000 },
  { id: "mill", task: "a-2041", lane: "mill", from: at(8), to: at(10), leadIn: 30 * 60_000 },
];
const dependencies: Dependency[] = [{ id: "move", from: "cut", to: "mill", lag: 15 * 60_000 }];

export function Plan() {
  return (
    <Schedule ariaLabel="Plan of Tuesday" initialDomain={[at(5), at(12)]}>
      <Lane id="saw" label="Saw" />
      <Lane id="mill" label="Mill" />
      <Dependencies data={dependencies} />
      <Subtasks data={subtasks} tasks={tasks} />
    </Schedule>
  );
}

Editing is controlled

The schedule draws the data it is given and changes none of it (ADR-0023). A drag shows a ghost with the findings the drop would create, and ends in an intent — move, lane, stretch, lead-in, lead-out — reported to the caller:

<Schedule
  intents={["move", "lane"]}
  onIntent={(intent) => setSubtasks((all) => all.map((s) => applyIntent(s, intent)))}
  …
>

intents lists what the application handles; without it the schedule is read-only. canMoveTo(subtask, lane) narrows it: a drag across a lane the subtask may not go to leaves the ghost where it was allowed and shows a refusal, and a refused drop reports nothing. A drag near the edge of the plot pans the plot along, and snap takes the tick raster, a step, or a step with an offset - handovers at 06:00, 14:00 and 22:00 are eight hours offset by six.

Dependent subtasks never move by themselves: ripple computes the cascade for the application to apply, or not, and shiftTask the moves of a whole task. findings, overlaps and violatedDependencies return what the schedule draws as data.

Work that is not on the plan is dragged in from any list of the application, with the platform's drag and drop: the application says what it is dragging in placing, the drop reports a place intent, and subtaskFromPlace builds the subtask under the application's own id.

What a bar says

label writes a line into every bar, cut off where the bar is too narrow and left out where nothing would be readable. appearance adds what a colour cannot say - "provisional", "fixed", "muted", "open" - and progress draws the share that is done as a rail along the bar. route, anchor and ends decide how a dependency is drawn, per schedule or per dependency, without touching what a finding says.

Each appearance owns exactly one property of the drawing, so several on one bar stay several statements: "provisional" the fill (none at all, the surface shows through), "fixed" the ends (a cap inside each), "muted" the saturation (the task colour half mixed into the surface, at full height), and "open" a fade at whichever edge of the view the bar passes. progress owns a rail inside the main time. The faint, outlined fill belongs to a lead-in and a lead-out and to nothing else.

Lanes that fold

<LaneGroup> puts teams, depots or regions over the lanes, to any depth. A group is structure and never a lane: nothing sits on it, no finding is reported for it, and no intent names it (ADR-0025).

<LaneGroup id="hall-a" label="Hall A">
  <LaneGroup id="turning" label="Turning line">
    <Lane id="lathe-1" label="Lathe 1" />
    <Lane id="lathe-2" label="Lathe 2" />
  </LaneGroup>
  <Lane id="press-1" label="Press 1" />
</LaneGroup>

Folded, a group becomes one row showing a miniature — every lane in it as a thin strip, the real work smaller. Dependencies still arrive at the right strip, findings are still marked, and a strip can still be hovered and selected: folding costs a planner detail and never access. collapsedGroups, defaultCollapsedGroups and onCollapsedGroupsChange put the state in the application's hands; it is a view state, never an intent.

Reading and moving

A tooltip on a hovered subtask or dependency names the order, the times, lead-in and lead-out and the findings; tooltip replaces its content or switches it off. now draws the present across the lanes. The wheel over the schedule, lane headers included, scrolls the lanes and releases the page at their end, Ctrl or ⌘ and a pinch zoom, Shift pans; on a touch screen one finger pans and a tap selects. The lane headers take at most 40 % of the width, so that on a phone the plot keeps the larger part; onDomainChange reports the visible span, and a ref handle turns a client point into a time and a lane and back.

Blocked time

<BlockedTimes data={…} /> takes the time a lane is not available — leave, maintenance — as plain data, { id, lane, from, to, label? }. It is drawn hatched behind the work; a subtask that covers some of it is a finding (inBlockedTime, and the third argument of findings), and a drag does not put work into it.

Not yet

What the schedule does not do today, and waits for a caller who needs it (what it never does stands in ADR-0032):

  • Milestones — a point in time on a lane, drawn as a mark rather than a bar.
  • Stacked overlap — overlapping subtasks packed into sub-lanes on request; today an overlap is always drawn offset, as the finding it is.
  • Dependencies across tasks — a dependency joins two subtasks of one task.

Done from this list: blocked time per lane.

Where to read on

  • The demo: https://romanhaendler.github.io/umriss-ui/schedule/, or locally pnpm dev:schedule (port 4176). It is the documentation — every page shows running examples with their source and the props table generated from src/.
  • CHANGELOG.md — what changes for a caller.
  • For a coding agent: docs/llms-full.md inside the installed package — the demo as one Markdown file, pinned to the installed version: every page with its examples' source, its props tables and why it is built as it is, and the declaration of every other export. Online, for the latest version: https://romanhaendler.github.io/umriss-ui/schedule/llms.txt.
  • The vocabulary — Task, Subtask, Lead-in, Lead-out, Dependency, Violated dependency, Blocked time, Lane header, Lane group, Miniature, Intent, Ghost — stands in the workspace's CONTEXT.md, section "The schedule".
  • ../../docs/design-language.md — the design language all five packages share.

Licence

MIT — see LICENSE.