import{type PropertyValues,type TemplateResult}from'lit';import{LyraElement}from'../../../internal/lyra-element.js';import{type CalendarDay}from'./calendar-grid.js'; /** * Which matrix label band, if any, is frozen while the grid scrolls. * * One closed set rather than a boolean or a pair of booleans, because freezing one axis is a real * request on its own: a wide matrix needs the row gutter to survive horizontal scrolling, a tall one * needs the column band to survive vertical scrolling, and the two are independent. A boolean cannot * express that at all, and a `sticky-row-labels`/`sticky-col-labels` pair spends two attributes and * four states on it while leaving no single reflected value to select on in CSS. The `rows`/`cols` * spelling matches the vocabulary the rest of this component already uses (`rowLabels`, * `row-label-width`, `colLabels`, `col-label-height`) rather than introducing axis letters. */ export type LyraHeatmapStickyLabels='none'|'rows'|'cols'|'both'; /** * A linear RGB ramp cannot contain more than 256 visually distinct integer * steps on any channel. Keeping the bucket count within that bound avoids an * untrusted attribute/property value turning the ramp into an unbounded * allocation without discarding any useful color resolution. */ export declare const MAX_BUCKET_COUNT=256; /** Total canonical matrix cells and maximum caller-supplied legend/annotation entries. */ export declare const MAX_HEATMAP_CELLS=10000;export declare const MAX_HEATMAP_DECORATIONS=256;export declare const MAX_ACCESSIBLE_HEATMAP_CELLS=400; /** The heatmap's two layout modes — a plain row/col grid or a GitHub-style calendar grid. */ export type HeatmapMode='matrix'|'calendar'; /** The value-to-color mapping applied within each mode — linear or square-root compressed. */ export type HeatmapScale='linear'|'sqrt'; /** Matrix-specific heatmap input. Collections are caller-owned and are projected into a bounded * immutable render model on update; reassign `data` after changing them. */ export interface HeatmapMatrixData{readonly kind:'matrix';readonly rowLabels:readonly string[];readonly colLabels:readonly string[];readonly values:readonly(readonly number[])[];} /** Calendar-specific heatmap input. Keeping its geometry and label callbacks in this branch makes * it impossible for stale calendar configuration to coexist with matrix data. */ export interface HeatmapCalendarData{readonly kind:'calendar'; /** Calendar records keyed by ISO `date`; invalid and later duplicate dates are omitted * first-wins before every count, scale, paint, selection, focus, and event path. */ readonly days:readonly CalendarDay[];readonly firstDayOfWeek?:number;readonly columnX?:(index:number)=>number;readonly rowY?:(weekday:number)=>number; /** Width, in CSS px, of the weekday-label gutter, or `'auto'` to measure the widest rendered * weekday label. Auto never shrinks below the built-in 28px gutter and is capped at 40% of the * host width. Unset preserves the original 28px geometry. */ readonly weekdayLabelWidth?:number|'auto';readonly weekdayLabelText?:(jsWeekday:number)=>string|undefined;readonly monthLabelText?:(jsMonth:number,year:number)=>string|undefined;} /** Discriminated input model for ``. */ export type HeatmapData=HeatmapMatrixData|HeatmapCalendarData; /** A cell cursor in matrix mode. */ export interface MatrixCellPos{row:number;col:number;} /** A cell cursor in calendar mode — a grid position, not necessarily one with * a matching entry in `days` (see `calendarCellAt()`). */ export interface CalendarCellPos{week:number;weekday:number; /** * The real ISO `yyyy-mm-dd` date this grid position falls on, so a `cellText`/`cellColor`/ * `cellInteractive` callback can key off the date without reconstructing the grid's own * `firstWeekStart + week * 7 + weekday` arithmetic. Always populated — including for a **gap** * position with no matching entry in `days` at all, which still sits on a real calendar day * (that case reports `NaN` in signed mode and the legacy `-1` no-data value otherwise). */ date:string;} /** * A marker drawn as a stroked ring over a matching cell (matrix mode: * `row`/`col`; calendar mode: `date` — whichever pair matches the active * `mode`; the other fields are simply ignored). An optional `label` * additionally surfaces the annotation in the legend. */ export interface HeatmapAnnotation{row?:number;col?:number;date?:string;label?:string;} /** * One entry of a discrete legend key, supplied via `legendStops`. Purely a legend description — * it never feeds back into the cell color ramp (see `LyraHeatmap.legendStops`). */ export interface HeatmapLegendStop{ /** Domain value this stop represents; used for the rendered label. */ value:number; /** * Any CSS color — typically whatever the consumer's own `cellColor` returns for `value`. * Omit it, pass an empty string, or pass an invalid/non-color value for a **caption-only** stop: * the entry then renders its * `[part="legend-stop-label"]` alone, with no `[part="legend-swatch"]` element in the DOM at * all, so a leading "0" / trailing "more" caption around a run of colored stops doesn't leave * an empty swatch box in the row. */ color?:string; /** Optional label override; defaults to the component's own numeric formatting of `value`. */ label?:string; /** * Set to `false` to exclude this stop from `warnOnLegendRampMismatch()`'s `colorSteps` agreement * check, for a real, distinctly-colored swatch that is intentionally not part of the sequential * ramp -- e.g. a calendar heatmap's fixed neutral "no data" color shown alongside an N-step * ramp. Defaults to `true`. A caption-only stop (no `color`) is already excluded regardless of * this flag, since it describes no color to compare. */ partOfRamp?:boolean;} /** A single cell to mark as persistently selected -- `row`/`col` in matrix mode, `date` in * calendar mode (whichever pair matches the active `mode`; the other field is ignored), * mirroring `HeatmapAnnotation`'s own row/col/date shape. */ export interface HeatmapSelectedCell{row?:number;col?:number;date?:string;} /** Origin of a controlled multiple-selection proposal. */ export type HeatmapSelectionSource='pointer'|'keyboard'|'row'|'column'; /** Frozen proposed selection. Assign selectedCells to accept it; property writes are silent. */ export interface HeatmapSelectionChangeDetail{readonly selectedCells:readonly Readonly[];readonly source:HeatmapSelectionSource;} /** * Parses a strict `#rgb`/`#rgba`/`#rrggbb`/`#rrggbbaa` hex string into an * `[r, g, b, a]` quadruple (`a` in `[0, 1]`, defaulting to `1` for the * 3/6-digit alpha-less forms), or `null` if `hex` isn't one (rather than * silently coercing an unparsable string to `0` via * `Number.parseInt(..., 16)` returning `NaN`). */ export declare function hexToRgb(hex:string):[number,number,number,number]|null; /** * Normalizes a bucket count to the safe, renderable range. Non-finite values * restore the public default; finite values are floored and clamped to * `[2, MAX_BUCKET_COUNT]`. Flooring keeps the ramp array's length in exact * agreement with the count used by `quartileBucket()`. */ export declare function normalizeBucketCount(bucketCount:number):number; /** * Resolves any syntactically valid CSS `` — hex, `rgb()`, `hsl()`, * `oklch()`, a named color, etc. — to an `[r, g, b, a]` quadruple (`a` in * `[0, 1]`, `1` for an opaque input). A translucent input (e.g. * `rgba(255,255,255,.028)`, a common way to key a color ramp off a themed * "quiet surface" token) round-trips its alpha rather than silently * resolving to the fully opaque equivalent. * * Hand-rolling a parser for every CSS color syntax is unnecessary and * error-prone (a naive hex-only parser silently turns an unrecognized format * into `NaN` -> `0`, i.e. solid black). The canvas 2D context already * implements the full CSS color grammar via its `fillStyle` setter, so this * normalizes through that instead. Assigning an unparsable string to * `fillStyle` is a spec'd no-op (the previous value is kept, it never * throws), so a sentinel round-trip is used to detect that case and fall * back to `fallbackHex` (with a one-time development diagnostic) instead of * silently drawing the wrong color. */ export declare function resolveRgb(color:string,fallbackHex:string,ownerDocument?:Document):[number,number,number,number];export type LyraHeatmapCellClickDetail={date:string;value:number;}|{row:number;col:number;value:number;};export type LyraHeatmapMatrixGeometryChangeDetail={padLeft:number;padTop:number;cellSize:number; /** Custom painted bounds; omitted for the default one-pixel separators and square corners. */ cellWidth?:number;cellHeight?:number;cellRadius?:number;};export interface LyraHeatmapEventMap{'lr-cell-click':CustomEvent;'lr-matrix-geometry-change':CustomEvent;'lr-selection-change':CustomEvent;} /** The raster snapshot format returned by `LyraHeatmap.exportData()`. */ export type LyraHeatmapExportFormat='png'; /** * `` — a Canvas heatmap with a DPR-aware, resize-aware redraw * loop. Its discriminated `data` property selects one of two projections: * * - `{ kind: "matrix", rowLabels, colLabels, values }` (default): a labeled matrix. `-1` * (or any non-finite value) is treated as "no data". `scale="sqrt"` * compresses the ramp via `sqrtStep()` so one heavy cell doesn't wash out * the rest; the default `"linear"` scale maps linearly instead. * - `{ kind: "calendar", days, firstDayOfWeek?, columnX?, rowY?, ... }`: a GitHub-style * weekday x week grid. `scale` governs its * bucketing too: the default `"linear"` buckets by `quartileBucket()` * (today's original behavior, unchanged); `"sqrt"` instead compresses via * the same `sqrtStep()` magnitude compression matrix mode uses, so one * heavy day doesn't wash out the rest. As in matrix mode, a cell whose * `value` is negative or non-finite is treated as "no data" rather than * being bucketed — as is a grid position with no matching entry in `days` * at all (a gap in a sparse calendar). * * `fitToWidth` divides the host's measured width across the grid in either * mode; `maxCellSize`/`minCellSize` bound the result, so a sparse grid in a * wide pane cannot inflate into a few giant blocks and a year calendar in a * narrow one cannot collapse into hairlines. Both are ignored while * `fitToWidth` is unset (an explicit `cellSize` is an exact request), and the * canvas is sized from the *clamped* size — a capped grid leaves the host's * remaining width unfilled rather than stretching to it. * * The sequential color ramp's endpoints are read from the * `--lr-heatmap-scale-lo`/`-hi` custom properties (declared in * `heatmap.styles.ts`) so hosts can retheme it — canvas can't consume * `var()` directly, so they're resolved once per draw via * `getComputedStyle`, then normalized to RGB by `resolveRgb()` (any valid * CSS color syntax, not just hex — see its doc comment). Invalid authored colors keep the * default ramp endpoint and issue a deduplicated diagnostic only in development. * * Every cell is independently addressable: a `pointermove` hit test over the * canvas shows `[part="tooltip"]` with that cell's label + value (hidden on * `pointerleave`); the canvas is a named `role="application"`, `tabindex="0"` control with * arrow-key roving focus (a stroked ring redrawn over the focused cell on every draw, plus a * shared light-DOM polite status announcement — avoids a * DOM-node-per-cell overlay, which would be hundreds of nodes for a year * calendar); and a click, or Enter/Space on the focused cell, fires * `lr-cell-click`. `annotations` additionally strokes a ring around * specific cells (e.g. to call out an anomaly), each one optionally * surfaced in the legend too via `[part="legend-annotation"]`. Focus-only updates restore * intersected neighboring fills and overlays without redrawing the entire canvas. * * Both grid modes deliberately retain physical LTR geometry under `dir="rtl"`: * matrix column 0 and calendar week 0 remain at the physical left. ArrowLeft * and ArrowRight therefore retain their physical previous/next movement rather * than swapping under RTL, matching the grid the user sees. * * In calendar mode every cell position handed to `cellText`, `cellColor` and * `cellInteractive` is a `CalendarCellPos` carrying the resolved ISO * `yyyy-mm-dd` `date` alongside `week`/`weekday` — including for a grid * position with no entry in `days` at all — so a callback can key off the * date without re-deriving the grid's own anchor arithmetic. * * `legendStops` swaps the legend's two-endpoint gradient bar for a discrete * key of swatches, so a consumer whose `cellColor` callback paints an * entirely different domain than the `--lr-heatmap-scale-lo`/`-hi` ramp can * keep the built-in legend (labels, number formatting, annotation entries) * instead of hiding `[part="legend"]` and hand-rolling swatches. It is * presentation only — it never feeds back into the cell colors. * * Set `accessibleCells` when cells need persistent DOM semantics for * assistive technology. The opt-in semantic grid virtualizes native buttons to a bounded * window while retaining full row/column counts, complete arrow navigation, * localized `aria-label`, and explicit `aria-selected` state * derived from the controlled `selectedCell` property; the canvas remains the * visual rendering surface underneath. When grid data refreshes while one of those buttons owns * focus, its semantic matrix coordinate or calendar date remains the sole roving stop; removal * clamps to the nearest survivor, or to the stable heatmap base when no interactive cells remain. * * `stickyLabels` freezes a matrix label band against the grid's own scrolling — `'rows'` pins the * row-label gutter through horizontal scrolling, `'cols'` pins the column-label band through * vertical scrolling, `'both'` pins both. The frozen band is repainted into its own layer from the * same `matrixGeometry` the cells were painted with in the same pass, so it tracks a * `row-label-width`/`col-label-height` `"auto"` re-resolution instead of hardcoding it. The default * `'none'` renders exactly what it always did: one canvas, no scrollport. * * Everything positioned in canvas coordinates moves into that scrollport with the cells. The hover * tooltip renders inside it, so it stays on the cell it describes through a scroll instead of * drifting by the scroll offset; since `overflow: auto` there clips whatever leaves the * scrollport, it is also kept inside the visible window — clamped along the inline axis, and * flipped to below its cell when a frozen band leaves no room above. Arrow-key navigation scrolls * the focused cell into that window, clear of the frozen bands: the canvas is the roving tab stop, * its focus ring is painted into the bitmap, and it calls `preventDefault()` on the arrows, so * without that scroll a keyboard user has no way at all to bring the focused cell back into view. * * Calendar `data.columnX` overrides the x-origin computed for each * week column — drawing, hit-testing, the focus ring, and month-label * positioning all consult it consistently, so a consumer can pixel-align a * calendar's week columns with a sibling chart's coordinate system. Unset * (the default) keeps the original evenly-spaced formula. `data.rowY` is its * calendar-mode vertical analogue — overrides the y-origin computed for each * weekday row, consulted consistently by drawing, hit-testing, and the focus * ring via the private `rowYFor()` helper (mirroring `columnXFor()` exactly). * * `data.firstDayOfWeek` (calendar only, default `0`/Sunday) * anchors the calendar grid at a different weekday — `0`-`6`, same * numbering as `CalendarCellPos.weekday` (`0` Sunday .. `6` Saturday) — * threaded into `buildCalendarGrid()`. * `data.weekdayLabelWidth` controls that mode's weekday-axis gutter independently of the matrix * `rowLabelWidth`: a CSS-pixel number pins it, while `'auto'` measures localized or overridden * weekday labels. Labels that still exceed the resolved gutter are ellipsized rather than clipped. * * `cellSize`/`fitToWidth` (previously matrix-mode only) also drive calendar * mode's per-cell size: unset, calendar mode keeps today's original 11px * cell size unchanged; explicitly set, the same fixed size (or, with * `fitToWidth`, the same host-width-derived size matrix mode already * supports) governs calendar mode's grid too. * * Full canvas redraws are suspended while the host is outside the viewport. Data, locale, theme, * resize, and DPR invalidations remain pending and coalesce into one redraw when the heatmap * intersects again; environments without `IntersectionObserver` retain eager drawing. * Matrix work is capped at `MAX_HEATMAP_CELLS`; calendar input/span and decoration collections * have corresponding exported ceilings. A localized `[part="projection-limit"]` disclosure is * attached whenever canonicalization truncates caller input. * Public data records and decoration collections are clone-owned, bounded readonly snapshots. * Create and reassign a new record or array after changing `data`, `annotations`, `legendStops`, * or `colorSteps`. * * @customElement lr-heatmap * @event lr-cell-click - Fired on click, or Enter/Space on the * focused/hovered cell. `detail: { row, col, value }` in matrix mode, * `detail: { date, value }` in calendar mode. `cellText` overrides the * localized matrix row/column/value or calendar date/value template used for both the hover * tooltip and the keyboard live-region announcement. Use the callback for application-specific * wording that is not represented by the locale catalog. * `cellColor` overrides a cell's ramp-computed color entirely for an exact value. * @event lr-matrix-geometry-change - Fired after a matrix-mode draw pass whose resolved * `matrixGeometry` (`padLeft`/`padTop`/`cellSize`) differs from the previous draw -- e.g. after * `row-label-width="auto"`/`col-label-height="auto"` resolves against new label content or a * resize. `detail` is the same object `matrixGeometry` returns. Never fired in calendar mode. * @event lr-selection-change - Non-cancelable controlled multiple-selection proposal with frozen * `HeatmapSelectionChangeDetail { selectedCells, source }`. Click/Enter/Space toggles, Shift+arrows * extends a rectangle, Shift+Space toggles a row and Ctrl/Meta+Space toggles a column. Pointer drag * paints or erases with a transient preview and emits once on release; cancellation discards it. * Assign the proposed array to `selectedCells` to accept. Programmatic assignments are silent. * @slot legend - Custom legend content rendered inside the built-in legend row. Nothing is * rendered, and the slot itself is absent, while `withoutLegend` is set. * @csspart base - The heatmap wrapper. * @csspart canvas - The heatmap canvas. * @csspart grid - The scrollport wrapping the canvas while `stickyLabels` freezes an axis; absent otherwise. * @csspart row-labels - The frozen row-label gutter, rendered while `stickyLabels` is `rows` or `both`. * @csspart col-labels - The frozen column-label band, rendered while `stickyLabels` is `cols` or `both`. * @csspart cells - The opt-in per-cell accessibility overlay. * @csspart cell - An opt-in native button for one matrix or calendar cell. * @csspart tooltip - The hover tooltip, positioned over the hovered cell. It renders inside * `[part="grid"]` while `stickyLabels` freezes an axis (so it scrolls with the cells) and as a * `[part="base"]` child otherwise. * @csspart live-region - An aria-hidden shadow mirror of the keyboard announcement; the actual * announcement uses the shared light-DOM polite sink. * @csspart projection-limit - Localized assistive disclosure for bounded projections. * @csspart legend - The color legend. The whole row, its slot included, is absent from the DOM * while `withoutLegend` is set. * @csspart legend-lo - The low legend endpoint (omitted when `legendStops` is supplied). * @csspart legend-hi - The high legend endpoint (omitted when `legendStops` is supplied). * @csspart legend-stop - One discrete `legendStops` entry — swatch plus label. * @csspart legend-swatch - The color swatch of one `legendStops` entry. Not rendered at all for a caption-only stop (one with no `color`). * @csspart legend-stop-label - The text of one `legendStops` entry. * @csspart legend-value-label - The trailing `valueLabel` caption that closes the legend row, in both the gradient and the `legendStops` branch. * @csspart legend-annotation - An annotation label. * @cssprop [--lr-heatmap-scale-lo=var(--lr-color-brand-quiet)] - Low endpoint of the sequential color ramp. * @cssprop [--lr-heatmap-scale-hi=var(--lr-color-brand)] - High endpoint of the sequential color ramp. * @cssprop [--lr-heatmap-no-data-fill=var(--lr-color-no-data)] - Fill for cells with no value. * @cssprop [--lr-heatmap-label-font] - Font for axis/legend labels drawn on the canvas. * @cssprop [--lr-heatmap-tooltip-bg=var(--lr-color-surface)] - Hover tooltip background. * @cssprop [--lr-heatmap-tooltip-text=var(--lr-color-text)] - Hover tooltip text color. * @cssprop [--lr-heatmap-focus-ring-color=var(--lr-focus-ring-color)] - Focus ring around a focused cell. * @cssprop [--lr-heatmap-annotation-color=var(--lr-color-danger)] - Border color for an annotated cell. * @cssprop [--lr-heatmap-selected-color=var(--lr-color-success)] - Border color for the selected cell. * @cssprop [--lr-heatmap-sticky-label-bg=var(--lr-color-surface)] - Backdrop painted under a frozen `stickyLabels` band. Must be opaque: it covers the same labels the scrolling canvas painted underneath it. * @cssprop [--lr-heatmap-grid-max-block-size=none] - Block-size ceiling of the `stickyLabels` scrollport. A frozen column band only stays behind once the grid actually scrolls vertically. * @cssprop [--lr-heatmap-color-steps-gradient=linear-gradient(to right, var(--lr-heatmap-scale-lo), var(--lr-heatmap-scale-hi))] - Gradient painted on the continuous legend bar. Set on the host by the component itself while `colorSteps` is supplied AND the legend is rendered, and removed again when either stops being true -- `withoutLegend` takes the whole legend row out of the DOM, and the legend bar is this property's only reader; the fallback is the two-endpoint scale ramp. * @status stable * @since 4.0.0 */ export declare class LyraHeatmap extends LyraElement{protected static readonly immutableEventDetails:readonly string[];protected static readonly ownedCollectionProperties:readonly string[]; /** `data` is a single opaque record (row/col labels plus a `values` matrix), not an item * sequence -- and a legitimately large matrix (tens of thousands of cells) can exceed the * generic snapshot machinery's node budget, which would otherwise silently replace the whole * object with an empty one. `rebuildCanonicalMatrixData()` already does this component's own * bounding (`MAX_HEATMAP_CELLS`) against the live object, so opting out of the generic * snapshot/freeze here loses no safety. */ protected static readonly identityCollectionObjectProperties:readonly string[];static styles:import("lit").CSSResultGroup[];static get observedAttributes():string[]; /** All mode-specific input. Reassign this property after changing caller-owned collections. */ data:HeatmapData;private get effectiveMode(); /** * Width, in CSS px, of the matrix row-label gutter, or `'auto'` to measure the widest label and * size the gutter to fit (never below the built-in 60px, never above 40% of the host's width, so * one long label cannot squeeze out the cells it exists to describe). * * Unset keeps the built-in 60px. That default is deliberate: auto-sizing every existing heatmap * would silently reflow charts whose labels already fit, which is a bigger change than the * clipping it fixes. Opt in per chart, or pin an exact figure. * * Independently of this, a row label too wide for the resolved gutter is now truncated with an * ellipsis instead of being clipped mid-glyph by whatever is painted beside it -- clipping read * as a rendering fault, truncation reads as "there is more here". `cellText` still carries the * full label to the tooltip and the keyboard announcement either way. * * Calendar mode is unaffected; use `data.weekdayLabelWidth` for its weekday gutter. */ rowLabelWidth?:number|'auto'; /** * Height, in CSS px, of the matrix column-label band, or `"auto"` to measure the labels and size * the band to fit them (never below the built-in 20px, and bounded above by a sanity ceiling so a * pathological label cannot produce an absurd canvas). Under a non-zero * `colLabelRotation` the measurement projects each label's width through the rotation, which is * what makes a rotated axis usable without hand-tuning a magic number. * * Unset keeps the built-in 20px, for the same reason `rowLabelWidth` does: auto-sizing every * existing heatmap would silently reflow charts whose labels already fit. */ colLabelHeight?:number|'auto'; /** * Rotation, in degrees, applied to matrix column labels. Unset (or `0`) paints them horizontally * exactly as before. In a dense matrix the per-column width is far narrower than a typical label, * so horizontal labels collide with their neighbours; `45` or `90` is the standard remedy. * * Each label is rotated about an anchor at its own column's centre, with the label's *end* at the * anchor, so it leans up and back over the columns to its left and the last column's label cannot * overflow the canvas. Values outside `[0, 90]` clamp into it and non-finite values normalize to * `0`; a rotation is not a coordinate a caller can usefully be surprised by. * * Pair with `colLabelHeight="auto"` to have the band size itself to the rotated extent. * * Not mirrored under `dir="rtl"`: both grid modes deliberately retain physical LTR geometry (see * the class doc), and leaning one axis' labels the other way while the grid itself stays physical * would be incoherent. */ colLabelRotation?:number;private _stickyLabels; /** * Freezes a matrix label band against the grid's own scrolling, instead of leaving it baked into * the scrolling bitmap. `'rows'` pins the row-label gutter so it survives horizontal scrolling, * `'cols'` pins the column-label band so it survives vertical scrolling, `'both'` pins both, and * the default `'none'` renders exactly what this component rendered before the option existed: * one canvas, no scrollport, no extra elements. * * Labels and cells share one bitmap, so a band cannot be `position: sticky` on its own; a tall * matrix therefore scrolled its column header away and left the columns unidentifiable. Setting * this repaints the requested band into its own layer, in the same draw pass and from the same * resolved `matrixGeometry` the cells were painted with, so the two cannot drift under scroll, a * resize, a DPR change, or a `rowLabelWidth`/`colLabelHeight` `"auto"` re-resolution. That last * one is the point: a hand-rolled light-DOM mirror had to hardcode the gutter width, which made * it mutually exclusive with `row-label-width="auto"`. * * Freezing needs something to scroll, so the frozen modes wrap the grid in a `[part="grid"]` * scrollport. It is bounded inline by the host's own allocation (a matrix wider than a 320px host * scrolls inside the component rather than overflowing it) and unbounded in block by default; set * `--lr-heatmap-grid-max-block-size` to bound it, since a column band can only stay behind while * the grid actually scrolls vertically. * * Matrix mode only, like `matrixGeometry` and `lr-matrix-geometry-change`: calendar mode's axes * are a different geometry (a weekday gutter, a month band, and the optional `columnX`/ * `rowY` overrides), so this property is read but has no effect there. * * Under `dir="rtl"` the grid keeps this component's documented physical LTR geometry, so the * scrollport is direction-pinned like the canvas already is and the bands then freeze against the * logical inline-start/block-start edges of that pinned box — which is to say the physical left * and top, where the labels they duplicate are actually painted. * * See `LyraHeatmapStickyLabels` for why this is one closed set rather than a boolean or a pair. */ get stickyLabels():LyraHeatmapStickyLabels;set stickyLabels(next:LyraHeatmapStickyLabels); /** `stickyLabels` restricted to the mode it applies to. One gate for render and paint alike, so * switching `data` to a calendar can never leave a frozen band or a scrollport behind. */ private get effectiveStickyLabels();private get freezesRowLabels();private get freezesColLabels(); /** Column band resolved during the last draw, mirroring `resolvedRowLabelWidth`. */ private resolvedColLabelHeight; /** Normalized rotation in degrees: finite, clamped to `[0, 90]`. */ private get effectiveColLabelRotation(); /** Gutter resolved during the last draw, so hit-testing between draws agrees with what was * painted. Only ever differs from the default while `rowLabelWidth="auto"`. */ private resolvedRowLabelWidth;private get matrixPadLeft(); /** * The gutter/cell geometry the last matrix-mode draw actually painted with -- * `{ padLeft, padTop, cellSize }`, all in CSS pixels. Custom gaps/radius additionally report * `cellWidth`, `cellHeight`, and `cellRadius`; default cells omit these fields. This lets a light-DOM consumer * (e.g. a sticky header mirror) line up with the canvas without hardcoding the same numbers `row-label-width` * or `col-label-height`'s `"auto"` resolution would otherwise keep private. `undefined` in * calendar mode, and before the first matrix draw. * * This returns the frozen object `drawMatrix()` stored (and `lr-matrix-geometry-change` carried) * on the last draw -- not a fresh computation. That distinction is the whole contract. Computing * it on read from the same internal getters `drawMatrix()` uses looks equivalent and is not: * those getters read CURRENT layout, so any interval where layout has moved but no draw has * happened yet makes the getter describe a canvas that does not exist. Full redraws pause while * the host is outside the viewport (documented behaviour of this component), so that interval * can be long-lived and lands hardest on the tall, partly-scrolled matrix this getter exists to * serve. * Returning the stored object also makes the getter and the event the same value by * construction rather than by coincidence. */ get matrixGeometry():Readonly |undefined; /** * Returns a PNG data URL for the most recently completed canvas paint. The snapshot includes the * painted axes, cells, and canvas overlays, plus frozen label bands when `stickyLabels` is in * use; DOM-only legend, tooltip, and accessible-cell overlays are intentionally omitted. An * empty string means the component has not completed a paint yet, its canvas has no dimensions, * it is waiting for deferred visibility, or the browser could not encode the snapshot. */ exportData(format:LyraHeatmapExportFormat):string; /** The geometry the last `drawMatrix()` pass painted with, frozen and shared with the * `lr-matrix-geometry-change` detail. `undefined` until the first draw, which is what gates * `matrixGeometry` returning a real value only once there is a draw to describe. Also backs the * event's change detection, so a same-valued redraw (a color-only update, an unrelated data * change) does not refire it. */ private lastPaintedMatrixGeometry?;private get matrixPadTop(); /** Tallest rotated column label plus inset, floored at the built-in band and capped so labels can * never crowd out the matrix. The column mirror of `measureRowLabelWidth()`. */ private measureColLabelHeight; /** Widest row label plus insets, floored at the built-in gutter and capped so labels can never * crowd out the matrix itself. Measured against the same font the labels are drawn with. */ private measureRowLabelWidth; /** Trims `label` to fit `maxWidth`, ending in an ellipsis. Returns `''` when not even one * character plus the ellipsis fits, which is honest: a single clipped glyph reads as data. */ private ellipsize; /** Calendar weekday gutter resolved by the last draw. Keeping the measured `'auto'` value as * painted state means resize/hit-test paths share one result instead of independently measuring. */ private resolvedCalendarWeekdayLabelWidth;private get calendarPadLeft(); /** Widest rendered weekday label plus insets, with the same non-shrinking and host-fraction * safety bounds as the matrix row-label gutter. */ private measureCalendarWeekdayLabelWidth;private get matrixRowLabels();private get matrixColLabels();private get matrixValues();private get calendarDays();private get calendarData();private _cellSize?; /** * Effective per-cell size (CSS px). Originally matrix-mode only; now also * governs calendar mode's cell size (replacing the previously hardcoded * 11px constant there) once explicitly set. Left unset, each mode keeps * its own original default — `DEFAULT_MATRIX_CELL_SIZE` (22) in matrix * mode, `CAL_CELL` (11) in calendar mode — so an existing consumer who * never touches `cellSize` sees no change in either mode. Set explicitly * (attribute or property), the same value governs both modes alike. */ get cellSize():number;set cellSize(value:number|undefined|null); /** Legend caption. Unset uses the localized default; every supplied string is literal. */ valueLabel?:string; /** * Hides the legend. Same name and same polarity as ``'s `withoutLegend`, so the two * chart-adjacent surfaces a dashboard puts side by side are turned off the same way rather than * through a third spelling. * * The row is removed from the DOM outright -- swatches, endpoint labels, the `valueLabel` * caption, the annotation entries and the `legend` slot all go with it -- rather than being * visually hidden, so it contributes no layout box and assigns no slotted content. The legend's * own preparation stops too: the `--lr-heatmap-color-steps-gradient` custom property this * component writes onto the host for the legend bar (and for nothing else) is not written while * the legend is hidden, and is removed again if it had been. * * Cells, tooltips, keyboard interaction and the generated accessible summary are unaffected: the * summary already names the value label independently of the legend. */ withoutLegend:boolean; /** * `"linear"` (default) maps values linearly to the color ramp in matrix * mode, and buckets calendar-mode values via `quartileBucket()` — both * unchanged from before this property governed calendar mode too. * `"sqrt"` compresses via `sqrtStep()`'s square-root magnitude compression * instead, in *both* modes, so one heavy cell/day doesn't wash out the * rest of a skewed dataset. */ scale:HeatmapScale; /** Matrix-only trailing horizontal separator in CSS pixels, subtracted from the square cell * pitch. Clamped between zero and cellSize minus one; non-finite values use the default. */ cellGapX:number; /** Matrix-only trailing vertical separator in CSS pixels, with the same bounds as cellGapX. */ cellGapY:number; /** Matrix-only painted corner radius in CSS pixels, clamped to half the smaller painted side. * Does not change cellSize, the matrix pitch, data labels, or calendar geometry. */ cellRadius:number; /** Paint every Nth matrix column label, starting at column zero. Truncated to an integer of at * least one; non-finite values use one. Tooltips, keyboard labels, and data retain every label. */ colLabelInterval:number; /** * Pins the color ramp's input domain to `[min, max]` instead of deriving it from the data's own * extremes. Unset (the default) keeps today's behavior exactly: the ramp spans the data's own * min-max, so two heatmaps of comparable data each normalize to their own extremes and cannot be * read against each other. Setting it also opts the component into **signed data** (see * `signedDomain`), because declaring a domain is what disambiguates a negative value from the * no-data sentinel. A reversed or degenerate pair falls back to the derived range. */ domain?:[number,number]; /** * Anchors a diverging ramp's neutral color on this value rather than at the middle of the * domain, scaling the two halves independently (`lo`->0, `midpoint`->0.5, `hi`->1). Unset (the * default) leaves the plain min-max normalization untouched. Like `domain`, setting it opts into * signed data. A midpoint outside the resolved domain degrades to plain normalization rather * than distorting the ramp. */ midpoint?:number; /** * Whether the consumer has declared signed data, by pinning `domain` or anchoring `midpoint`. * * This gates the negative-value contract. By default a negative value is no-data, matching the * long-documented `-1` sentinel -- a matrix of counts has no meaningful negative and consumers * rely on that. A consumer who supplies a domain or midpoint has explicitly said the data is * signed, at which point silently dropping the whole negative half (32.7% of cells in the report * that prompted this) is the defect, not the feature. In signed mode only a non-finite value is * no-data; an absent matrix cell reads as `NaN`, so it stays no-data in both modes. * Live domain or midpoint changes refresh this sentinel without requiring replacement data. */ private get signedDomain(); /** Ramp position in `[0, 1]` for `value`, midpoint-anchored when `midpoint` is set. Every fill * path goes through this pair so the diverging anchor can never apply to some cells and not * others. */ private rampAlpha; /** `rampAlpha`'s discrete twin, for a `colorSteps` ramp. */ private rampBucket; /** Whether `value` paints as no-data, honoring the signed-domain contract above. */ private isNoData; /** * When set, `cellSize` is derived from the host's measured `clientWidth` * on every draw (including ResizeObserver-triggered redraws) instead of * the fixed `cell-size` attribute, so the grid actually fills the * available width. Without this, canvas dimensions are computed purely * from `matrixPadLeft + cols * cellSize` (matrix mode) or * `calendarPadLeft + weekCount * cellSize` (calendar mode), so a * resize-triggered redraw is a geometric no-op. Originally matrix-mode * only; now applies to calendar mode too. */ fitToWidth:boolean;private _maxCellSize?; /** * Ceiling (CSS px) on the cell size `fitToWidth` derives from the host width, in **both** modes. * Ignored entirely while `fitToWidth` is unset — an explicit `cellSize` is never clamped, since * it is already an exact request. * * Exists because `fitToWidth` divides the whole host width across the grid: a 5-week calendar or * a 3-column matrix in a wide pane produces enormous cells. Capping them keeps the cell a cell. * The canvas is sized *from the clamped cell size*, so a capped grid deliberately leaves the * remaining host width unfilled (the canvas simply ends early) rather than stretching to fill it * — position it with normal CSS on the host if you want it centered or end-aligned. * * Unset (the default) reproduces today's exact fit-to-width behavior. Clamped to at least the * built-in `4`px floor; a non-finite value (or an empty attribute) means unset rather than `0`. * When both clamps are set and `maxCellSize < minCellSize`, the ceiling wins — the same * precedence `finiteRange()` itself applies. */ get maxCellSize():number|undefined;set maxCellSize(value:number|undefined);private _minCellSize?; /** * Floor (CSS px) under the cell size `fitToWidth` derives from the host width, in **both** modes * — the mirror of `maxCellSize`, and likewise ignored while `fitToWidth` is unset. Raises the * built-in `FIT_MIN_CELL` (4px) floor so a year-long calendar in a narrow pane keeps legible, * hit-testable cells and overflows its host instead of collapsing to hairlines. * * Can only raise that floor, never lower it: a value below `4` normalizes to `4`. Unset (the * default) reproduces today's exact fit-to-width behavior, and a non-finite value (or an empty * attribute) means unset. */ get minCellSize():number|undefined;set minCellSize(value:number|undefined); /** * Calendar mode only (no-op in matrix mode): anchors the calendar grid at * a different weekday instead of always Sunday — `0`-`6`, same numbering * as `CalendarCellPos.weekday` (`0` Sunday .. `6` Saturday, matching * `CalendarCell.weekday`'s existing convention). Threaded into * `buildCalendarGrid()`. Defaults to `0` (Sunday), unchanged from before * this property existed. Normalized into `[0, 6]` via modulo wrap (not * clamp) -- mirrors `buildCalendarGrid()`'s own `(x + 7) % 7` weekday-wrapping * convention (already used elsewhere in this file), so e.g. `7` wraps to `0` * (Sunday) and `-1` wraps to `6` (Saturday) rather than being clamped to the * nearest in-range end. A non-finite input falls back to `0`. */ private get normalizedFirstDayOfWeek();private _bucketCount;get bucketCount():number;set bucketCount(value:number); /** Cells to ring-highlight — `row`/`col` in matrix mode, `date` in calendar mode. See `HeatmapAnnotation`. */ annotations:readonly HeatmapAnnotation[]; /** * A discrete legend key rendered *instead of* the `--lr-heatmap-scale-lo`/`-hi` gradient bar * and its `[part="legend-lo"]`/`[part="legend-hi"]` endpoint labels — one * `[part="legend-stop"]` per entry, in array order, each a `[part="legend-swatch"]` in that * entry's `color` plus a `[part="legend-stop-label"]`. Labels default to this component's own * locale-aware numeric formatting of `value`, so a stop only needs an explicit `label` when * the number isn't the right caption ("none", "≥ 90%"). * * A stop's `color` is optional: omit it (or pass `''`) for a **caption-only** entry, which * renders its label with no `[part="legend-swatch"]` element in the DOM at all — the shape a * "less ▢▢▢▢ more" style key needs for its two end captions, without an empty swatch box * sitting at either end of the row. * * Exists for the consumer who supplies `cellColor`: because that callback overrides a cell's * color entirely, the built-in two-endpoint bar can describe a ramp the grid no longer uses. * Supplying the same colors here keeps the legend honest without hiding `[part="legend"]` and * re-implementing swatches, labels and the annotation entries by hand. * * Strictly presentation: the stops are never consulted by the color ramp, the bucket math, the * tooltip, or the accessible name — supplying them changes nothing a cell renders. Any * `annotations` with a `label` still render their `[part="legend-annotation"]` entries after * the stops. Reassigning stops whose supported fields are unchanged does not schedule a redraw; * every assignment is still clone-owned, so mutating and reassigning the caller's array is * detected without exposing that mutation in place. Unset (the default) or an empty array * reproduces today's exact gradient legend. */ legendStops?:readonly HeatmapLegendStop[]; /** * The single cell to mark as persistently selected -- `row`/`col` in matrix mode, `date` in * calendar mode. Purely a controlled, consumer-owned visual/accessibility marker, mirroring * ``'s `selectedIndices` -- this component never mutates it itself; a consumer * wires it up from `lr-cell-click` (or any other source) to build a toggle-select * interaction. Unset (the default, `null`) draws no selection ring, adds no selected-cell text * to the host's `aria-label`, and adds no selected suffix to the keyboard announcement, * reproducing today's exact output. */ selectedCell:HeatmapSelectedCell|null; /** Enables controlled multiple selection through selectedCells instead of selectedCell. * Click or Enter/Space toggles a cell. Drag paints/erases; Shift+arrows extends a rectangular * range. The default false preserves the existing single-cell event and selection contract. */ multiple:boolean; /** Controlled selection in multiple mode. First MAX_HEATMAP_CELLS entries are clone-owned; * duplicates, invalid/out-of-grid coordinates and non-interactive cells are ignored. Matrix * entries use integer row/col; calendar entries use ISO dates (interactive gaps included). * User actions propose a new array through lr-selection-change, never mutate this property. */ selectedCells:readonly HeatmapSelectedCell[]; /** Proposes toggling all interactive cells in one row (calendar: weekday row 0..6). * An entirely selected row is cleared; otherwise it is added. No-op outside multiple mode. */ toggleRowSelection(row:number):void; /** Proposes toggling all interactive cells in one column (calendar: zero-based week column). * An entirely selected column is cleared; otherwise it is added. No-op outside multiple mode. */ toggleColumnSelection(col:number):void; /** * Renders an opt-in DOM overlay of native buttons over the canvas. Each * button has a localized accessible name, explicit `aria-selected="true"` or * `"false"` from `selectedCell`, and participates in a roving tabindex so a * dense calendar does not create hundreds of tab stops. The selection is * controlled: clicking a cell still emits `lr-cell-click`, and the * consumer updates `selectedCell` when it wants `aria-selected` to change. * Controlled grid refreshes preserve owned focus by matrix coordinate or calendar date, then * clamp to the nearest surviving interactive cell (or the heatmap base when none remain). */ accessibleCells:boolean; /** Formats the per-cell tooltip and keyboard announcement text — receives the cell position * (`MatrixCellPos` in matrix mode, `CalendarCellPos` — which carries the resolved ISO * `yyyy-mm-dd` `date`, gap positions included — in calendar mode) and its value. Falls back to * localized matrix row/column/value or calendar date/value templates when unset; the default * English catalog renders "Row X, Col Y: value" / "Mon DD: value". Use this callback for * application-specific wording rather than ordinary translation. */ cellText?:(pos:MatrixCellPos|CalendarCellPos,value:number)=>string; /** Opts individual cells out of the interaction model — receives the cell position and its * value, return `false` to make that cell present-but-non-interactive (no hover tooltip, * click, or keyboard roving-focus stop), without losing the layout/color-ramp machinery. Lets * a consumer omit a future/out-of-range date from interaction, or mark a zero-value cell as * non-interactive, without ~300+ meaningless keyboard tab stops on a dense grid. In calendar * mode the position carries its resolved ISO `date`, so "everything after today" is a direct * string comparison. Unset (the default) keeps every cell interactive, unchanged from before * this property existed. */ cellInteractive?:(pos:MatrixCellPos|CalendarCellPos,value:number)=>boolean; /** Overrides a cell's computed ramp/no-data color entirely for an exact value -- receives the * cell position (`MatrixCellPos` in matrix mode, `CalendarCellPos` — carrying the resolved ISO * `date` — in calendar mode) and its * value, return a CSS color string to force that cell to it, or `undefined` to fall back to the * normal `colorSteps`/ramp math unchanged. Lets a consumer designate a value as categorically * outside the ramp (e.g. a real zero-count day rendered as a neutral hairline, distinct from * both "no data" and the ramp's own lightest step) without a prepended synthetic ramp color, * which can't safely reserve an exact value on a skewed dataset (the bucket selectors round by * continuous ratio, with no equality-based reservation). Unset (the default) reproduces today's * exact ramp/no-data behavior for every cell. A returned value containing a CSS custom property * (e.g. `var(--x)`) or other browser-resolvable color syntax (e.g. `color-mix(...)`) is * automatically resolved before being used as a canvas fill color. */ cellColor?:(pos:MatrixCellPos|CalendarCellPos,value:number)=>string|undefined; /** Overrides the weekday-axis label text in calendar mode -- receives the real JS weekday index * (`0` Sunday .. `6` Saturday) for a row that would otherwise render a label (today, only rows * 1/3/5 relative to `firstDayOfWeek` ever do) and, when it returns a string, uses it instead of * the built-in `Intl.DateTimeFormat`-derived short weekday name. Unset (the default) reproduces * today's exact locale-derived output. */ private get calendarWeekdayLabelText(); /** Calendar weekday-gutter override, normalized at the runtime data boundary. The discriminated * record is a property-only API, so malformed JS still needs the same fail-safe behavior the * matrix gutter's attribute converter provides. */ private get calendarWeekdayLabelWidth(); /** Overrides the month-axis label text in calendar mode -- receives the real JS month index * (`0` January .. `11` December) and full year for a month boundary that would otherwise * render a label and, when it returns a string, uses it instead of the built-in * `Intl.DateTimeFormat`-derived short month name. Mirrors `weekdayLabelText`'s exact * override-with-fallback shape -- unset (the default) reproduces today's exact * `toLocaleString(effectiveLocale, ...)`-derived output. */ private get calendarMonthLabelText(); /** A discrete array (≥2) of CSS colors used as exact ramp steps instead of linearly * interpolating between the two `--lr-heatmap-scale-lo`/`-hi` endpoints — lets a consumer * bring a validated, non-linear (or simply non-2-endpoint) sequential palette. Governs both * `mode`s and both `scale` values, discretizing whichever scale would otherwise interpolate * continuously into `colorSteps.length` buckets instead. Unset (the default, or fewer than 2 * entries) keeps today's 2-endpoint interpolation exactly. Invalid entries use the canvas * fallback color and prevent the custom legend gradient from being assigned. */ colorSteps?:readonly string[]; /** * Calendar mode only: overrides the x-origin (canvas-local CSS px) of week * column `index` (0-based, same indexing as `CalendarCellPos.week`). * Consulted consistently by every calendar-mode geometry call site — * drawing, hit-testing, the keyboard focus ring, and month-label * positioning — via the private `columnXFor()` helper, so painted * geometry and pointer hit-testing never disagree. Lets a consumer * pixel-align a calendar's week columns with a sibling chart's bars by * supplying that chart the same coordinate function. Unset (the default) * keeps the evenly-spaced `calendarPadLeft + week * (cellSize + CAL_GAP)` * formula, whose default `calendarPadLeft` remains the original 28px. Ignored in matrix mode. */ private get calendarColumnX(); /** * Calendar mode only: overrides the y-origin (canvas-local CSS px) of * weekday row `weekday` (0-based, same indexing as * `CalendarCellPos.weekday`). The vertical analogue of `columnX` — consulted * consistently by every calendar-mode geometry call site that computes a * cell's y-coordinate from its weekday — drawing, hit-testing, and the * keyboard focus ring — via the private `rowYFor()` helper (mirroring * `columnXFor()`'s exact dispatch-with-computed-fallback shape). Also * consulted at `weekday = 7` (one past the last row) to size the canvas's * height, mirroring how `columnX` is consulted at `week = weekCount` to * size its width — so a function that spaces rows out further than the * default formula still gets a canvas tall enough to paint every row. * Unset (the default) keeps today's evenly-spaced `CAL_LABEL_H + weekday * * (cellSize + CAL_GAP)` formula unchanged. Ignored in matrix mode. */ private get calendarRowY();private canvas?;private resizeObserver?;private intersectionObserver?;private drawFrameRequest?;private dprQuery?;private dprChangeListener?;constructor(); /** The current value range, refreshed once per update cycle by `willUpdate()`. See `computeValueRange()`. */ private cachedValueRange;private cachedMatrixData; /** * The calendar-mode grid layout, refreshed by `willUpdate()` only when * `days` actually changes (see `computeValueRange()`'s twin comment above) * — `buildCalendarGrid()` parses every date, filters invalid ones, and does * a full `.slice().sort()` for month labels, so recomputing it from * `drawCalendar()`, `hitTestCalendar()`, `calendarCellAt()`, and * `onCalendarKeyDown()` independently would redo that work 2-4x for a * single hover/click/keydown. */ private cachedCalendarGrid;private cachedCalendarSortedValues;private cachedCalendarCellsByPos;private cachedCalendarCellsByDate;private cachedAnnotations; /** * Dev-mode-only: warns when `legendStops` describes colors the cells are not actually painted * from. * * `colorSteps` supplies the cell ramp and `legendStops` supplies the key, deliberately without * feeding back into each other -- that independence is what lets a `cellColor` consumer describe * a ramp the grid no longer uses. The cost is that a consumer building a diverging ramp supplies * both by hand and nothing checks they agree, so a legend can confidently label colors that never * appear. A wrong legend is worse than no legend, because the reader trusts it, and the mismatch * is invisible both in review and at runtime. * * Warning rather than deriving one from the other: deriving would silently change what an * existing `colorSteps`-only consumer sees, and would also break the deliberate `cellColor` * escape hatch. This keeps every current behavior and only makes the disagreement audible. * Compares only the stops that carry a color -- a caption-only stop describes no color at all -- * and, among those, only the ones that did not opt out via `partOfRamp: false`. */ private warnOnLegendRampMismatch;private cachedLegendStops;private cachedColorSteps;private cachedCalendarAnnotationDates;private cachedMatrixAnnotationPositions;private decorationProjectionTruncated; /** * Every grid position's ISO date, indexed by `week * 7 + weekday`, rebuilt alongside * `cachedCalendarCellsByPos` whenever the grid itself is. Populating `CalendarCellPos.date` from * a flat array keeps that field an O(1) string read at every call site — crucially inside * `drawCalendar()`'s inner loop, where computing it per cell would be a `new Date()` plus a * `toISOString()` for all ~365 positions of a year grid on *every* repaint (hover, resize, theme * change). Built once per grid change instead, which is the same order of work * `buildCalendarGrid()` already does on that same path. Covers gap positions too, so a day * missing from `days` still resolves to its real date. */ private cachedCalendarDateByPos;private cachedColumnGeometry?;private cachedRowGeometry?;private cachedAccessiblePositions;private cachedAccessiblePositionsByKey;private cachedAccessiblePositionIndexByKey;private cachedRamp; /** Set after a complete canvas pass. Focus movement can then repaint only the old/new cell * rectangles instead of clearing and repainting an entire dense heatmap. */ private canvasHasContent;private canvasVisible;private drawDirty; /** The cell currently under the pointer (`null` when not hovering one) — drives `[part="tooltip"]`. */ private hoverCell; /** The roving keyboard-focus cell cursor, moved by arrow keys — drives the * canvas-drawn focus ring, aria-hidden mirror, and light-DOM announcement. */ private focusedCell; /** Text mirrored in `[part="live-region"]`, refreshed on every focus move. */ private liveText;private accessibleTargetSizePx;private pendingAccessibleFocus;private pendingAccessibleFocusOrigin;private restoringAccessibleFocus;private accessibleFocusGeneration;private announcementSink?;private authorRole;private authorAriaLabel;private generatedAriaLabel;private syncingGeneratedSemantics;attributeChangedCallback(name:string,oldValue:string|null,value:string|null):void;connectedCallback():void;disconnectedCallback():void;private releaseAnnouncementSink;private syncAnnouncementSink;private watchDpr;private clearDprWatcher;private onDprChange;protected willUpdate(changed:PropertyValues):void;private rebuildBoundedDecorations;private get projectionTruncated();private get cellProjectionTruncated();private projectedCellCount;private projectionDescription; /** Snapshots one bounded rectangular matrix projection. Invisible ragged outliers never reach * scale/rank/count semantics, and later caller mutation cannot split paint from accessibility. */ private rebuildCanonicalMatrixData; /** The localized "Selected: ." description appended to the host `aria-label`, or `''` when * `selectedCell` is unset or doesn't resolve to a real cell in the current grid. */ private selectedCellDescription; /** * The real (non-no-data) value range across `values` (or `days` in * calendar mode), or `null` if there is none. Only called from * `willUpdate()`, which caches the result in `cachedValueRange` for * `render()` and `drawMatrix()` to reuse — `willUpdate()`, `render()`, and * `updated()` (which triggers `draw()`) all run within the same Lit update * cycle against the same `values`/`days`, so a single scan per cycle * suffices instead of one per consumer. */ private computeValueRange;private localizedValueLabel;private formatNumericValue;protected updated(changed:PropertyValues):void; /** Redraws canvas content after an upstream token or theme change. */ refreshTheme():void;private refreshAccessibleTargetSize; /** Reads the customizable ramp endpoints off the host's computed style. */ private scaleEndpoints; /** Resolves the `--lr-color-text-quiet` chrome token for axis labels. */ private labelColor; /** Reads the customizable canvas axis/label font off the host's computed style. */ private labelFont; /** Reads the customizable frozen-label-band backdrop off the host's computed style, resolved to a * concrete color: canvas silently keeps its previous `fillStyle` for anything it cannot parse, * and a `var()` chain is exactly that. */ private stickyLabelBg; /** Reads the customizable no-data cell fill off the host's computed style. */ private noDataFill; /** Reads the customizable canvas-drawn keyboard-focus-ring stroke color off the host's computed style. */ private focusRingColor; /** Reads the customizable canvas-drawn annotation-ring stroke color off the host's computed style. */ private annotationColor; /** Reads the customizable canvas-drawn selected-cell-ring stroke color off the host's computed style. */ private selectedColor; /** Paints persistent annotation, selection and transient focus through independent concentric * channels. Selection/focus use distinct dash patterns as a forced-color/small-cell fallback. */ private strokeCellState; /** Whether `selectedCell` refers to the given grid position, in whichever mode is active -- * `row`/`col` equality in matrix mode, resolved-date equality in calendar mode (looking the * position's actual date up via `calendarCellAt()`, the same way the annotation ring resolves * `ann.date` against `cells`). Shared by the live-region announcement, which needs to know * whether the just-announced cell *is* the selection. */ private isSelectedPos;private draw;private requestDraw; /** Repaints the old and new focus-ring cells after keyboard/click navigation. The underlying * cell fill plus annotation/selection rings are restored before the new focus ring is stroked, * so clearing a dirty rectangle never erases persistent data. Returns false when a complete draw * is still required (for example before the first canvas pass or after a mode change). */ private repaintFocusRing;private scheduleDraw;private cancelDrawFrame; /** Resolves a safe caller-supplied color in this element's live token scope before it reaches * canvas. Canvas accepts ordinary colors but not `var()`; a hidden child lets the browser resolve * arbitrary nested `var()`/`color-mix()` expressions without a hand-written CSS parser. */ private resolveColorStep;private colorRamp; /** * Effective per-cell size in calendar mode — mirrors `matrixCellSize()` * exactly: `fitToWidth` derives it from the host's measured width, * otherwise it's the (possibly explicitly-set) `cellSize` property, which * itself falls back to today's original 11px calendar default when left * unset (see `cellSize`'s accessor doc comment). Shared by `columnXFor()`, * `rowYFor()`, `drawCalendar()`, and the hit-testing below (`weekAtX()`, * `weekdayAtY()`, `cellRect()`) so they always agree on exactly the same * geometry as what's actually painted. */ private calendarCellSize; /** * Applies the optional `minCellSize`/`maxCellSize` clamps to a width-derived cell size. Only * ever reached from the `fitToWidth` branch of `calendarCellSize()`/`matrixCellSize()` — an * explicitly-set `cellSize` is an exact request and is never clamped. With both clamps unset * this is exactly the `Math.max(FIT_MIN_CELL, …)` floor both call sites applied before they * existed, so an untouched consumer's geometry is unchanged. * * The floor and ceiling are applied as two sequential clamps — raise to `minCellSize` first, * then cap to `maxCellSize` — rather than a single `finiteRange(size, fallback, min, max)` call. * `finiteRange()` sorts its `min`/`max` arguments before clamping, so passing an inverted pair * straight through would let whichever bound is numerically larger win, regardless of which * property a caller actually set. Applying the ceiling last instead means `maxCellSize` always * has final say once both clamps are set, even when it's set below `minCellSize`. */ private clampFitCellSize; /** * Calendar-mode week-column x-origin (canvas-local CSS px) — `columnX(week)` * when set, otherwise the original evenly-spaced formula (now derived from * `calendarCellSize()` rather than the fixed `CAL_CELL` constant). Shared * by every calendar-mode drawing and hit-testing call site (`drawCalendar()`, * `hitTestCalendar()`/`weekAtX()`, `cellRect()`) so painted geometry and * interactive hit-testing never disagree — mirrors the existing * `matrixCellSize()` invariant for matrix mode's * `drawMatrix()`/`hitTestMatrix()`/`cellRect()`. */ private columnXFor; /** * Calendar-mode weekday-row y-origin (canvas-local CSS px) — `rowY(weekday)` * when set, otherwise the original evenly-spaced formula (now derived from * `calendarCellSize()` rather than the fixed `CAL_CELL` constant). The * vertical analogue of `columnXFor()`, mirroring its exact * dispatch-with-computed-fallback shape and shared by the same * drawing/hit-testing/focus-ring call sites. */ private rowYFor;private calendarColumnPositions;private calendarRowPositions; /** * Inverse of `columnXFor()`: resolves an x position (canvas-local CSS px) * to the week column it falls in, or `null` if it's outside every column * (`weekCount` is 0, or `x` doesn't land inside `[0, weekCount)`'s span). * The default spacing has a closed-form inverse (division); an arbitrary * `columnX` override doesn't, so that case instead scans each column's * `[columnXFor(week), columnXFor(week + 1))` span — algebraically the same * span the default formula's division derives — so hit-testing always * agrees with wherever `columnXFor()` actually painted that column. */ private weekAtX; /** * Inverse of `rowYFor()`: resolves a y position (canvas-local CSS px) to * the weekday row it falls in (0-6), or `null` if it's outside every row. * Mirrors `weekAtX()`'s closed-form-vs-scan split for the same reason: the * default spacing has a closed-form inverse; an arbitrary `rowY` override * doesn't. */ private weekdayAtY; /** * Locale-derived weekday-axis labels for the 7 rows drawn down the left of * the calendar grid. The labeled weekdays are always Monday, Wednesday, and * Friday — matching today's sparse every-other-day label density — the * rest stay blank. Which *row* each one lands on depends on * `firstDayOfWeek`, since `buildCalendarGrid()` anchors `firstWeekStart` * (and therefore row 0) at that weekday. For each target weekday (using * the standard JS convention, Sunday=0..Saturday=6, so Monday=1, * Wednesday=3, Friday=5), the row is `(weekday - firstDayOfWeek + 7) % 7`; * with the default `firstDayOfWeek` of 0 this reduces to rows 1/3/5, * unchanged. The label *text* for each computed row is derived via * `Intl.DateTimeFormat` on a real UTC date that actually falls on that row, * so it follows the runtime locale instead of a hardcoded English array * (see `calendarCellText()`'s tooltip text for the same pattern). */ private weekdayLabels; /** Paints both calendar axes. Shared by the full draw and focus-cell repaint so the latter cannot * resurrect an untruncated weekday label after clearing a ring near the axis. */ private paintCalendarAxisLabels;private drawCalendar; /** * Effective per-cell size in matrix mode — `fitToWidth` derives it from * the host's measured width, otherwise it's the fixed `cellSize` * property. Shared by `drawMatrix()` and the pointer/keyboard hit-testing * below (`hitTestMatrix()`, `cellRect()`) so they always agree on exactly * the same geometry as what's actually painted. */ private matrixCellSize;private matrixCellShape;private fillMatrixCell;private paintMatrixCell;private paintMatrixFocusOverlays;private focusRepaintBounds;private repaintMatrixFocusCell;private paintCalendarCell;private paintCalendarFocusOverlays;private repaintCalendarFocusCell;private drawMatrix; /** Paints the row-label gutter at `x = 0 .. padLeft`. Extracted so the cell canvas and a frozen * `[part="row-labels"]` band paint from one routine and one set of geometry arguments — the * band's own canvas shares the cell canvas's origin, so "aligned" is what the same coordinates * mean, rather than something a second implementation has to keep agreeing about. */ private paintMatrixRowLabels; /** The column-label band at `y = 0 .. padTop`, rotation included. The mirror of * `paintMatrixRowLabels()`, shared with a frozen `[part="col-labels"]` band the same way. */ private paintMatrixColLabels; /** * Repaints whichever label bands `stickyLabels` freezes, from the geometry this very draw pass * painted the cells with. Each band is a canvas sharing the cell canvas's origin (both sit in the * same single-area grid), sized to the band it owns and offset on one axis only by * `position: sticky`, so it slides along the axis it is not frozen on and stays put on the one it * is. Nothing here recomputes geometry, which is what keeps a band aligned across a resize, a DPR * change, and a `row-label-width`/`col-label-height` `"auto"` re-resolution alike. * * The cell canvas keeps painting its own labels underneath: leaving that pass untouched is what * makes the default `'none'` byte-identical, and the band's opaque backdrop covers the duplicate * once it scrolls out from under the frozen copy. */ private paintFrozenLabelBands;private frozenBandCanvas; /** Sizes one band's backing store to the same DPR the cell canvas uses, fills the opaque backdrop, * then hands the caller a context already in CSS-pixel coordinates identical to the cell * canvas's — so a label lands on the same physical pixel in both. */ private paintFrozenBand; /** * Maps a pointer position, in canvas-local CSS px (e.g. * `PointerEvent.offsetX/offsetY`), to the cell underneath it — or `null` * if the pointer is outside the grid. Dispatches on `mode` so callers * don't have to. */ private hitTest;private hitTestMatrix; /** The first interactive matrix cell in row-major order, or `null` if every cell is excluded — * used by `onMatrixKeyDown()`'s first-arrow-press case. */ private firstInteractiveMatrixCell; /** Steps from `(row, col)` by `(dRow, dCol)` repeatedly, skipping non-interactive cells, until * an interactive cell is found or the grid edge is reached (in which case the original * position is returned unchanged, matching today's clamp-at-edge behavior). Bounded: each * iteration strictly approaches the edge, so this always terminates. */ private nextInteractiveMatrixCell; /** Calendar-mode analogue of `firstInteractiveMatrixCell()`. */ private firstInteractiveCalendarCell; /** Calendar-mode analogue of `nextInteractiveMatrixCell()`. */ private nextInteractiveCalendarCell;private hitTestCalendar; /** * Resolves a (week, weekday) grid position to its real calendar date and * value. Computed from grid geometry (`firstWeekStart` + offset), not * just looked up in `cells` — so a position with no matching entry in * `days` (a gap in a sparse calendar) still resolves to a real ISO date, * with a no-data sentinel value instead of being unresolvable. Signed mode * uses `NaN` so a real `-1` day remains distinguishable from an absent day; * default mode retains the legacy `-1` value. */ private calendarCellAt; /** Resolves one calendar grid position's value while keeping a real signed `-1` distinct from * an absent day. Every calendar paint, text, predicate and event path uses this boundary. */ private calendarValueAt; /** * The ISO `yyyy-mm-dd` date of a (week, weekday) grid position — an O(1) read out of * `cachedCalendarDateByPos`, falling back to the grid-geometry arithmetic for a position outside * the cached grid (a cursor left over from a shrinking `days`, say). Grid geometry guarantees * this agrees with the `date` of a matching `days` entry: `week * 7 + weekday` *is* the day * offset from `firstWeekStart` (see `buildCalendarGrid()`). */ private calendarDateAt; /** Builds the full calendar-mode cursor for a grid position, `date` included. Every calendar-mode * `CalendarCellPos` in this component goes through here, so no call site can forget the date. */ private calendarPos; /** * Pixel rect (canvas-local CSS px) of a cell — shared by `tooltipAnchor()` * so the tooltip's position always agrees with the same geometry * `drawMatrix()`/`drawCalendar()`/`hitTest*()` use. */ private cellRect; /** Geometry for the semantic cell overlay. Matrix buttons describe the bitmap that exists now, * not geometry a future draw might use: while redraws are paused offscreen, live getters can * move ahead of the last-painted canvas indefinitely. */ private accessibleCellRect; /** * Human-readable "