import type { SchedulerModule, SchedulerRuntime } from '../scheduler-runtime'; /** * The timeline header: one row per configured band, plus the slot row. * * ## Why it is not part of the grid's own header renderer * * The grid's header is a column header -- it exists to name and manipulate * `ColumnDef`s. A timeline header names *time*, has a variable number of stacked * rows, and its cells do not correspond to columns at all. Modelling slots as * columns would push thousands of synthetic `ColumnDef`s through the grid's * pipeline on every view change; instead the plugin gives this class a container * inside the header region and it paints into it directly. * * ## What makes it cheap * * The band rows are rebuilt only when the {@link Timeline} reference changes, * which the timeline engine guarantees happens only on a genuine view or range * change -- a scroll never touches them. The slot row is virtualized like the * bars are, and panning is a single `translateX` on one wrapper rather than a * reposition of every cell, so a horizontal scroll costs exactly one style write * no matter how many slots exist. Cells are pooled and every write is diffed * against the last value, so a cell that scrolls out and back in unchanged * writes nothing at all. * * Nothing here measures the DOM: widths and offsets come from the axis, and the * band rows are sized as a percentage of the container, so the header never * forces layout. */ export declare class SchedulerHeader implements SchedulerModule { private readonly runtime; private readonly hostEl; /** The panned wrapper. Created on first render and reused for the header's lifetime. */ private inner; /** The virtualized slot row, the last child of {@link inner}. */ private slotRow; /** Slot cells currently mounted, keyed by slot index. */ private readonly slotLive; /** Band cells currently mounted, flat across every band, for wholesale recycling. */ private readonly bandCells; /** Retired cells available for reuse, shared by the bands and the slot row. */ private readonly cellPool; /** Per-cell diff state. See {@link CellState}. */ private readonly cellStates; /** * One `Intl.DateTimeFormat` per unit, built on first use. * * Constructing a formatter is expensive -- it resolves a locale and compiles a * pattern -- and doing it per cell would dominate the cost of a view change. * `null` marks a unit whose label is computed arithmetically (week, quarter) * and therefore needs no formatter, so the miss is not retried every time. */ private readonly formatters; /** Local midnights of configured holidays, for O(1) lookup per slot. */ private readonly holidayDays; /** Weekend day indices, hoisted out of the config for the per-slot test. */ private readonly weekendDays; /** Whether any non-working shading is wanted at all. */ private readonly shadeNonWorking; /** * Reused `Date` for calendar field extraction. * * `getDay()` and `getDate()` need a `Date`, and allocating one per slot per * view change is pure garbage; `setTime` on a single instance is the same * arithmetic without the allocation. Safe because every use is synchronous and * the value is consumed before the next `setTime`. */ private readonly scratchDate; /** Timeline the current DOM was built from. Reference equality is the rebuild trigger. */ private builtTimeline; /** Slot window the slot row currently covers. */ private builtWindow; /** Last width written to {@link inner}. */ private builtWidth; /** Last horizontal offset written, so a repeated scroll event writes nothing. */ private scrollX; /** * @param runtime - Shared scheduler state; the header reads the timeline and * the non-working config and never mutates either. * @param hostEl - Container the plugin created inside the grid's header * region. The header takes ownership of its children but not of the element * itself, so the plugin remains responsible for placing and sizing it. */ constructor(runtime: SchedulerRuntime, hostEl: HTMLElement); /** * Builds or refreshes the header. * * Called once per rendered frame by the plugin. Almost every call is the cheap * path: the timeline is unchanged, so only the slot row is re-virtualized, and * if the visible slot window is also unchanged the method writes nothing. */ render(): void; /** * Pans the header to match the body's horizontal scroll. * * Called from the plugin's scroll listener, which fires synchronously during * the scroll -- ahead of the animation frame the grid books -- so this must stay * a single composited write. It is, and it is skipped entirely when the offset * has not moved, which matters because scroll events fire far more often than * the position actually changes on trackpads and momentum scrolling. * * When the pan uncovers slots outside the built window, a render is requested * rather than performed: building cells during a scroll event would extend the * event handler, whereas requesting one lets the work land in the frame the * grid was already going to render. */ setScrollX(px: number): void; /** * Releases the header's DOM and caches. * * The host element itself is left in place: the plugin created it and may be * reusing it, and removing another owner's element from its parent is exactly * the kind of cross-ownership teardown that leaves a grid with a hole in it. */ destroy(): void; /** Creates the panned wrapper on first render. */ private ensureInner; /** * Rebuilds every band row and resets the slot row. * * Only reached on a view or range change. Band cells are not virtualized * because a band is coarse by construction -- the outer rows of a timeline are * months, quarters or years, so even a decade-long range produces tens of * cells, and virtualizing them would cost more bookkeeping than it saves. */ private rebuild; /** Builds one band row with all of its cells. */ private buildBand; /** * Mounts exactly the slot cells the viewport can see, plus the buffer. * * Keyed by slot index rather than by position, so a cell that stays visible * across a scroll keeps its element *and* its cached values -- which is what * makes a small scroll write only the handful of cells that genuinely entered * or left. */ private renderSlotRow; /** Slot containing the current instant, or `-1` when now falls outside the timeline. */ private todaySlot; /** * Whether a slot is a weekend or a configured holiday. * * Only asked of day-or-finer units: a month or quarter slot spans both working * and non-working days, so shading the whole cell would assert something * false. Coarse units therefore always report `false` rather than reporting * whatever their first instant happens to be. */ private isNonWorking; /** Takes a cell from the pool or creates one, with the slot modifier applied as asked. */ private acquireCell; /** Detaches a cell and returns it to the pool. */ private releaseCell; /** Builds an empty cell along with its diff state. */ private createCell; /** * Writes a cell's geometry, label and state modifiers, skipping every value * that already holds. * * Uses `left`/`width` rather than a transform, unlike the event bars: a header * cell is inside a wrapper that is itself transformed, so the cell's own * position is static from the compositor's point of view and never re-written * on a scroll. The bars have no such wrapper per row, which is why they pay for * a transform each and these do not. */ private applyCell; /** * Formats one cell's label for a unit. * * Each unit gets the shortest label that stays unambiguous at its own * granularity, because header cells are as narrow as the slot width and an * ellipsised label communicates nothing. Weeks and quarters are computed * rather than formatted: no locale exposes an ISO week number or a quarter * label through `Intl` at the precision this needs. */ private formatLabel; /** Returns the cached formatter for a unit, building it on first use. */ private getFormatter; } //# sourceMappingURL=scheduler-header.d.ts.map