import * as React from "react"; import type { DensityProp } from "../props/vocabulary/index.js"; export type RangeTimelineRow = { id: string; label: React.ReactNode; /** * First and last unit of the bar. `null` (either) means the row has no dates yet: no bar, a * muted "no dates" hint in its track, and no resize handles (gh#1189). */ start: number | null; end: number | null; startLabel: string; endLabel: string; /** * Nesting level, 0 for a top-level row. Rows arrive flat and depth-first (a parent, then its * descendants); a row is a parent when the row after it is deeper. The component draws the * indent (`--range-timeline-indent-width` per level) and the disclosure. */ depth?: number; /** * The bar's fill, a hex colour from data (the same contract as `Badge color`), e.g. the issue * status colour. The grips' ink flips black or white from the colour's luminance, the same pivot * `tenantTheme` uses for `--primary-foreground`; a value that is not a hex keeps the default ink. */ color?: string; /** De-emphasise a closed / done row's bar (`--range-timeline-bar-muted-alpha`), nothing else. */ muted?: boolean; /** * Mark the bar overdue: a destructive edge on its inline end that keeps `color`, plus a localized * screen-reader "overdue" so the colour is never the only carrier. */ overdue?: boolean; /** * Shown on hover and on keyboard focus of the bar (key, title, status, assignee, start → due). * The bar gains a focusable layer named by the row label; the grips stay separate buttons. */ tooltip?: React.ReactNode; /** * The PLANNED range (予定), drawn as a dashed ghost bar; `start` / `end` are then the ACTUAL range * (実績), drawn solid. Past the planned end the actual bar carries a destructive overrun segment; * ending before it, a small early marker. Both are also spoken. */ plan?: { start: number | null; end: number | null; } | null; /** * Text after an overrun segment. Default: the overrun in axis units, localized as days * ("+3日" / "+3d" / "+3 ngày"), since a Gantt's unit is the day. `false` shows none. */ varianceLabel?: React.ReactNode | false; /** * A muted note shown after the bar when the row has no `plan`, e.g. "baseline後に追加". Nothing * is drawn for a row without a plan unless this is set. */ planNote?: React.ReactNode; /** * Which grips this row offers when `onRangeChange` is set. Default `true` (both). A started-but-open * issue whose end is "today" passes `{ end: false }`, keeping the start grip. `false` removes both. */ editable?: boolean | { start?: boolean; end?: boolean; }; /** * What an undated row (no `start` / `end`) says in its track. Default: the localized "no dates" * hint. `false` says nothing: a pure context row (a phase heading, a group label). */ emptyHint?: React.ReactNode | false; /** * Spoken with a FINISH-ONLY row (`start: null`, `end` set): a finish was recorded without a known * start, drawn as a finish tick at `end`. Default: the localized "start unknown". */ startUnknownLabel?: string; /** * The row started but its completion is not recorded: the bar runs to `end` (pass the as-of * unit), its inline end fades out, and it has no end grip. */ openEnd?: boolean; /** Spoken with an `openEnd` bar. Default: the localized "completion not recorded". */ openEndLabel?: string; /** * `milestone` draws a diamond at `end` (`start` is ignored); its `plan` is a hollow diamond at * `plan.end`; overrun / early and their labels apply as for a bar; one grip moves it, reported * as `onRangeChange(id, "end", delta)`. */ shape?: "bar" | "milestone"; /** * What the ghost means for this row, e.g. "ベースライン" (a frozen baseline) or "予定" (the live * plan), used in the spoken overrun / early text. Overrides the timeline's `planLabel`. */ planLabel?: string; /** * Text after the early marker. Default: how early, localized as days ("-2日" / "-2d" / "-2 ngày"). * `false` shows none. */ earlyLabel?: React.ReactNode | false; /** Cancelled (中止): a hatched bar, distinct from `muted`, spoken, and without grips. */ cancelled?: boolean; /** * An accent on the row's inline start plus a spoken label, so colour is never the only cue: * `warning` (at risk, e.g. a late predecessor) or `critical` (on the critical path). */ emphasis?: "warning" | "critical"; }; /** * A dependency between two rows, drawn as an elbow connector over the body. `FS` (the default) * runs from the predecessor's finish to the successor's start; `SS` start to start, `FF` finish to * finish, `SF` start to finish. A milestone's endpoint is its diamond. */ export type RangeTimelineLink = { id: string; /** The predecessor row's id. */ from: string; /** The successor row's id. */ to: string; type?: "FS" | "SS" | "FF" | "SF"; /** * The schedule breaks this dependency: the connector turns destructive and its head gets a * focusable marker (with `label` in a tooltip), also listed for screen readers. */ violated?: boolean; label?: string; }; export type RangeTimelineProps = React.HTMLAttributes & { label: string; /** `muted` tints that column down the whole body — a non-working day, a closed period. */ columns: { label: string; units: number; muted?: boolean; }[]; bands?: { label: string; units: number; }[]; rows: RangeTimelineRow[]; today?: number | null; onRangeChange?: (id: string, edge: "start" | "end", delta: number) => void; /** * Rule the body as a grid: a line between rows, a line under the header, and a vertical line for * every column down the whole body. Default `true`, like `Calendar bordered`; `false` restores * the header-only ruling. Colour `--range-timeline-grid-color`, weight `--range-timeline-grid-width`. */ bordered?: boolean; /** * How wide ONE axis unit is — the canonical density vocabulary, the same three steps * `DataTable density` uses. `default` (56px/day) is the shipped width, so an existing Gantt * does not move; `compact` (42px/day) fits a third more days on the same screen; `comfortable` * (70px/day) spreads them out. It moves the column width ONLY — row height, bar height and the * label column are identical at every step. Retune a step with * `--range-timeline-unit-width-{compact,default,comfortable}`. */ density?: DensityProp; /** * Controlled ids of the EXPANDED parent rows (same spelling as `Tree expandedValues`). A folded * parent hides every descendant row — label and bar. */ expandedValues?: readonly string[]; /** Uncontrolled initial expanded parents. Omitted: every parent starts expanded. */ defaultExpandedValues?: readonly string[]; /** Fires with the next expanded parent ids, for controlled and uncontrolled timelines alike. */ onExpandedValuesChange?: (values: string[]) => void; /** * Keep the axis header (bands + ticks) on screen while the PAGE scrolls a long schedule — antd * Table `sticky`. `offsetHeader` is the distance in px from the top of the scrolling viewport, * e.g. the height of a fixed app topbar. The header then scrolls horizontally with the body. * Needs no clipping ancestor (e.g. `Card`, which is `overflow: hidden`) between it and the page * scroller. antd's `offsetScroll` / `getContainer` (a sticky horizontal scrollbar) are not ported. */ sticky?: boolean | { offsetHeader?: number; }; /** * Let the user resize the label column by dragging a divider in the header (gh#1189): a * `role="separator"` with aria-valuenow/min/max, ArrowLeft/Right (Shift = a bigger step), * Home/End, RTL-aware. `true` uses min 160 / max 640 px. The width lands on * `--range-timeline-label-width`, so the token stays the default until the user drags. */ resizableLabel?: boolean | { min?: number; max?: number; }; /** Controlled label-column width in px. Persist it per user from `onLabelWidthChange`. */ labelWidth?: number; /** Uncontrolled initial label-column width in px. Omitted: the token's width. */ defaultLabelWidth?: number; /** Fires with the next label-column width (px) while dragging and on every key step. */ onLabelWidthChange?: (width: number) => void; /** * A new range is loading while the previous rows stay on screen: dims the body, draws an * indeterminate line under the header and sets `aria-busy` (gh#1189). */ busy?: boolean; /** * What the plan ghost means on every row (a row's own `planLabel` wins), e.g. "ベースライン", * used in the spoken overrun / early text: "ベースラインより3日遅れ". */ planLabel?: string; /** * Dependency connectors between rows (FS / SS / FF / SF). Only rows that are visible and dated at * the needed endpoint are connected; an endpoint outside the range is clamped to the edge. */ links?: RangeTimelineLink[]; }; /** A horizontal range axis. Units and labels are data; the design system owns all geometry. */ export declare const RangeTimeline: React.ForwardRefExoticComponent & { label: string; /** `muted` tints that column down the whole body — a non-working day, a closed period. */ columns: { label: string; units: number; muted?: boolean; }[]; bands?: { label: string; units: number; }[]; rows: RangeTimelineRow[]; today?: number | null; onRangeChange?: (id: string, edge: "start" | "end", delta: number) => void; /** * Rule the body as a grid: a line between rows, a line under the header, and a vertical line for * every column down the whole body. Default `true`, like `Calendar bordered`; `false` restores * the header-only ruling. Colour `--range-timeline-grid-color`, weight `--range-timeline-grid-width`. */ bordered?: boolean; /** * How wide ONE axis unit is — the canonical density vocabulary, the same three steps * `DataTable density` uses. `default` (56px/day) is the shipped width, so an existing Gantt * does not move; `compact` (42px/day) fits a third more days on the same screen; `comfortable` * (70px/day) spreads them out. It moves the column width ONLY — row height, bar height and the * label column are identical at every step. Retune a step with * `--range-timeline-unit-width-{compact,default,comfortable}`. */ density?: DensityProp; /** * Controlled ids of the EXPANDED parent rows (same spelling as `Tree expandedValues`). A folded * parent hides every descendant row — label and bar. */ expandedValues?: readonly string[]; /** Uncontrolled initial expanded parents. Omitted: every parent starts expanded. */ defaultExpandedValues?: readonly string[]; /** Fires with the next expanded parent ids, for controlled and uncontrolled timelines alike. */ onExpandedValuesChange?: (values: string[]) => void; /** * Keep the axis header (bands + ticks) on screen while the PAGE scrolls a long schedule — antd * Table `sticky`. `offsetHeader` is the distance in px from the top of the scrolling viewport, * e.g. the height of a fixed app topbar. The header then scrolls horizontally with the body. * Needs no clipping ancestor (e.g. `Card`, which is `overflow: hidden`) between it and the page * scroller. antd's `offsetScroll` / `getContainer` (a sticky horizontal scrollbar) are not ported. */ sticky?: boolean | { offsetHeader?: number; }; /** * Let the user resize the label column by dragging a divider in the header (gh#1189): a * `role="separator"` with aria-valuenow/min/max, ArrowLeft/Right (Shift = a bigger step), * Home/End, RTL-aware. `true` uses min 160 / max 640 px. The width lands on * `--range-timeline-label-width`, so the token stays the default until the user drags. */ resizableLabel?: boolean | { min?: number; max?: number; }; /** Controlled label-column width in px. Persist it per user from `onLabelWidthChange`. */ labelWidth?: number; /** Uncontrolled initial label-column width in px. Omitted: the token's width. */ defaultLabelWidth?: number; /** Fires with the next label-column width (px) while dragging and on every key step. */ onLabelWidthChange?: (width: number) => void; /** * A new range is loading while the previous rows stay on screen: dims the body, draws an * indeterminate line under the header and sets `aria-busy` (gh#1189). */ busy?: boolean; /** * What the plan ghost means on every row (a row's own `planLabel` wins), e.g. "ベースライン", * used in the spoken overrun / early text: "ベースラインより3日遅れ". */ planLabel?: string; /** * Dependency connectors between rows (FS / SS / FF / SF). Only rows that are visible and dated at * the needed endpoint are connected; an endpoint outside the range is clamped to the edge. */ links?: RangeTimelineLink[]; } & React.RefAttributes>;