|undefined;
/** Applied as the generated ``'s native `title`, symmetrical with `cellStyle` -- e.g. the
* untruncated text behind an ellipsized cell, or a formatted timestamp behind a relative one.
* Returning `undefined` (or an empty string) omits the attribute entirely rather than rendering
* `title=""`, which would suppress an ancestor's own tooltip. The attribute is also suppressed
* while that cell is in inline-edit mode, so the tooltip can't shadow the editor.
*
* Accessibility: some screen readers announce a ` ` as the cell's accessible name,
* replacing the cell's own content rather than supplementing it (the same caveat `lr-stat`'s
* `exactValue` carries). Use it for a longer form of what the cell already shows, never for
* information that exists nowhere else. */
cellTitle?(row:T):string|undefined;
/** Numeric accessor backing the heat-tint background. A column that omits this is excluded from
* tinting (e.g. a label column) — its presence on any column is the opt-in signal for heat-tint
* mode as a whole, mirroring how `expandedContent` alone signals expand-mode (no separate
* boolean). Returns `null`/`undefined` for a cell with no value: excluded from both the domain
* computation and the tint (reads as "no data", not "zero"). A `cellStyle` on the same column
* that returns `background`/`backgroundColor` silently wins over this tint -- see `cellStyle`'s
* own doc for why. */
heatValue?(row:T):number|null|undefined;
/** Reads the value shown in the inline editor. When omitted, `row[key]` is
* used for record-like rows. For `editType: 'select'`, this is the selected option's
* `value` -- match one entry of `editOptions`, or none of them renders selected. */
editValue?:(row:T)=>string|number;
/** Accessible name (`aria-label`) for this row's inline editor, read once per row exactly like
* `editValue`/`cellTitle`. When omitted, every editor in the column shares the same interpolated
* `tableEditCell` name (`Edit {column}`) -- indistinguishable from its column siblings, which is
* harmless for `editTrigger: 'double-click'` (only one editor is ever open at a time) but not for
* `editTrigger: 'always'`, where each row's editor is a permanent, individually focusable Tab
* stop: an unset `editLabel` there leaves a keyboard or screen-reader user with no way to tell
* which row a given editor belongs to (WCAG 2.4.6, 1.3.1). Return the row's own full name (e.g.
* combining the column label with a row identifier) -- this is consumer-owned text like
* `cellTitle`/`editValue`, not passed through the table's own localization. */
editLabel?:(row:T)=>string;
/** Native editor type used when `editTrigger` is set. `'select'` renders a native ``
* populated from `editOptions`, one `` per entry in order; a `'select'` column with no
* `editOptions` renders an empty, valueless `` rather than throwing. */
editType?:'text'|'number'|'select';
/** Choices offered by an `editType: 'select'` column's editor. Ignored for every other
* `editType`. */
editOptions?:TableColumnEditOption[];}
/** One column definition. `cell` is required for every `editTrigger` except `'always'` -- the
* table's render path calls it to paint that column's plain-text resting state, which an
* `'always'` column never has (its editor is unconditional from first paint, see `editTrigger`'s
* own doc). Supplying `cell` on an `'always'` column stays valid; it is simply never required. */
export type TableColumn =(TableColumnCommon &{
/** Enables inline editing for this cell, opened by a double-click or, once the cell holds
* keyboard focus (Tab/arrow into the row, then Right/Left onto the cell -- see the
* class-level keyboard doc), by `F2` or `Enter`. One editor is open at a time; the table
* emits the proposed value through `lr-cell-edit` and never mutates `row` -- apply the
* change in the consumer and pass the updated `rows` back in. Omit for a column with no
* inline editor at all. */
editTrigger?:'double-click';
/** Renders this cell's content. Required whenever the column can be in its plain-text
* resting state -- i.e. always, for a `'double-click'` or unset `editTrigger`. */
cell:(row:T)=>unknown;})|(TableColumnCommon &{
/** `'always'` renders a persistent editor in every body cell of this column from first
* paint -- a settings/rate-style column the user is expected to type straight into. The
* table emits the proposed value through `lr-cell-edit` and never mutates `row`.
*
* These editors are plain tab stops outside the roving header/row/cell tabindex model, and
* bind their `value` as a content attribute, so native dirty-value-flag semantics apply:
* once the user has typed into one, an out-of-band `rows` update to that same cell no
* longer replaces what they are still editing. An untouched editor picks up a new `rows`
* value normally. */
editTrigger:'always';
/** Omittable for this `editTrigger: 'always'` arm: the persistent editor renders
* unconditionally, so the table's render path never falls back to this renderer. Still
* consulted as the `sortValue`-less sort fallback when `sortable` is set -- define
* `sortValue` on such a column instead of relying on this being present. */
cell?:(row:T)=>unknown;});
/** Every event `` emits. `K` is the row-key type the element was parameterized with
* (`LyraTable` -> `rowKey: number` in every detail below), defaulting to the
* `string | number` union an unparameterized table has always carried. */
export interface LyraTableEventMap{blur:CustomEvent;focus:CustomEvent;'lr-priority-columns-visibility-change':CustomEvent>;'lr-sort-request':CustomEvent;'lr-sort':CustomEvent;'lr-row-click':CustomEvent>;'lr-row-expand-request':CustomEvent>;'lr-row-expand-toggle':CustomEvent>;'lr-load-more':CustomEvent;'lr-retry':CustomEvent;'lr-selection-change':CustomEvent>;'lr-filter-change':CustomEvent>;'lr-page-change':CustomEvent>;'lr-cell-edit':CustomEvent>;'lr-column-resize':CustomEvent>;}
/**
* `` — a sort/select-aware data table.
*
* A sortable-header activation first proposes a cancelable `lr-sort-request`. If accepted, client
* mode writes `sortKey`/`sortDir` and reorders the rendered rows before emitting `lr-sort`; server
* mode leaves those properties controlled and emits the same committed transaction so the caller
* can fetch and supply the corresponding row order. Single/multiple selection is self-managed in
* one `selectedRowKeys` store; row expansion follows the same opt-in shape through
* `expansionMode`/`expandedRowKeys` -- see that property's own doc block below for the
* request/commit detail. The
* direction chosen the first time a column becomes the active `sortKey` comes from that column's
* own `columns[].defaultSortDir` when set, falling back to the element-level `defaultSortDir`
* (`'asc'` by default) otherwise -- letting one column in an otherwise-ascending table (e.g. a
* "last updated" column) start descending. Re-activating the column that is already `sortKey`
* still only toggles between `'asc'` and `'desc'`.
*
* Header/row activation is delegated: one `click` and one `keydown`
* listener on `` resolve the target via `closest('[data-col-key]'
* | '[data-row-key]')` and a key→object lookup map, instead of allocating
* fresh per-column/per-row closures on every render. Both listeners inspect
* the delegated event's composed path for actual native, role, or tabindex
* semantics (see `INTERACTIVE_SELECTOR`) so a button/link/input inside a cell
* owns its own activation instead of triggering `lr-row-click`. A passive
* custom element remains part of the row activation surface; an opaque
* closed-shadow control marks its host with `data-table-interactive`.
*
* Keyboard focus follows a roving-tabindex pattern (one `tabindex="0"` stop
* among the header cells, one among the body rows — see `focusedColKey()` /
* `focusedRowKey()`), matching this repo's other `role="grid"`/composite
* widgets. Left/Right/Home/End move within the header row; Up/Down/Home/End
* move within the body; Down from the header enters the body's roving stop,
* and Up from the body's first row returns to the header's roving stop.
* Enter/Space still only sort/activate (see `activateColumn()` /
* `activateRow()`). When controlled rows or columns replace the focused
* member, focus follows the same stable key when it survives and otherwise
* clamps to the nearest surviving index; an update never reclaims focus once
* the user has moved it outside the table. Effective locale changes use the same current
* page identity for activation, editing, and direct row-focus restoration.
*
* A column with `editTrigger: 'double-click'` additionally gives its own resting cell a
* `tabindex="-1"` roving-focus stop — reachable, once the row itself has focus, with ArrowRight
* (ArrowLeft under RTL) to enter at the first editable cell in the row and step forward, ArrowLeft
* (ArrowRight under RTL) to step back and, from the first editable cell, return focus to the row —
* never through Tab, so a table with one or more editable columns gains no new Tab stop, only a
* new arrow-reachable one, and a table with none renders no `tabindex`/`part='cell'[data-editable]`
* at all. `F2` or `Enter` on that focused cell opens its editor (`startEditing()`); `editCell()` is
* the same effect as a public method, for a consumer's own key binding, menu action, or other
* trigger. `Enter` on the row itself (not a focused cell) still only activates the row, exactly as
* before — the two coexist because the table tells them apart from which of the two currently has
* focus, not from the key alone. Escape and Enter inside the open editor keep cancelling/committing
* as already documented below; either one now also returns focus to the cell that opened it (the
* editor's own DOM node is what closes), matching a conventional grid's F2/Escape contract and
* closing the WCAG 2.1.1 (Keyboard) gap a pointer-only `double-click` trigger otherwise leaves.
* Priority-hidden columns hide their header, body, and footer cells together; revealing
* priority columns restores all three bands.
* Blank and later-duplicate column keys are omitted first-wins at assignment. Rows retain their
* caller-owned records, then one canonical `rowKey` projection omits blank and later-duplicate
* identities before filtering, counts, pagination, focus, actions, and events.
*
* Set `aria-label` on the host to give the `role="grid"` element an
* accessible name; it's forwarded into the shadow DOM's ``.
*
* `columns[].priority` ('medium' | 'low') hides that column once measured overflow says
* `[part='base']` actually needs the room -- `'low'` first, `'medium'` next if the table would still
* overflow with just `'low'` gone -- never at a fixed container width; `[part='reveal-columns-button']`
* forces them all back into view. The public `hasHiddenPriorityColumns`
* property reports only whether a priority column is actually hidden right
* now, measured via `ResizeObserver` on `[part='base']` plus a post-render DOM
* check. The toggle separately measures whether priority columns would hide
* at the current allocation, so it stays available while force-visible mode
* is active without making `hasHiddenPriorityColumns` contradict the rendered
* state. `priorityColumnsVisible` defaults to `false` and toggles itself on
* `[part='reveal-columns-button']` activation with no external wiring
* required, but is also settable up front (property or the reflected
* `priority-columns-visible` attribute) to restore a previously-persisted
* preference, and readable back — directly or via the `lr-priority-columns-visibility-change`
* event — to persist the current one. `columns[].sticky` pins a column's
* header/cells to the inline-start (`'start'`) or inline-end (`'end'`)
* edge while the table scrolls horizontally. Every member of the priority-column family --
* `revealColumnsLabel`/`hideColumnsLabel` (which only ever reach the DOM on
* `[part='reveal-columns-button']`), `priorityColumnsVisible` (which only overrides a hide rule
* there is none of) and `storageKey` (which persists nothing else) -- is inert unless at least one
* column declares `priority`, so configuring any of them without one logs a one-time,
* production-silent, page-bounded development `console.warn` naming the members that will do
* nothing (the same dev-diagnostic shape as an unnamed grid's own warning). The read-only
* `priorityColumnsToggleAvailable` reports whether the reveal button is currently offered at all,
* which is the same measured state the button itself renders from.
*
* `expandedContent` (a table-level `(row: T) => unknown`, not a per-column
* hook, since the resulting panel spans every column via `colspan`) makes
* every row render a leading chevron-toggle cell before its data columns.
* `canExpand` optionally gates which rows actually get an interactive
* toggle — a row that fails it still gets a blank leading cell for column
* alignment. Which rows are currently open lives in `expandedRowKeys` (a set of row keys, per
* `rowKey`/`keyOf()`), and `expansionMode` decides who writes it — mirroring `selectionMode`
* member for member. Under the default `'none'` the set is fully consumer-owned: the table only
* reads it and emits `lr-row-expand-toggle` on activation, exactly as it always has. Under
* `'single'` or `'multiple'` the table proposes each change with the cancelable
* `lr-row-expand-request` first and, unless a listener vetoes it, writes `expandedRowKeys` itself
* before announcing the applied change with `lr-row-expand-toggle`. `'single'` keeps at most one
* row open; the row it closes to make room gets its own `lr-row-expand-toggle` (`expanded: false`)
* just before the accepted one, so the per-row event stays a complete account of what opened and
* closed. Flipping `expansionMode` to `'single'` coerces an already-larger set down to its
* first key the same way `selectionMode` does for `selectedRowKeys`.
*
* Neither mode clears keys when the visible rows change: filtering, sorting and pagination leave
* `expandedRowKeys` alone, so a row scrolled, filtered or paged out of view returns expanded, and
* a key matching no current row simply renders nothing until one exists again. That is
* `selectedRowKeys`' own convention — valid off-view keys stay controlled state so a
* server-paginated table can keep them.
*
* Selection is opt-in through the `selectionMode` property. Use `single` or
* `multiple` to self-manage row selection; the default `none` remains
* presentational. `selectedRowKeys` contains the raw keys in every mode; single mode enforces one.
*
* `rowElement(rowKey)`, `cellElement(rowKey, columnKey)` and `expandedContentElement(rowKey)`
* resolve a rendered ``/`` from the identity a consumer already has, for code that has to
* reach content its own `cell(row)` or `expandedContent(row)` callback rendered into this shadow
* root (measuring it, scrolling it into view, or applying a style `::part()` cannot express, since
* only pseudo-classes may follow a part selector). One method per callback, because the expansion
* panel is a *sibling* ` ` of the data row rather than a descendant of it:
* `rowElement`/`cellElement` reach `cell(row)` output only, and `expandedContentElement` returns
* the `[part='expanded-cell']` holding `expandedContent(row)` output. All three read the current
* render output, so `await table.updateComplete` first and treat `null` as "not rendered right
* now". They resolve the `data-row-key`/`data-col-key`/`data-expanded-row-key` attributes those
* elements carry: `data-col-key` is the column's own `key`, while `data-row-key` and
* `data-expanded-row-key` are a type-tagged encoding of the row key (`string:a` vs `number:1`)
* that keeps a numeric key distinct from the string that stringifies the same way. The panel
* deliberately does not repeat `data-row-key`, so every `[data-row-key]` query still resolves
* exactly one element per row. All three attributes are stable public API; prefer the methods over
* building a selector from them, since a consumer-supplied key is not safe to interpolate into CSS
* unescaped.
*
* `filterable` adds a compact search field above the grid. `filterText` is
* controlled and emits `lr-filter-change`; `filter` can provide a typed
* predicate, otherwise the row is matched against its JSON representation.
* The internal filter and cell-editor native value events are contained at
* their translation boundaries; hosts receive `lr-filter-change` and
* `lr-cell-edit` instead.
* `pageSize` bounds pagination through the existing `` primitive (100 rows by
* default, normalized to 1..500). Client mode owns the accepted page and slices `rows`; server mode
* leaves `page` controlled, bounds the supplied page to `pageSize`, and uses `totalItems` for the
* navigation summary. `unknownTotal` (server mode only) forwards ``'s own
* indeterminate mode for a caller with no total -- previous/next only, no numbered list, no
* item-range summary -- with `hasNext` as the one extra signal that mode needs; see both
* properties' own docs. `loading` keeps the table shell busy; `loadingAppearance`
* chooses how — the default `'spinner'` replaces the grid with an indeterminate
* spinner, while `'skeleton'` keeps the real ``/` ` (and the
* filter/pagination chrome) and fills the body with `skeletonRows` placeholder
* rows, so column geometry survives the load instead of collapsing and
* reflowing. Loading takes precedence over both empty branches. Because a skeleton needs a
* column schema, a skeleton request received before `columns` arrives temporarily falls back to
* the spinner rather than showing the no-columns empty state. Initial declarative loading stays
* silent; every post-mount transition into either
* loading appearance appends to the shared light-DOM polite sink — including repeated cycles —
* while every placeholder opts out of ``'s own announcement.
* Columns with `editTrigger: 'double-click'` open a native text/number/select editor on
* double-click, `F2`, or `Enter` on the cell's own roving focus stop (see the keyboard paragraph
* above), and emit `lr-cell-edit`; row mutation remains consumer-owned. `editType: 'select'`
* renders a native `` populated from `editOptions` (`{ value, label }[]`) instead of an
* ` ` -- a column with no `editOptions` renders an empty, valueless `` rather than
* throwing. `editTrigger: 'always'` instead renders that editor in every body cell of the
* column from first paint — a settings/rate-style column meant to be typed
* straight into. Persistent editors are plain tab stops (no `tabindex` of their
* own, exactly like the row-expand toggle) outside the header/row roving model,
* so arrow keys still navigate the grid from a row's own tab stop and act as
* caret movement once focus is inside a field. Enter commits and keeps focus;
* Escape has nothing to cancel back to, so it is left uncancelled for an
* ancestor dialog/popover. Their value binds as a content attribute, so once
* the user has typed into one an out-of-band `rows` update to that same cell no
* longer replaces the draft; an untouched editor still picks up a new value. A persistent
* `editType: 'select'` editor has no such native protection -- ``/`` carry no
* dirty-value flag, so an out-of-band `rows` update to that cell re-applies the selection even
* after the user has picked a different option.
* Each editor's accessible name is `columns[].editLabel(row)` when the column defines it, or
* otherwise the interpolated `tableEditCell` string (`Edit {column}`) -- the same name for every
* row in that column, since the default carries no row context. Define `editLabel` for any
* `editTrigger: 'always'` column: its editors are permanent, individually focusable Tab stops, so
* leaving every one of them identically named (e.g. fifty "Edit Status" controls) fails WCAG 2.4.6
* and 1.3.1 for keyboard and screen-reader users.
* Focus is restored across a re-sort that moves the editor's node, and dropped
* (never re-aimed at an unrelated row) when its row leaves the rendered page.
* `spellcheck`/`autocapitalize`/`autoCorrect` forward to the filter input and, for a `'text'`
* (the default) `editType`, the inline cell editor -- no effect on a `'number'` or `'select'` cell
* editor.
* `groupBy` inserts non-focusable group header rows before each group; use
* `groupLabel` when the raw group key needs custom content. A client-mode
* sort applies *within* each group, so grouping survives sorting on a column
* unrelated to the group key.
*
* `columns[].heatValue` opts a column into heat-tint mode: its numeric return value is normalized
* against a shared scale spanning every `heatValue`-defining column across every currently-rendered
* row (auto-derived, or overridden via `heatTintScale`) and painted as a `color-mix()` background via
* the retheme-able `--lr-table-heat-tint-lo`/`-hi` custom properties (matching `lr-heatmap`'s own
* ramp-token convention). The heat-tint and resize properties can be set on the table or a theme
* ancestor; a value set directly on the table wins through the normal cascade. `rowTotal`/`grandTotal`
* add a trailing column mirroring `expandedContent`'s
* leading one: `rowTotal(row)` renders per-row, `grandTotal(rows)` renders at its intersection with
* the footer row (only when a column also defines `footer`) — both share `footer`'s own
* "consumer computes/renders" contract rather than assuming addition.
*
* The public readonly `viewRows` (`rows` after filtering and client-mode sorting, ignoring
* pagination) and `pageRows` (`viewRows` sliced to the page currently rendered in ``) getters
* expose the same rows `footer(rows)`/`grandTotal(rows)`/the heat-tint domain already see, so a
* consumer that needs "what the grid currently shows" -- e.g. to export it -- reads one of these
* instead of re-implementing filtering, sorting, and pagination itself. Both return a fresh, frozen
* array on every read; mutating the result cannot reach the table's own internal state.
*
* The built-in empty state is addressable rather than fixed: every `` the table renders
* carries `part="empty"` and re-exports its own inner parts as `empty-heading`/`empty-description`/
* `empty-icon`/`empty-actions`/`empty-base`, the two *data*-empty branches (no rows at all, and
* filtered/paginated down to zero) render it as the fallback content of a named `empty` slot so a
* consumer can replace it wholesale, and `emptyCompact` overrides each branch's built-in `compact`
* default. The no-columns branch is deliberately **not** slot-replaceable — it reports a
* configuration problem (`noColumnsHeading`), not "this query returned nothing", and a single slot
* covering all three would collapse that distinction.
*
* A separate `error` state reports a failed load without discarding grid context: while `error` is
* set, ``'s single row becomes a failed-load `` (the same `error`-prefixed exported
* parts as the empty state, plus a built-in `[part='retry-button']`), behind its own `error` slot —
* but ``, the filter field, and pagination all stay mounted around it, unlike either
* data-empty branch above, which replace them too. Precedence when more than one state could apply
* at once: `loading` beats `error` beats every empty branch, so a `loading` table never flashes a
* stale `error`, and an `error` table never falls through to "no rows"/"no columns" copy
* underneath it. The retry button's `lr-retry` is cancelable: the built-in action clears `error`,
* and `preventDefault()` leaves it set for a consumer that owns its own retry timing.
*
* `layout` sets a floor on the ``'s `table-layout`: `'fixed'` forces it even with no column
* widths, while the default `'auto'` still resolves to `fixed` whenever a column declares a `width`
* or a drag-resize is in flight (column resizing does not work under `table-layout: auto`).
*
* @customElement lr-table
* @event lr-sort-request - Cancelable sort proposal. Frozen readonly
* `detail: { phase: 'request', sortKey, sortDir }`. Vetoing it leaves sort state and rows
* unchanged and suppresses `lr-sort`.
* @event lr-sort - Accepted sort transaction. Frozen readonly
* `detail: { phase: 'commit', sortKey, sortDir }`. Client mode also updates `sortKey`/`sortDir`;
* server mode leaves them controlled while reporting the accepted proposal.
* @event lr-row-click - A row was activated. `detail: { row }`.
* @event lr-load-more - The "load more" control was activated.
* @event lr-retry - The built-in `[part='retry-button']` was activated, only rendered while
* `error` is set. Cancelable: the default action clears `error`; `preventDefault()` leaves it
* set instead.
* @event lr-priority-columns-visibility-change - `priorityColumnsVisible` was toggled by
* `[part='reveal-columns-button']`. Frozen readonly `detail: { visible: boolean }`.
* @event lr-row-expand-request - Cancelable proposal before a self-managed expansion change.
* Frozen readonly `detail: { row, rowKey, expanded }`, where `expanded` is the state being
* proposed. Emitted only while `expansionMode` is `'single'` or `'multiple'`; vetoing it skips
* the built-in `expandedRowKeys` write and suppresses the following `lr-row-expand-toggle`,
* leaving the row's expansion fully controlled.
* @event lr-row-expand-toggle - The row-expand chevron was activated.
* Frozen readonly `detail: { row, rowKey, expanded }`, where `expanded` is the state the
* activation resolves to. Fired only when `expandedContent` is set and the row passes
* `canExpand`. Under the default `expansionMode: 'none'` it does not itself mutate
* `expandedRowKeys` — the consumer updates it and passes the new value back in. Under a
* self-managed mode it follows an unvetoed `lr-row-expand-request` and the write has already
* landed, so a listener reading `expandedRowKeys` sees the new state. `'single'` additionally
* fires it once with `expanded: false` for the row it just closed to make room, immediately
* before the accepted one, so a host mirroring open rows from this event alone stays correct —
* with one boundary: a displaced row that is filtered or paged out of view has no `row` object
* to describe, so that case is reported only through `expandedRowKeys`. The `'single'` coercion
* that runs when `expansionMode` itself becomes `'single'` reports through `expandedRowKeys`
* alone for the same reason — a key that matches no rendered row cannot carry a `row`.
* @event lr-selection-change - Opt-in row selection changed, from a row activation or from a
* `selectionMode` flip to `'single'` coercing an existing multi-row selection down to one key.
* Frozen readonly `detail: { rowKeys: readonly K[] }` — `readonly (string | number)[]` on an
* unparameterized table, the default `K`. Not cancelable in either case: it announces a
* selection that has already changed rather than proposing one.
* @event lr-filter-change - The filter field changed. Frozen readonly `detail: { text }`.
* @event lr-page-change - A pagination control requested a page. Frozen readonly `detail: { page }`.
* @event lr-cell-edit - An inline editor committed a value. `detail: { row, columnKey, value }`.
* @event lr-column-resize - A resizable column changed width by pointer or keyboard. `detail:
* { columnKey, width }`, where `width` is in CSS pixels. A pointer drag fires this once per pixel of
* movement as non-cancelable live feedback, then once more, **cancelable**, for the final
* width committed at drag-end; a keyboard step (Home/End/Arrow) is already a single discrete
* action and fires that one cancelable commit directly. `preventDefault()` on a cancelable
* emission reverts the column to its pre-gesture width -- unless the listener resolved the
* resize itself during that same synchronous dispatch, in which case the width it applied stands
* instead of being rolled back over.
* @event focus - Re-dispatched from the internal filter/cell-editor native inputs' own `focus` —
* bubbling and composed (unlike the native event, which is neither).
* @event blur - Re-dispatched from the internal filter/cell-editor native inputs' own `blur`, for
* the same reason as `focus`.
* @csspart base - The root wrapper around the `` and its footer controls.
* @csspart table - The `` element.
* @csspart caption - The `` element, rendered only when `caption` is set.
* @csspart head - The ` ` element.
* @csspart header-cell - Each `` header cell.
* @csspart resize-handle - The focusable separator used to resize a `resizable` column. Its
* numeric ARIA range remains in CSS pixels while `aria-valuetext` reports the current value
* through the effective locale.
* @csspart row - Each body ` `.
* @csspart cell - Each body ``. An `editTrigger: 'double-click'` column's resting (not
* currently editing) cell additionally carries `[data-editable]` and its own `tabindex="-1"`
* roving-focus stop -- see the keyboard paragraph above.
* @csspart row-total-cell - Each body row's trailing ` ` holding `rowTotal(row)`, rendered only
* when `rowTotal` is set. The corresponding footer-row cell (holding `grandTotal`) is a
* `footer-cell` instead, matching every other footer cell.
* @csspart foot - The ` `, only rendered when at least one column defines `footer`.
* @csspart footer-row - The single footer row.
* @csspart footer-cell - A single footer cell.
* @csspart cell-editor - The native inline cell editor -- an ` ` for `editType: 'text'`/
* `'number'`, a `` for `editType: 'select'`: shown after a double-click on an
* `editTrigger: 'double-click'` cell, and rendered persistently in every body cell of an
* `editTrigger: 'always'`
* column.
* @csspart more-button - The "load more" control, shown when `hasMore` is true.
* @csspart sort-icon - The direction indicator in a sortable header cell.
* @csspart sort-icon-active - The active direction chevron; also carries sort-icon.
* @csspart sort-icon-inactive - The muted bidirectional indicator when sortIndicators is all; also carries sort-icon.
* @csspart reveal-columns-button - The button that toggles `priority`-hidden columns back into view.
* @csspart expand-toggle-cell - Each row's (and the header's) leading
* chevron-toggle cell, rendered only when `expandedContent` is set.
* @csspart row-expand-toggle - The `` inside `expand-toggle-cell`,
* absent for a row that fails `canExpand`; it inherits the table's typography.
* @csspart row-expand-icon - The 1em chevron icon inside `row-expand-toggle`.
* @csspart expanded-row - The full-width panel `` rendered beneath a
* row whose key is in `expandedRowKeys`. Carries `data-expanded-row-key`, not `data-row-key`:
* it is a sibling of the data row, so repeating that attribute would make every
* `[data-row-key]` query resolve two elements per open row.
* @csspart expanded-cell - The single `colspan`-spanning `` inside
* `expanded-row`, containing `expandedContent(row)`. Resolved from script by
* `expandedContentElement(rowKey)`.
* @csspart group-row - A non-focusable group header row.
* @csspart group-cell - The full-width group header cell.
* @csspart filter - The optional row-filter input.
* @csspart filter-label - The `` wrapping the filter input.
* @csspart filter-clear - The button that clears the filter field, replacing the native
* search-cancel glyph suppressed by this field's own reset; rendered only while it has a value.
* @csspart loading - The loading-state wrapper. Under `loadingAppearance="spinner"` (the default)
* it is the visible block holding the spinner; under `"skeleton"` it is the visually-hidden
* aria-hidden announcement mirror, since the placeholder rows are the visible affordance. It is
* never itself live; post-mount loading announcements use the shared light-DOM polite sink.
* @csspart skeleton - Each `` placeholder inside a `loadingAppearance="skeleton"`
* body cell. Its rows and cells reuse the ordinary `row`/`cell`/`row-total-cell` parts (that
* is what keeps them geometrically identical to real rows), so this is the part to target for
* the placeholder's own look — e.g. `::part(skeleton) { --lr-skeleton-h: 2em; }`.
* @csspart pagination - The optional pagination component.
* @csspart empty - The built-in `` host, in all three empty states (no columns
* configured, no rows at all, and filtered/paginated down to zero rows). The two data-empty
* states render it as the `empty` slot's fallback, so it disappears once that slot is filled.
* Note that the no-columns and no-rows states return the empty element as the shadow root's own
* root, with no `[part='base']` wrapper around it — `::part(base)` does not apply in those two
* states, only in the filtered-to-zero one.
* @csspart empty-base - Exported from the built-in ``'s own `base` part.
* @csspart empty-icon - Exported from the built-in ``'s `icon` part.
* @csspart empty-heading - Exported from the built-in ``'s `heading` part.
* @csspart empty-description - Exported from the built-in ``'s `description` part.
* @csspart empty-actions - Exported from the built-in ``'s `actions` part.
* @csspart error-row - The single full-width `` that replaces the row content while `error` is
* set. Present only in the in-grid branch: when `columns` is empty there is no grid to keep
* mounted, so the failed-load content renders standalone and neither this part nor `error-cell`
* exists.
* @csspart error-cell - The `` inside `[part='error-row']`, spanning every column, that holds
* the failed-load content.
* @csspart error - The built-in `` host rendered in the row body while `error` is set.
* Unlike `[part='empty']`'s no-rows branches, its surrounding ``, filter, and pagination
* stay mounted rather than being replaced along with it.
* @csspart error-base - Exported from the built-in error ``'s own `base` part.
* @csspart error-icon - Exported from the built-in error ``'s `icon` part.
* @csspart error-heading - Exported from the built-in error ``'s `heading` part.
* @csspart error-description - Exported from the built-in error ``'s `description` part.
* @csspart error-actions - Exported from the built-in error ``'s `actions` part; wraps
* `retry-button`.
* @csspart retry-button - The built-in retry control rendered into the error state's `actions`.
* @slot empty - Replaces the built-in empty state on the two *data*-empty branches (no rows at
* all, and filtered/paginated down to zero). Left unfilled, the built-in `[part='empty']`
* `` renders as this slot's fallback content. The no-columns branch renders its own
* `noColumnsHeading` state and is not slot-replaceable.
* @slot error - Replaces the built-in failed-load state, including its retry button, while `error`
* is set. Left unfilled, the built-in `[part='error']` `` renders as this slot's
* fallback content.
* @cssprop [--lr-table-resize-min-width=var(--lr-size-3rem)] - Default minimum width for a
* resizable column without an explicit pixel `minWidth`. Inherits from theme ancestors.
* @cssprop [--lr-table-resize-handle-opacity=0.12] - Hover/focus opacity of the resize handle.
* Legacy shared-state hook; inherits from theme ancestors.
* @cssprop [--lr-table-resize-handle-hover-bg=var(--lr-color-brand)] - Resize-handle hover/focus background.
* @cssprop [--lr-table-resize-handle-hover-opacity=var(--lr-table-resize-handle-opacity,0.12)] - Resize-handle hover/focus opacity.
* @cssprop [--lr-table-resize-handle-active-bg=var(--lr-table-resize-handle-hover-bg,var(--lr-color-brand))] - Resize-handle pressed background.
* @cssprop [--lr-table-resize-handle-active-opacity=calc(var(--lr-table-resize-handle-hover-opacity,var(--lr-table-resize-handle-opacity,0.12))*2)] - Resize-handle pressed opacity.
* @cssprop [--lr-table-cell-color=inherit] - Text colour of body cells; inherits the host's own
* colour by default.
* @cssprop [--lr-table-cell-link-color=var(--lr-color-brand)] - Colour of an anchor returned from a
* column's `cell(row)`. Such an anchor renders inside this component's shadow root, so page CSS
* cannot reach it and `::part()` cannot select past the first compound selector to reach it
* either; without this hook it computes to the UA default link blue. Set `revert` for the UA
* default.
* @cssprop [--lr-table-cell-link-hover-color=var(--lr-table-cell-link-color,var(--lr-color-brand))] -
* Colour of that anchor on hover and `:focus-visible`, which also thicken its underline.
* @cssprop [--lr-table-cell-padding=var(--lr-space-s)] - Padding of a header cell, a body cell, and
* the row-total cell.
* @cssprop [--lr-table-cell-padding-compact=var(--lr-space-xs) var(--lr-space-s)] - Padding of a
* group-header cell and a footer cell, which default to a tighter block/inline shorthand than
* `--lr-table-cell-padding` rather than sharing it outright.
* @cssprop [--lr-table-font-size=inherit] - Font size of the `` element and, through normal
* inheritance, every cell inside it. The rest of the font shorthand (family, weight, etc.) keeps
* inheriting from the host regardless of this override.
* @cssprop [--lr-table-max-height=none] - Cap on the scroll container's block size, past which the
* table body scrolls.
* @cssprop [--lr-table-heat-tint-lo=var(--lr-color-brand-quiet)] - Low endpoint of the heat-tint
* ramp used by `heatValue` columns. Inherits from theme ancestors.
* @cssprop [--lr-table-heat-tint-hi=var(--lr-color-brand)] - High endpoint of the heat-tint ramp
* used by `heatValue` columns. Inherits from theme ancestors.
* @cssprop [--lr-table-heat-t] - This cell's position on the heat-tint ramp, as a percentage
* string. Set inline by the component on each `[data-heat]` cell; not consumer-settable.
* @cssprop [--lr-table-row-selected-bg=var(--lr-color-brand-quiet)] - Background of a row whose
* `aria-selected` is `true`, including that row's own `sticky` column cell -- a sticky cell
* otherwise paints its own opaque surface and would hide the selected fill. Shadow Parts forbids
* an attribute selector after `::part()`, so `::part(row)[aria-selected]` is invalid CSS and the
* selected row could otherwise only be restyled by hijacking the library-wide
* `--lr-color-brand-quiet` token.
* @cssprop [--lr-table-row-stripe-bg=transparent] - Background of alternating body rows, including
* each row's own `sticky` column cell. The token is read only on rows carrying the internal
* stripe marker, so it can be set on the table or an ancestor without affecting group, expanded,
* hover, or selected rows.
* @cssprop [--lr-table-header-sorted-bg=var(--lr-color-surface)] - Background of the currently-sorted column's
* header cell (`[aria-sort]` other than `none`), including a `sticky` column's own header cell.
* Same rationale as `--lr-table-row-selected-bg`: `::part(header-cell)[aria-sort]` is invalid CSS,
* so this token is the supported way to recolor the sorted header without hijacking a
* library-wide token.
* @cssprop [--lr-table-header-sorted-color=inherit] - Text color of the currently-sorted column's
* header cell.
* @cssprop [--lr-table-sticky-offset=0] - Distance a `sticky` column pins from the inline edge.
* Measured and set inline per column by the component so multiple sticky columns stack instead
* of overlapping; falls back to `0` for the first one, or before the first measurement pass.
* @cssprop [--lr-theme-scrollbar-width=auto] - Opt-in theme-level scrollbar width honored by the
* `base` scroll container; unset, renders identically to before. Set on `:root` or any ancestor
* to retune every internal scroll container in the library at once.
* @cssprop [--lr-theme-scrollbar-gutter=auto] - Opt-in theme-level scrollbar gutter honored by the
* `base` scroll container; see `--lr-theme-scrollbar-width`.
* @status stable
* @since 4.0.0
*/
export declare class LyraTableextends LyraElement>{static styles:import("lit").CSSResultGroup[];private _columns;
/** Clone-owned readonly column-definition sequence, bounded to the first 10,000 source
* positions. Blank keys and later duplicate keys are omitted (first valid occurrence wins)
* before any header, cell, sort, focus, or event path. Column objects and callbacks retain their
* identities; reassign the collection to update. */
get columns():readonly TableColumn[];set columns(value:readonly TableColumn[]);private _rows;
/** Clone-owned readonly row sequence, bounded to the first 10,000 rows. Row objects are retained
* here; the rendered model applies `rowKey` as one unique nonempty first-wins identity
* projection before filtering, counts, pagination, focus, actions, and events. Reassign the
* collection to update. */
get rows():readonly T[];set rows(value:readonly T[]);
/** Floor for the ``'s `table-layout`. `'fixed'` forces the fixed algorithm even when no
* column declares a `width`, so every column shares the available width evenly and long cell
* content is clipped/wrapped instead of stretching its column. The default `'auto'` is only a
* floor: it still resolves to `fixed` whenever a column declares a `width`, a column has been
* drag-resized, or a resize gesture is in flight — resizing does not work under
* `table-layout: auto`.
*
* Two consequences of the fixed algorithm are worth knowing before opting in: with no declared
* widths the *first* row (header row included) determines every column's width, so revealing a
* `priority`-hidden column via `[part='reveal-columns-button']` re-measures and changes all of
* them; and `columns[].minWidth`/`maxWidth` are silently ignored by `table-layout: fixed`
* (declare `width` instead when you need a specific column sized). */
private _layout;get layout():'auto'|'fixed';set layout(next:'auto'|'fixed');sortKey:string;sortDir:TableSortDirection;
/** `'active'` preserves the active-column chevron alone. `'all'` also reserves the same icon
* space with a muted bidirectional indicator in inactive sortable headers, including on touch
* screens. This presentation choice does not change sorting, focus or aria-sort semantics. */
sortIndicators:TableSortIndicators;
/** `'client'` (the default) orders `rows` itself, in the browser, from `sortKey`/`sortDir` and
* the active column's `sortValue`. `'server'` renders `rows` in exactly the order given,
* assuming the caller has already sorted them — mirroring `paginationMode`'s identical
* client/server split. Header activation first emits cancelable `lr-sort-request`; an accepted
* activation emits `lr-sort` either way, but only client mode mutates these properties.
*
* With no `sortKey` set (the default) `'client'` is a no-op: the input order is preserved
* verbatim, so an existing consumer that only listens for `lr-sort` sees unchanged rendering
* until a header is actually activated. */
sortMode:TableSortMode;
/** The direction applied whenever header activation switches sorting to a *different* column —
* including the first column ever sorted — for any column that does not declare its own
* `columns[].defaultSortDir` (that column-level value wins first when set). Re-activating the
* column that is already `sortKey` toggles between `'asc'` and `'desc'` instead, so this never
* overrides a direction the user just chose for the column they are still on. Defaults to
* `'asc'`; set `'desc'` for a most-recent-first or highest-first table. */
defaultSortDir:TableSortDirection;
/** Accessible name for the `role="grid"` — a typed alternative to setting `aria-label` on the
* host. When set it becomes the grid's `aria-label`; a host `aria-label` is used as a fallback
* when this is unset. Consumer-supplied text, so it is NOT run through `this.localize()`. An
* explicitly empty string is a real override (renders `aria-label=""`) rather than falling
* back to the host `aria-label`. */
accessibleLabel?:string;
/** Optional visible caption rendered as the table's ``. Also names the grid (via
* `aria-labelledby`) when no `accessibleLabel`/host `aria-label` is set. Consumer-supplied
* text, not localized. */
caption:string;
/** Stable id for the ``, so `aria-labelledby` can point at it. */
private readonly captionId;
/** Derives each row's stable identity for `repeat()`'s DOM-reconciliation
* key and the delegated click/keydown row lookup (`rowsByKey`,
* `data-row-key`). When omitted, `keyOf()` falls back to the row's index
* in `rows`, which is only a safe identity while `rows` never reorders —
* provide `rowKey` whenever `rows` can be sorted, filtered, or otherwise
* re-ordered across renders, or row identity (selection, focus, click
* targets) can silently attach to the wrong row. Empty string identities and later duplicates
* are omitted before every rendered/count/focus/action/event path; the first valid occurrence
* wins. */
rowKey?:(row:T)=>K;selectionMode:TableSelectionMode;
/** Who owns `expandedRowKeys`, mirroring `selectionMode`'s three members. `'none'` (the default,
* and the behaviour this component shipped with) leaves the set entirely consumer-controlled:
* activation only reports `lr-row-expand-toggle`. `'single'` and `'multiple'` self-manage the
* set behind the cancelable `lr-row-expand-request`, with `'single'` keeping at most one row
* open and coercing an already-larger set down to its first key when this property becomes
* `'single'`. Closing a row to make room for another is an expansion change like any other, so
* `'single'` reports the displaced row with its own `lr-row-expand-toggle` (`expanded: false`)
* ahead of the accepted one -- unless that row is filtered or paged out of view, which leaves
* it no `row` to describe. The property-driven coercion above reports through
* `expandedRowKeys` alone for the same reason. No mode ever clears keys because the visible
* rows changed -- see the class JSDoc's note on off-view keys, which follows
* `selectedRowKeys`' convention. */
expansionMode:TableExpansionMode;
/** Which element scrolls when the table overflows; see `TableScrollMode`. `'auto'` keeps page
* flow while content fits and contains horizontal overflow only when needed. Defaults to
* `'self'`, which is the pre-10.0 behaviour. */
scrollMode:TableScrollMode;private _selectedKeys;
/** Selected raw row keys in every selection mode, bounded to 10,000 keys. Single mode replaces
* this set with exactly one key per row activation; multiple mode toggles membership. Reads
* return immutable detached `ReadonlySet` facades; malformed and whitespace-only string keys are
* omitted while valid off-page keys are retained for server pagination. Reassign a new set to
* update. */
get selectedRowKeys():ReadonlySet;set selectedRowKeys(value:ReadonlySet);filterable:boolean;filterText:string;filter?:(row:T,text:string)=>boolean;
/** Optional filter-copy overrides. Omission localizes the matching message key; supplied
* strings, including the built-in English text or an empty string, render verbatim. */
filterLabel?:string;filterPlaceholder?:string;
/** Forwarded to the filter input's, and (when the active column's `editType` is `'text'`, the
* default) the inline cell-editor input's, native `spellcheck`. Defaults to `true`, matching
* the native element's own default. `spellcheck="false"` is parsed as `false` (see
* `spellcheckConverter` above). No effect on a `'number'` or `'select'` cell editor. */
spellcheck:boolean;
/** Forwarded to the same inputs' native `autocapitalize`. Empty string omits the attribute
* (browser default). */
autocapitalize:string;
/** Forwarded to the same inputs' native `autocorrect` (Safari/WebKit-specific). Empty string
* omits the attribute (browser default). Named `autoCorrect` (capital `C`), not `autocorrect`,
* to dodge a TS `lib.dom.d.ts` collision -- same fix as ``/``. */
autoCorrect:string;loading:boolean;
/** Optional loading-copy override. Omission localizes `tableLoading`; a supplied string renders verbatim. */
loadingLabel?:string;
/** How `loading` renders. `'spinner'` (the default, unchanged output) replaces the whole grid
* with an indeterminate spinner. `'skeleton'` instead renders the real table — the same
* `` (declared *and* drag-resized widths included), the same ` `, the filter
* field and the pagination footer — and fills ` ` with placeholder rows, so a cold load
* sketches the grid's shape rather than collapsing to a spinner and reflowing when the rows
* land. Kept separate from `loading` rather than widening it to a string union, so
* `?loading=${…}` bindings and `el.loading === true` checks keep working.
* When `columns` is empty, a skeleton request temporarily renders the spinner: loading still
* takes precedence over the no-columns empty state, but there is no schema to sketch yet.
*
* Column *widths* only stay pixel-identical across the load if the browser isn't sizing them
* from cell content: declare `columns[].width`, or set `layout="fixed"`. Under the default
* `table-layout: auto`, placeholder cells have no intrinsic width, so the columns re-measure
* when real content arrives — exactly as they do between any two different data sets. */
loadingAppearance:TableLoadingAppearance;
/** Number of placeholder rows rendered by `loadingAppearance="skeleton"`. `0` (the default)
* renders 3 placeholders for the ordinary bounded default, or derives a non-default explicit
* `pageSize` (capped at 20). Positive explicit values are also capped at 20. Ignored entirely
* under the default spinner appearance. */
skeletonRows:number;
/** Inserts a non-focusable group header row wherever this key changes between consecutive
* rendered rows. Supply `rows` with each group already contiguous — the table does not
* re-order them to make them so, and the group order it renders is their first-appearance
* order in `rows`. A client-mode sort (`sortMode: 'client'`) is applied *within* each group
* rather than across the whole set, so sorting a grouped table on a column unrelated to the
* group key reorders rows inside their groups and leaves the grouping itself intact. Sorting on
* a column whose value is constant inside every group — the group column itself, most obviously
* — reorders the *groups* by that value instead, since there is nothing to reorder within
* them. */
groupBy?:(row:T)=>string|number;groupLabel?:(key:string|number,rows:readonly T[])=>unknown;
/** Maximum rows mounted per page. Defaults to 100 and normalizes into 1..500 so a bare table
* never creates an unbounded row-by-column DOM projection. */
pageSize:number;
/** Current page. Client pagination updates it on accepted navigation; server pagination leaves it
* controlled and only emits `lr-page-change`. */
page:number;
/** Total item count for server pagination; `-1` derives it from filtered rows. */
totalItems:number;paginationMode:'client'|'server';
/** Marks server pagination as indeterminate -- the caller has no total item count, only whether
* one more page exists (`hasNext`). Forwarded to the nested `` as its own
* indeterminate mode (a `total="-1"` sentinel; see that component's docs), which then renders
* previous/next only, with no numbered page list and no item-range summary. A dedicated boolean
* rather than a second magic `totalItems` value: `totalItems="-1"` already means "derive from
* the currently matching rows", and stacking a different meaning onto another negative number
* would be exactly the kind of easy-to-mistake sentinel this library avoids elsewhere. Ignored
* outside `paginationMode: 'server'` -- client mode always knows the exact row count it slices,
* so it never needs the indeterminate layout. */
unknownTotal:boolean;
/** Whether at least one more page exists past the current one. Consulted only alongside
* `unknownTotal`; forwarded verbatim to the nested ``'s own `hasNext`. Defaults to
* `true` so an indeterminate server pager stays navigable until the caller's API reports
* otherwise. */
hasNext:boolean;
/** Renders a full-width panel beneath a row when that row's key is in
* `expandedRowKeys`. Table-level (not per-column) since the panel spans
* every column via `colspan`. Setting this makes every row render a
* leading chevron-toggle cell before all data columns; omit for no
* leading cell at all (unchanged output). The returned content renders inside this
* component's shadow root, behind the `expanded-cell` part -- page-level CSS selectors cannot
* reach it, and `::part(expanded-cell)` only reaches that wrapping ``, not the descendants
* this callback returns (`::part()` is a pseudo-element; only pseudo-classes may follow it, so
* `::part(expanded-cell) .child` never matches, the same limitation `cell(row)`'s returned
* anchors run into). Style such content by returning already-styled elements -- inline
* `style`, or elements that reference this table's own `--lr-*` design tokens, which inherit
* across the shadow boundary like any custom property -- rather than depending on a
* page-level selector to find it. When script has to reach the rendered panel anyway (to
* measure it or scroll it into view), `expandedContentElement(rowKey)` resolves that ` `;
* `rowElement()` does not, since the panel is a sibling ` ` rather than part of the row. */
expandedContent?:(row:T)=>unknown;
/** Gates whether a given row gets an interactive chevron/toggle at all,
* when `expandedContent` is set. Omit to make every row expandable. A
* row that fails this check still gets a leading cell (for column
* alignment) but it renders empty — no button, no `aria-expanded`, no
* click handler. */
canExpand?:(row:T)=>boolean;
/** Accessible name for one row's expand/collapse chevron, read once per render for that row,
* exactly like a column's `editLabel`/`cellTitle`. Consumer-owned text: it is used verbatim and
* never passed through the localization runtime.
*
* Omit it and every chevron in the table shares the same localized `expand`/`collapse` name,
* which carries no row context. That is fine for a handful of rows, but each chevron is its own
* Tab stop, so a long table otherwise announces the same two names over and over with no way to
* tell the rows apart. There is no default row context to add here: this component has no
* row-header notion to derive one from (`rowKey` is an opaque identity, not a label). */
rowExpandLabel?:(row:T,expanded:boolean)=>string;
/** Open/closed state, bounded to 10,000 keys and keyed the same way as `rowKey`/
* `selectedRowKeys`. Under the default `expansionMode: 'none'` the table never mutates this
* itself — it only reads it to decide which rows currently render `expandedContent`, and the
* consumer toggles it in response to `lr-row-expand-toggle`. Under `expansionMode: 'single'`
* or `'multiple'` the table writes it on each accepted activation, exactly as `selectionMode`
* does for `selectedRowKeys`, and a `preventDefault()` on `lr-row-expand-request` hands one
* change back to the consumer. Reads return immutable detached `ReadonlySet`
* facades; malformed and whitespace-only string keys are omitted and valid off-page keys remain
* controlled in every mode — filtering, sorting and pagination never clear them. Reassign a new
* set to update. */
private _expandedKeys;get expandedRowKeys():ReadonlySet;set expandedRowKeys(value:ReadonlySet);
/** Re-types an internally computed key set for a write to `selectedRowKeys`/`expandedRowKeys`.
* Every key this component derives is the `string | number` union `keyOf()` produces, while a
* parameterized element's public surface promises the narrower `K`; the two are only
* convertible through `unknown`, so the conversion lives in these two helpers rather than at
* each of the six call sites. Nothing is normalized away by going through it -- both setters
* re-run `keySet()` on whatever they receive. */
private asKeySet;
/** The {@link asKeySet} counterpart for a `K`-typed event detail's key list. */
private asKeyList;
/** Overrides the auto-derived heat-tint domain (min/max of every `heatValue` result across every
* currently-rendered row — post-sort, pre-pagination, the same rows `footer(rows)` already sees).
* Unset computes the domain automatically from the data, spanning every `heatValue`-defining
* column together (a single shared scale across the whole grid, not one scale per column). */
heatTintScale?:{min?:number;max?:number;};
/** Renders a trailing `` on every body row holding this row's total. Same
* "consumer computes/renders, table only positions" contract as the existing per-column
* `footer(rows)` — does not assume addition, so a non-sum aggregate works identically. Omit for
* no trailing column at all (unchanged output). */
rowTotal?:(row:T)=>unknown;
/** Renders the bottom-right cell (row-total column × footer row). Only rendered when both
* `rowTotal` is set **and** at least one column defines `footer` — otherwise there is no footer
* row for it to occupy, and this renders nothing. */
grandTotal?:(rows:readonly T[])=>unknown;hasMore:boolean;
/** Optional copy overrides. Omission localizes the matching message key; supplied strings,
* including the built-in English text or an empty string, render verbatim. */
moreLabel?:string;
/** Non-`false` replaces ` `'s row content with a built-in failed-load state, while the
* surrounding ``, filter field, and pagination stay mounted — unlike either data-empty
* branch below, which this state overrides and which replace that chrome too. Precedence when
* more than one applies at once: `loading` beats `error` beats every empty branch, so a
* `loading` table never shows a stale `error`, and an `error` table never falls through to
* "no rows"/"no columns" copy underneath it. Reflected so `[error]` is selectable from
* page-level CSS the same way `[loading]` already is. */
error:boolean;
/** Optional failed-load heading override. Omission localizes `tableLoadFailed`; a supplied
* string, including the built-in English text or an empty string, renders verbatim. Has no
* effect once the `error` slot is filled. */
errorHeading?:string;
/** Optional failed-load description. Empty by default (no description line); a supplied string,
* including the built-in English value, renders verbatim — never localized, the same contract
* as `emptyDescription`. Has no effect once the `error` slot is filled. */
errorDescription:string;
/** Opts this table into announcing a failed-load state it already carries when it first mounts,
* through the same shared assertive region and the same heading text the later `error`
* transition announces, so the two paths cannot drift. Leave unset for a table that is part of
* the page a user is arriving on: the built-in error state renders in document order and
* repeating it is noise. Set it when the table is created in response to a user action — a
* reload that rejects mounts a fresh `error` table whose failure would otherwise never be
* spoken. Read once, on the first update: a later reconnection or adoption stages the same
* state again rather than replaying the announcement, and later `error` transitions announce
* either way. Deliberately not forwarded to the composed `[part='error']` ``, whose
* own `announce` stays unset so the failure is spoken once, not twice. Remove any host
* `role="status"`/`role="alert"` hand-added before this property existed once it is set --
* otherwise the failure is announced a third time, through the native role as well. */
announce:boolean;emptyHeading?:string;emptyDescription:string;
/** Overrides the built-in `[part='empty']` state's `compact` rendering. Leave `undefined` (the
* default) to keep each branch's own built-in behavior: the whole-table states (no columns, no
* rows) render spacious, while the in-table filtered/paginated-to-zero state — which sits below
* the filter field inside `[part='base']` — renders compact. `empty-compact="false"` forces the
* spacious rendering everywhere. Has no effect once the `empty` slot is filled. */
emptyCompact?:boolean;noColumnsHeading?:string;noColumnsDescription:string;revealColumnsLabel?:string;hideColumnsLabel?:string;
/** Whether the current rendered allocation actually hides at least one `priority` column. This
* read-only state becomes false once `priorityColumnsVisible` reveals them; the toggle remains
* available at narrow allocations through an internal capability measurement. */
hasHiddenPriorityColumns:boolean;private priorityToggleAvailable;
/** Whether the reveal/hide control is currently offered at all -- the public, read-only
* counterpart of the measurement `[part='reveal-columns-button']` itself renders from. True
* while at least one `priority` column is actually hidden at the current allocation, and it
* stays true once `priorityColumnsVisible` has revealed those columns (otherwise the control
* would remove itself the moment it was used, stranding the columns visible). Always false with
* no `priority` column declared, which is the state the inert-configuration development
* warning describes. Remeasured from the live DOM after every render and on every container
* resize, so read it after `await table.updateComplete`. */
get priorityColumnsToggleAvailable():boolean;
/** Forces `priority`-hidden columns back into view, overriding the
* measured-overflow hide rules in table.styles.ts. Toggles itself on
* `[part='reveal-columns-button']` activation by default — no external
* wiring is required for the button to work. Also settable from outside
* (property or the reflected `priority-columns-visible` attribute) to restore a
* previously-persisted preference. The single
* `lr-priority-columns-visibility-change` event reports button-driven changes.
* @default false */
priorityColumnsVisible:boolean;
/** Persists `priorityColumnsVisible` to `localStorage` across reloads when set. Namespaced as
* `lr-table:${storageKey}`. Restoration never overwrites a `priorityColumnsVisible` the consumer
* declared on the same mount (`priority-columns-visible` present, or a
* `.priorityColumnsVisible=${...}` binding) -- including a binding that pins it to `false`, its
* own default. The same "explicit beats persisted" guarantee as `lr-app-rail`'s and
* `lr-widget`'s `storage-key` restores, which share this one's write-tracking mechanism. */
storageKey?:string;private get storageFullKey();
/** Skips the very first `updated()` pass so mounting never writes to storage -- `willUpdate()`
* restored `priorityColumnsVisible` on that first pass, and Lit has already flipped `hasUpdated` to true
* by the time `updated()` runs, so a dedicated flag is needed. Mirrors `lr-app-rail`'s
* `persistReady`. */
private persistReady;private announcementSink?;private errorAnnouncementSink?;private firstUpdateAnnouncementsReady;
/** Roving-tabindex position among header cells; `null` until a header is
* clicked/navigated to, at which point `focusedColKey()` falls back to
* the first column. */
private activeColKey;
/** Roving-tabindex position among body rows; `null` until a row is
* clicked/navigated to, at which point `focusedRowKey()` falls back to
* the first surviving `selectedRowKeys` member (if it matches a row) or the first row. */
private activeRowKey;private editingCell;
/** The persistent (`editTrigger: 'always'`) editor cell that most recently took focus, recorded by
* the delegated `focusin` handler. `repeat()` is keyed by row key, so a re-sort *moves* the
* ` ` node (its typed value rides along) rather than recreating it -- but a DOM move drops
* focus, so `updated()` puts it back. Deliberately non-reactive: it tracks focus, and writing it
* must never schedule a render. */
private focusedEditorCell;
/** Whether `focusedEditorCell` still actually held focus when the in-flight update started.
* Captured in `willUpdate()`, i.e. before `render()` has had the chance to move the node out
* from under it. Without this, a record left behind by a user who has since clicked away
* entirely (no `focusin` reaches this component to clear it) would let any later, unrelated
* update yank focus back into the table. */
private editorHadFocusBeforeUpdate;
/** Focused roving member captured before a controlled collection update removes or moves its
* DOM node. `targetKey` is resolved from the new collection before render; `updated()` only has
* to put focus on the already-correct `tabindex="0"` owner. */
private rovingFocusSnapshot;private _resizedColumnWidths;
/** Opened immediately before each cancelable `lr-column-resize` commit and read immediately
* after it. Both commit paths apply the new width optimistically and roll it back when the
* event is vetoed -- a write that lands *after* the synchronous dispatch, so without this it
* also overwrites a width a listener resolved for itself from inside that dispatch (it refuses
* the proposed step and drives the component's own resize affordance instead). Tracking that a
* write happened, rather than comparing before/after widths, is what keeps "the listener chose
* this width" distinguishable from "nothing touched it". */
private readonly resizeWriteGuard;
/** Accessor-backed purely so every write marks `resizeWriteGuard`; the map itself is replaced,
* never mutated in place, exactly as before. */
private get resizedColumnWidths();private set resizedColumnWidths(value);private resizeState?;
/** Window that owns the active resize gesture's global pointer listeners. */
private resizeEventWindow?;private rowsByKey;private rowsLocale?;private columnsByKey;
/** Watches `[part='base']` and `[part='table']` for the size changes that
* `recomputeHiddenPriorityColumns()`'s measured-overflow check reacts to — so a
* `priority` column flipping hidden/visible from an *external* width
* change (a window resize, an ancestor flex-layout reflow, ...) is caught
* even though no Lit-tracked property changed. Mirrors
* lite-chart.ts's connectedCallback()/disconnectedCallback() ResizeObserver
* lifecycle. */
private resizeObserver?;
/** Owner-bound rAF for the coalesced `resizeObserver` callback below — an animated ancestor resize (a
* CSS transition/drag on a containing panel) can fire the observer once per animation frame,
* and each tick's full synchronous read+write pass (offsetParent over every priority header,
* a fresh `[data-col-key]` query per sticky column, an aria-valuenow write per resize handle)
* would otherwise run unbatched on every single one of them. Mirrors lite-chart.ts's/
* heatmap.class.ts's own `drawRafId` coalescing pattern. */
private layoutFrame;
/** The `[part='base']` element `resizeObserver` is currently observing —
* `render()`'s columns/rows-empty branches swap in the built-in
* (or `empty`-slotted) empty state instead,
* a different template shape that gives `[part='base']` a fresh DOM
* identity on the next non-empty render, so `updated()` re-observes
* whenever this no longer matches the live element. */
private observedBase?;
/** The rendered `` is observed separately because intrinsic content can grow its width
* without changing `[part='base']`'s own border box. */
private observedTable?;private readonly observedHeaders;
/** Last-measured rendered width (border box, summed across every header sharing that tier) of
* each priority tier while it was actually visible -- `display: none` collapses an element's own
* box to nothing, so a hidden tier's contribution to the table's full (everything-visible) width
* has to come from here instead of a live measurement. Refreshed every layout pass in which that
* tier's headers are actually rendered (including while `priorityColumnsVisible` force-reveals
* them), which for a newly-hidden tier is every pass up to and including the one that first hides
* it -- so the cache is always populated before it is ever needed. Never reset on disconnect: a
* reconnect's first measurement pass re-hides from a stale-but-still-reasonable cached width
* sooner than it otherwise could, and a genuinely stale entry self-corrects the next time that
* tier is visible. */
private readonly priorityTierNaturalWidth;
/** Last-measured natural (unstretched) rendered width of every ALWAYS-visible (no `priority`)
* header combined, mirroring `priorityTierNaturalWidth` above for the one group that is never
* itself hidden. It exists for the same reason: once any tier is hidden, `[part='table']`'s own
* `inline-size: 100%` (table.styles.ts) makes the browser's auto table layout stretch every
* still-rendered column to fill whatever room the hidden ones vacated, so `[part='base']`'s own
* `scrollWidth` stops carrying the always-visible group's true content width and starts tracking
* `clientWidth` instead -- reconstructing "how wide would everything be" from that stretched
* number silently turns "would the full set fit" into "does the CURRENT container happen to have
* spare room", which never re-admits a hidden tier once the remainder alone fits any container.
* Refreshed only on a pass where `[part='base']` is genuinely too wide for its container
* (`scrollWidth - clientWidth` exceeds the tolerance, computed from whatever the previous pass
* left rendered) -- the one condition under which nothing on screen has spare room to stretch
* into, so every currently-rendered header's own rect reports its real content width. That
* condition necessarily holds on the pass that first decides to hide anything (the hide decision
* itself requires it), so this is always populated before `recomputeHiddenPriorityColumns()` ever
* needs it for a widening pass, and a stale entry only lingers while nothing overflows -- exactly
* when substituting it for a live, potentially-stretched reading is the correction, not a risk. */
private alwaysVisibleNaturalWidth;private parsePixelLength;
/** The themed floor a column may be dragged/keyed down to, in used pixels. `rem`/`em` resolve
* against the live root/own font size through the shared `resolveCssLength()` -- a hardcoded
* `* 16` would pick the wrong floor on a page whose root font-size isn't the browser default.
* A token in a unit with no used pixel length here (`ch`, `pt`, `calc()`, a bare `%` with no
* base) falls back to DEFAULT_RESIZE_MIN_WIDTH_PX rather than being read as raw pixels. */
private minimumResizeWidth;private maximumResizeWidth;private currentResizeWidth;private resizeValueText;private resizeColumnTo;private renderedColumnWidth;private onResizePointerDown;private onResizePointerMove;private onResizeKeyDown;private renderResizeHandle;private syncResizeHandleValues;private onResizePointerEnd;private detachResizePointerListeners;private rollbackResizePreview;private cancelResizeGesture;
/** Coalesces however many `resizeObserver` callback ticks land in one animation frame (an
* animated/dragged ancestor resize can fire the observer once per frame) into a single
* read+write pass, instead of re-running `recomputeHiddenPriorityColumns()` / `applyStickyOffsets()` /
* `syncResizeHandleValues()` -- each its own DOM query plus per-element measurement -- on every
* tick. A second tick that lands while a frame is already pending is a no-op; the id resets once
* the scheduled frame runs, so the very next tick after that schedules a fresh one. */
private scheduleLayoutSync;protected firstUpdated(changed:PropertyValues):void;connectedCallback():void;disconnectedCallback():void;private releaseAnnouncementSink;private syncAnnouncementSink;private localizedOverride;private loadingText;private observeBase;private observeTable;
/** Applies the opt-in responsive scroll policy from rendered geometry. A one-pixel tolerance
* absorbs CSSOM's integer rounding of fractional layouts, matching the library's other
* overflow-aware controls. Existing `'self'` and `'page'` modes never retain this internal
* measurement marker, so their established CSS remains authoritative. */
private syncAutoScrollMode;private observeHeaders;
/** Every currently-rendered `` sharing `tier` -- usually one, but `columns` may declare more
* than one column at the same tier, and they hide/reveal together. */
private priorityTierHeaders;
/** Recomputes the measured-overflow priority-hide state from the live DOM: writes
* `data-hide-priority-low`/`-medium` on `[part='base']` (table.styles.ts's replacement for a
* fixed `@container` breakpoint), and separately tracks whether the reveal/hide toggle remains
* useful while force-visible. Shares its overflow signal -- `[part='base']`'s `scrollWidth` versus
* its `clientWidth` -- with `syncAutoScrollMode()`'s `scroll-mode="auto"` check, so the two
* responsive systems key off the same measurement instead of disagreeing about whether the table
* is actually too wide for its container. A tier hides only once hiding it would actually help:
* `'low'` first (reconstructing the fully-visible width from every group's cached natural width
* below), then `'medium'` on top of that if the table would still overflow with just `'low'` gone.
* Writing the same decision this function already reached is a no-op `toggleAttribute()` call,
* which keeps the `ResizeObserver` round-trip this triggers (hiding a column changes
* `[part='base']`'s/`[part='table']`'s own measured size) a fixed point rather than a layout
* thrash: the next pass reconstructs the same full width from the same cached natural widths,
* reaches the same decision, and writes nothing further -- in either direction, hiding or
* restoring. Called from `updated()` (covers a change driven by
* `columns`/`rows`/`priorityColumnsVisible` rather than a container resize) and
* from the `ResizeObserver` callback (covers a container resize with no
* Lit-tracked property change at all). */
private recomputeHiddenPriorityColumns;private rehomeFocusedColumn;private keyOf;
/** The one identity projection shared by filtering, sorting, pagination, focus, actions, events,
* totals, and rendering. Identity is resolved once per source occurrence and retained on the
* entry, so a stateful `rowKey` callback cannot make those consumers disagree within one model. */
private cachedCanonicalRowEntries;private canonicalRowEntriesInputs;private canonicalRowEntries;
/** Memoized across update cycles and re-validated against the exact inputs the computation
* reads — `rows` and `filter` by identity, the trimmed filter text, and (only when there is
* text to case-fold at all) the effective locale. This method is read (directly or
* transitively, via `matchingTotalItems`/`pageCount`/`appliedPage`/`renderedEntries()`) around
* a dozen times across one `willUpdate()` + `render()` pass, and an *unrelated* reactive
* update (a roving-tabindex move, an inline-editor open, ...) shouldn't re-run the
* `JSON.stringify()`-per-row default filter over the full `rows` array even once — comparing
* the recorded inputs instead of dropping the cache on every update keeps both cases to a
* single filtering pass. The locale is compared as its resolved string, so a change that
* arrives without a matching reactive-property key (an ancestor `lang` edit picked up on the
* next update, `setLyraLocale()`'s keyless `requestUpdate()`) still recomputes. */
private cachedMatchingEntries;private matchingEntriesInputs;private matchingEntries;
/** Read-time-safe view of `pageSize` -- finite, truncated and bounded to 1..500.
* Mirrors ``'s own identically-named getter (this component composes that
* primitive for the actual pagination UI in `render()`, but slices `rows` itself for client-mode
* pagination, so it needs the same safe count independently). */
private get normalizedPageSize();
/** Placeholder row count actually rendered by `loadingAppearance="skeleton"`. An explicit,
* positive `skeletonRows` wins after applying the shared bound; otherwise the count is derived
* from the normalized `pageSize` under the same bound. */
private get effectiveSkeletonRows();
/** `totalItems: -1` (the default) is a sentinel meaning "derive from filtered rows" -- normalize
* first so a non-finite/garbage `totalItems` degrades to that same derived-count fallback
* instead of propagating NaN, while a genuine non-negative value is still honored verbatim. */
private get matchingTotalItems();
/** `unknownTotal`, gated to the one mode it applies to -- client mode always knows the exact row
* count it is slicing, so it never enters the nested ``'s indeterminate layout. */
private get serverUnknownTotal();private get pageCount();
/** Read-time-safe view of `page`, clamped to `[1, pageCount]` -- mirrors
* ``'s own `currentPage` getter and, like ``'s `currentTime`
* setter, clamps against a dynamic, just-computed upper bound rather than a fixed one. */
private get appliedPage();
/** Memoized on the same principle as `matchingEntries()` above, and re-validated against the
* exact inputs the sort reads. `renderedEntries()` is read several times per update pass (the
* `rowsByKey` rebuild, `focusedRowKey()`, and `render()` itself), and a miss costs one
* consumer-supplied `sortValue()`/`cell()` call per row on top of the comparison pass — sorting
* once per reader would multiply that callback work by the number of readers for no benefit.
* `columns` is compared by identity because the ordering reads `sortValue`/`cell` off the
* active column object; `locale` because it selects the collator. */
private cachedSortedEntries;private sortedEntriesInputs;
/** `matchingEntries()` reordered by the active sort column. A no-op — the filtered array is
* returned by identity, never copied — under `sortMode: 'server'`, with no `sortKey`, or when
* `sortKey` names a column that is missing or not `sortable`, which is what keeps a table that
* never sets `sortKey` rendering its input order verbatim.
*
* When `groupBy` is set the sort is applied *within* each group rather than across the whole
* set. `render()` emits a group header wherever the group key changes between consecutive
* rendered rows, so a flat global sort on a column uncorrelated with the group key would
* interleave the groups and emit a header before nearly every row — the grouping would be
* visually destroyed by sorting. Groups keep their first-appearance order in `rows` (a `Map`
* iterates in insertion order); the group key is deliberately *not* collated, because the
* consumer controls group order through the order it supplies `rows` in, exactly as it does
* when no sort is active.
*
* The one exception: when the active column's value is constant inside *every* group, the
* within-group sort is provably inert, so the *groups* are ordered by that constant value
* instead. Without it, sorting on the group column would flip `aria-sort` and the chevron while
* changing nothing — announcing an ordering the table never applied. */
private sortedEntries;private renderedEntries;
/** `rows` after filtering and (client-mode) sorting, ignoring pagination -- the same set
* `columns[].footer(rows)`/`grandTotal(rows)` and the heat-tint domain already compute over.
* Lets a consumer that needs "what the grid currently shows" (e.g. exporting the visible rows)
* read this instead of re-implementing filtering/sorting itself. Reads `sortedEntries()`'s
* existing memoized cache, so an unrelated update (a roving-tabindex move, an inline-editor
* open, ...) costs nothing extra here. Always a fresh, frozen array: mutating what this returns
* cannot reach or corrupt the table's own internal state. */
get viewRows():readonly T[];
/** `viewRows` sliced to the page currently rendered in `` -- exactly the rows on screen
* right now. Same defensive-copy guarantee as `viewRows`. */
get pageRows():readonly T[];private onFilterInput;private onClearFilter;private stopOwnedEvent;private onNativeFocus;private onNativeBlur;private onPaginationChange;
/** The header cell that currently owns `tabindex="0"`. */
private focusedColKey;
/** The body row that currently owns `tabindex="0"`. */
private focusedRowKey;private rowsAffectingRovingFocusChanged;
/** Captures a direct header/row focus owner before `repeat()` moves or removes it. Nested
* controls and persistent editors deliberately do not qualify: they own their own focus
* contracts, and the editor restoration path below handles the one supported moved-node case. */
private captureRovingFocus;
/** Resolves the captured key against the new controlled collection before render, so the new
* DOM immediately assigns `tabindex="0"` to the member `updated()` will focus. */
private resolveRovingFocusTarget;protected willUpdate(changed:PropertyValues):void;
/** Each sticky column's cumulative inline-start offset — the sum of the *rendered
* width* of every earlier sticky column — so multiple sticky columns
* stack left-to-right instead of all pinning to inset-inline-start: 0 and
* overlapping. Table columns are intrinsically sized (not fixed-width), so
* this can't be computed in CSS alone; it requires measuring the actual
* laid-out `offsetWidth` of each earlier sticky column's header cell. */
private stickyOffsets;
/** `[lo, hi]` of every `heatValue` result across every matching row (post-sort, pre-pagination) and
* every `heatValue`-defining column, or `null` when heat-tint mode is off or there's no usable
* domain (no numeric values and no override). `heatTintScale` overrides either or both bounds. */
private computeHeatDomain;
/** This cell's tint share as a CSS percentage string (e.g. `"42.00%"`), or `null` when the column
* has no `heatValue`, the domain is unavailable, or this row's value is missing/non-finite. */
private heatShare;private applyStickyOffsets;
/** Applies stickyOffsets()'s measured per-column offsets as an inline
* `--lr-table-sticky-offset` custom property on every header cell and
* body cell in that column (addressed by the shared `data-col-key`
* attribute). This is a post-render DOM measurement — column widths
* aren't known until after the browser has laid out this update's
* render() output — so it runs from `updated()`, not `willUpdate()`,
* intentionally kept as a separate pass from the rowsByKey/columnsByKey
* rebuild above: those two must stay in `willUpdate()` so `render()`'s
* own `focusedRowKey()` call sees the current update's identity maps
* (e.g. freshly-assigned `selectedRowKeys` resolving to the correct
* roving-tabindex row on the very first paint), whereas the sticky-offset
* measurement can only run after that same paint has happened. Only runs
* when `hasSticky` is true (opt-in) and simply recomputes on every
* update; column widths are measured per update so the current layout
* reflects the rendered columns. */
protected updated(changed:PropertyValues):void;
/** Restores only focus that the current render displaced. If another internal control or an
* outside element owns focus now, the snapshot is discarded instead of stealing it back. */
private restoreRovingFocus;
/** Header activation (click, Enter, Space) proposes one canonical sort transaction. Client mode
* owns the accepted state; server mode leaves state controlled while reporting the same commit. */
private activateColumn;private activateRow;
/** Whether any column opts into persistent (`editTrigger: 'always'`) editors --
* the table-level flag inferred from the columns themselves, mirroring how
* `heatValue`/`width` opt their own modes in with no separate boolean. */
private get hasAlwaysOnEditors();private editorValue;
/** Double-click only ever *opens* an editor, so an `'always'` column is
* deliberately excluded: its editor is already open, and setting
* `editingCell` for it would render a second, competing editor in the same
* cell. */
private startEditing;private commitEdit;
/** The `[part='cell-editor']` rendered in one specific body cell, or `null` when that row/column
* is not currently rendered (paginated away, filtered out, column removed) or holds no editor.
* Matched by walking `data-row-key`/`data-col-key` rather than by interpolating them into a
* selector: both are consumer-supplied strings, and the row key additionally carries an encoding
* prefix (`string:a`), so neither is safe to splat into CSS unescaped. */
private editorElementFor;
/** Records which persistent editor holds focus, and drops the record as soon as focus lands on
* anything else inside the grid. Only `'always'` columns are tracked: a double-click editor is
* closed (and its node removed) by the very updates this would restore focus across. */
private onTableFocusIn;
/** Puts focus back into the persistent editor this update moved it out of. Runs from `updated()`,
* after `render()`'s DOM moves have landed. A row that left the rendered set entirely
* (pagination, filtering) only clears the record: yanking focus to whichever unrelated row now
* occupies that position would be worse than losing it. */
private restoreAlwaysOnEditorFocus;
/** The editor owns its own keys. `stopPropagation()` is unconditional and stays that way: inside
* a text field arrow keys are caret movement, not grid navigation.
*
* Enter commits either flavor. For a double-click editor that also closes it (`commitEdit`
* clears `editingCell`); a persistent editor has no closed state to fall back to, so it stays
* open and keeps focus.
*
* Escape cancels a double-click edit, which is a real action, so it is consumed. A persistent
* editor has nothing to cancel back to -- `editingCell` was never set for it -- so Escape is
* left uncancelled and an ancestor dialog/popover still closes on it. */
private onEditorKeyDown;private onTableDoubleClick;
/** Chevron activation. Under the default `expansionMode: 'none'` this reports the activation and
* nothing else, which is all it ever did. Under a self-managed mode it runs the library's one
* request/commit helper: the cancelable `lr-row-expand-request` carries the proposed state, and
* only an unvetoed proposal writes `expandedRowKeys` and then announces the applied change with
* `lr-row-expand-toggle`.
*
* No `VetoWriteGuard` is passed, matching ``'s group-collapse pair: a request
* listener's own `expandedRowKeys` assignment runs inside `emit()`, i.e. before `commit()`
* writes, so today it is overwritten and only `preventDefault()` stops the built-in write.
* Passing a guard would newly let that assignment suppress the commit — a behaviour change
* rather than the shared shape. A listener that wants to own one change vetoes it. */
private activateExpandToggle;
/** The rendered `` for one row key, or `null` when that row is not in the current render
* output (filtered out, paginated away, or never present). Public counterpart of the
* `data-row-key` attribute the row carries: the attribute's value is a type-tagged encoding
* (`string:a` vs `number:1`), and a consumer-supplied key is not safe to interpolate into a CSS
* selector unescaped, so resolve it through here instead of building one. Reads the DOM as it
* stands — `await table.updateComplete` before calling it after changing any input. */
rowElement(rowKey:K):HTMLElement|null;
/** The rendered `` at one `(rowKey, columnKey)` pair, or `null` when either the row is not
* currently rendered or the column is not in `columns`. `columnKey` is the column's own `key`,
* which is exactly what the cell's public `data-col-key` attribute carries. Same
* `updateComplete` contract as {@link rowElement}. */
cellElement(rowKey:K,columnKey:string):HTMLElement|null;
/** Opens the inline editor at one `(rowKey, columnKey)` pair -- the imperative entry point behind
* the `F2`/`Enter` keyboard shortcuts (`onRowKeyDown`) and the double-click pointer path
* (`onTableDoubleClick`), for a consumer that wants to bind its own key, menu action, or other
* trigger to the same effect. A no-op, not a throw, for an unknown row key, an unknown column
* key, or a column with no `editTrigger` at all -- exactly `startEditing`'s own guard, so this
* never opens a second, competing editor and never targets a column that has no editor to open.
*
* An `editTrigger: 'always'` column's editor is already open from first paint, so there is
* nothing to *start*; this instead moves focus into that column's already-rendered editor,
* mirroring what `F2`/`Enter`/double-click do for a `'double-click'` column. Fire-and-forget --
* await `table.updateComplete` first if the newly-opened editor needs to be read back
* synchronously afterward (e.g. via {@link cellElement}). */
editCell(rowKey:K,columnKey:string):void;
/** The rendered `[part='expanded-cell']` holding one row's `expandedContent(row)` output, or
* `null` when that row is not currently rendered, is not expanded, or renders no panel at all.
* {@link rowElement} deliberately cannot reach this: the panel is a *sibling* ` ` of the data
* row, not a descendant of it, so it carries its own `data-expanded-row-key` attribute (encoded
* exactly like `data-row-key`, and left off the data-row attribute so every existing
* `[data-row-key]` row query keeps resolving one element per row). Same `updateComplete`
* contract as {@link rowElement}. */
expandedContentElement(rowKey:K):HTMLElement|null;
/** Row lookup by the already-encoded `data-row-key` token, shared by the public API above and
* the internal editor-focus bookkeeping, which only ever holds encoded tokens. Walks the rows
* rather than interpolating the token into a selector, for the escaping reason above. */
private rowElementForToken;private cellElementForToken;
/** Header cells currently in the tab sequence — excludes columns hidden by
* a `priority`-driven measured-overflow rule (table.styles.ts/
* `recomputeHiddenPriorityColumns()`), so Left/Right/
* Home/End never strand the roving tab stop on a `display: none` cell
* that `.focus()` would silently no-op on. Scoped to `th` — body ``s
* now carry the same `data-col-key` attribute (for the sticky-offset
* measurement pass) but must never be treated as header cells here. */
private visibleHeaders;private focusHeader;private focusRow;private toggleColumns;private onTableClick;private onTableKeyDown;private onHeaderKeyDown;
/** The editable (`editTrigger: 'double-click'`) ` ` that owns `target`, if any -- i.e. `target`
* itself is that cell, since it is the only body-cell shape that ever carries a `tabindex` (see
* the render template). Used to tell "a row's own keydown" apart from "a keydown on the row's
* own cell-level focus stop" without a second piece of `@state`: the answer is recomputed from
* `e.target` on every keydown instead of tracked. */
private editableCellFor;
/** This row's own editable cells, in DOM (visual, LTR) order, skipping one a priority-collapse
* hid (`offsetParent === null`, matching `visibleHeaders()`) or an inert ancestor would refuse
* `focus()` on silently (matching `lr-menu`'s `isNavigable()` shape) -- so ArrowLeft/ArrowRight
* below can never strand the cell-level focus stop on a target that cannot actually take it. */
private editableCellsInRow;
/** `tr` is always the row that owns the keydown -- resolved by the delegated caller via
* `closest('[data-row-key]')` -- regardless of whether `e.target` is the row itself or one of
* its own editable cells, so every branch below that reads `tr` (the roving ArrowUp/ArrowDown/
* Home/End switch) keeps working unchanged from a cell-focused keydown, with zero code of its
* own aware that cell-level focus exists. Only Enter/F2 (open an editor) and Space (still always
* row activation, on a cell exactly as on the row -- neither key is part of the conventional
* grid editing idiom) need to know which of the two actually has focus, via `editableCellFor`. */
private onRowKeyDown;
/** ArrowLeft/ArrowRight roving between a row's own tab stop and its editable cells -- the
* smallest addition that keeps the row's existing ArrowUp/ArrowDown/Home/End roving (above)
* fully authoritative: this only ever moves focus sideways, within the current row, and a row
* with no editable cell keeps its pre-existing behavior exactly (neither arrow key did anything
* at the row level before this method existed, so `cells.length === 0` intentionally leaves the
* key unhandled rather than calling `preventDefault()`).
*
* Direction is visual, not literal, matching `onHeaderKeyDown`'s own `isRtl()` mirroring: under
* RTL, ArrowLeft moves toward the visual end (forward) and ArrowRight toward the visual start
* (backward). From the row itself (`cell === null`), the forward key enters at the first
* editable cell and the backward key enters at the last, so a table with editable columns on
* both a left- and right-heavy layout is reachable from either direction. From a cell, the
* backward key at the first editable cell returns focus to the row (mirroring the header's own
* "ArrowUp from the first body row returns to the header" boundary); the forward key at the last
* editable cell simply stays put, matching the header's own clamped ends. */
private onRowLateralKeyDown;
/** One body cell's inline editor.
*
* For `editType: 'text'`/`'number'`, the persistent (`editTrigger: 'always'`) and double-click
* flavors differ in exactly one binding: the persistent editor binds `value` as a **content
* attribute**, the double-click one keeps the `.value` **property**. Native HTML sets an input's
* dirty-value flag on the user's first edit, after which content-attribute updates no longer
* overwrite what is displayed -- so an out-of-band `rows` update to a cell the user is already
* typing into leaves their draft alone, with no is-focused bookkeeping of the library's own. An
* untouched persistent editor has no dirty flag set, so it still picks up a new `rows` value
* normally. A double-click editor is short-lived and opens against the value it is editing, so the
* property binding's deliberate re-assert is right for it. Two templates rather than one because a
* lit template literal fixes each binding's kind at authoring time.
*
* `editType: 'select'` renders a native `` instead, populated from `editOptions`
* (`col.editOptions ?? []`, so a column with none renders an empty, valueless `` rather
* than throwing). ``/`` carry no native dirty-value flag, so there is no
* content-attribute-vs-property distinction to make here -- both flavors bind `.value` the same
* way, and (unlike the text/number editors) a persistent select editor does not protect an
* in-progress, uncommitted selection from an out-of-band `rows` update to that cell.
*
* No `tabindex` on any of them: a persistent editor is a plain tab stop, exactly like the
* row-expand toggle rendered a few lines above, and stays outside the header/row roving model. */
/**
* Renders one cell, tolerating a column that omits its required `cell` renderer.
*
* `TableColumn.cell` is typed and documented required, but columns arrive through a lit
* `.columns=${[...]}` property binding, and lit-html property bindings are not type-checked by
* `tsc` -- only by lit-analyzer, which many projects do not run. So required-ness is unenforced
* where it is actually written. Calling it unguarded would make a malformed column interrupt the
* table render.
*
* Degrade to an empty cell so the rest of the table stays usable, and emit one fixed
* development-only diagnostic for the page without retaining or reporting caller data.
*/
private renderCellValue;private renderCellEditor;
/** One rectangular placeholder, using ``'s canonical `shape` axis. `announce` is
* switched off on every one of them: `` defaults it
* to `true`, which would make each of the N x M cells its own `role="status"` live region and
* turn a single "Loading rows" announcement into a storm of them. The table's shared light-DOM
* announcement sink carries the post-mount state transition for the whole grid instead.
* A property binding (not `?announce=`) is required to assign `false` to a `true`-defaulting
* boolean property. */
private renderSkeletonPlaceholder;
/** `loadingAppearance="skeleton"`'s body rows. Deliberately built from the same per-column
* attributes (`data-col-key`/`data-align`/`data-priority`/`data-sticky`) and the same
* `cell`/`row` parts as a real row, so the measured-overflow priority-hide rules, the sticky-offset
* measurement pass and every cell style apply to them unchanged -- that, plus rendering inside
* the real ``/` `, is what keeps the grid's geometry stable across the load.
* They carry no `data-row-key` and no `tabindex`: they are not data rows, so the delegated
* click/keydown handlers and the roving tab stop ignore them. */
private renderSkeletonRows;
/** The failed-load content itself, shared by the in-grid error row and the standalone no-columns
* error branch in `render()` so the two cannot drift in copy, parts, or slot name -- both go
* through the shared {@link renderDataState} ladder renderer (`internal/data-state-renderer.ts`),
* which owns the `error`-prefixed part naming, the `error` slot wrapping, and the cancelable
* `lr-retry` request/commit pair. `compactDefault` differs between callers: the row sits inside
* an existing grid and defaults to compact, while the standalone branch owns the whole component
* box and matches the other full-area empty states. */
private renderErrorContent;
/** The single full-width row rendered in ` ` while `error` is true. Mirrors the `empty`
* slot/`` shape (same exported-part naming scheme, `error`-prefixed) so the two states
* stay visually and structurally consistent, plus the built-in retry affordance -- but unlike
* either data-empty branch in `render()`, the caller keeps ``, the filter field, and
* pagination mounted around this row instead of replacing them too. */
private renderErrorRow;render():TemplateResult;}declare global{interface HTMLElementTagNameMap{'lr-table':LyraTable;}}export{};