import type { ReactNode } from "react"; import type { TimelineLane } from "./LaneTimelineLane.tsx"; import type { TimelineSpan } from "./laneTimelineGeometry.ts"; export type LaneTimelineProps = { className?: string; /** * Shown when there are no lanes, or when `range` is not a pair of * calendar dates. Defaults to a real `EmptyState` — *"real empty * states"* is a standing requirement, and an empty timeline is * what a household sees on a good week. */ emptyState?: ReactNode; /** * What level the lane headings are. The caller's, because only * the caller knows what the timeline sits under: one directly * below a page `

` wants `2`. Getting it wrong is an axe * `heading-order` failure and a hole in the screen-reader * outline. */ headingLevel?: 2 | 3 | 4 | 5; /** The timeline's accessible name. Required — it is a landmark. */ label: string; lanes: readonly TimelineLane[]; /** * BCP-47, for the dates only. Omitted, `Intl` uses the runtime's * own — which is right on a personal machine and wrong on a * server, so a server render should pass one rather than let two * environments disagree about what "6 Apr" looks like. */ locale?: string; /** How many bars one lane may stack. Default 6. */ maxRowCount?: number; /** * The narrowest a day column may get before the axis is abandoned * for the Narrow View, as a multiple of the timeline's **own font * size**. Default 1.75. * * In em rather than pixels because every length in this token set * moves with the density axis: a floor pinned at 20px is generous * on a desktop and clips a two-digit date on the kiosk. Same * argument `Avatar` makes about a chip that stays 20px while the * prose around it scales. */ minColumnSize?: number; /** * The days on screen. **Both ends inclusive**, like every other * span in this component — a Monday-to-Sunday week is * `2026-04-06` to `2026-04-12`, which is seven columns, not eight. */ range: TimelineSpan; /** * The narrowest a day column may be before a bar's title wraps * onto up to three lines instead of clipping to one, as a * multiple of the timeline's **own font size**. Default 6. In em * for the reason `minColumnSize` is. */ wrapColumnSize?: number; }; /** * One horizontal lane per group, across a date axis, with anything * that lasts more than a day drawn as a **bar**. * * ## What it is for * * A month grid gives every day the same box, so a thing that runs * Tuesday to Friday is four separate entries and the fact that it is * *one* thing is lost. Worse, every cell is the same size no matter * how much is in it, so a grid cannot show that one person is * carrying most of the week. A lane can: bars keep their real * shape, and a lane with overlapping work stacks into rows and gets * **taller**. The asymmetry then reads from across the room, before * anybody counts anything. * * It is not a calendar. There is no event, no attendee and no * recurrence in this API, because Docket wants the same shape for * phases and Horizon wants it for a household — the library owns the * **shape** and the app owns the **data**. What a lane holds is a * `key`, a `label` and items with a `start`, an `end` and a `title`. * * ## `end` is INCLUSIVE, and that is the decision to know * * An off-by-one on a date range is invisible in both directions: a * bar one day short and a bar one day long are both bars. So it is * stated loudly here and again on the type — **`end` is the last day * the item covers**. `2026-04-06` to `2026-04-06` is one day, and a * seven-day week ends on its seventh day, not on the eighth. The * alternative (a half-open interval, the way `Date` ranges and * iCalendar's `DTEND` work) is the better model for *instants* and * the wrong one for a human writing down when the roofers are * coming: nobody says "Monday to Saturday" about a working week, so * an exclusive end would make every consumer add a day on the way in * and forget to on one of the paths. * [Decision](../../../docs/decisions/2026-09-18-a-timeline-span-ends-on-its-last-day-and-the-axis-folds-on-measurement.md). * * ## The Narrow View is a LIST, not a pan surface * * Thirty columns have no narrow layout, and the two obvious answers * are both wrong. Horizontal scrolling hides lanes behind a gesture * with no affordance and fights the trackpad's own back-navigation * — the same objection `Board` settled with *"no horizontal scroll, * ever"*. Squeezing thirty columns into 390px gives each day 13 * pixels, which is not a timeline, it is a texture. * * So below the threshold the axis is **abandoned** rather than * shrunk: each lane becomes a one-column list of its items, each one * a line of text with its span written out in words. Nothing is * hidden, nothing overflows, and the reading order is the one the * fleet's text-heavy lists already use. * * ## It folds on a MEASUREMENT, not a breakpoint * * There is no `@media` and no `cq-` in this component, and neither * could do the job: the question is not *how wide is the box* but * **how wide is one day**, which is the width divided by a number * that lives in the data. Thirty columns in 900px is a readable * month and three hundred and sixty five columns in the same 900px * is a grey smear, and no query can tell those apart. `Nav` folds * its bar on a measured width for the weaker version of this * reason; `useToolbarOverflow` writes the argument down. * * ⚠️ **It must not be sized by its own contents.** The component * reads its own box to decide what to draw in it, so a shrink- * wrapping parent makes the answer its own input. The root declares * `container-type: inline-size` — not to query it, the same reason * `Main` declares one — which forbids the element from being sized * by what is inside it and makes that loop unformable. In a story * this is what `align="stretch"` on the `StoryCell` is for. * * ## What it deliberately does not do * * No time of day: this is a **calendar-date** axis, so two things on * one Tuesday stack rather than sit at 09:00 and 14:00. No "now" * marker, no zoom, no drag to reschedule, no data. A range control * belongs to the page — `DatePicker` already is one — and a bar you * can drag is a write operation, which is a different component with * a different decision behind it. */ export declare const LaneTimeline: ({ className, emptyState, headingLevel, label, lanes, locale, maxRowCount, minColumnSize, range, wrapColumnSize, }: LaneTimelineProps) => ReactNode; //# sourceMappingURL=LaneTimeline.d.ts.map