import { LitElement } from "lit"; import type { TemplateResult } from "lit"; import type { TableColumn, TableRow } from "../table/index.js"; import "../primitives/index.js"; export interface DataTableColumn extends TableColumn { sortable?: boolean; /** Raw value to sort this column by, when the displayed cell is a formatted string the table can't compare correctly on its own (e.g. "412 KB" must sort as 421888, "2 min ago" as a timestamp). Returns the primitive used for comparison; the displayed cell (`row[key]`) is unchanged. Without it, number cells sort by magnitude and string cells sort lexically. */ sortValue?: (row: TableRow) => string | number | null | undefined; /** Per-column formatter → the displayed cell TEXT (data-only, ADR 0008; the output is rendered as text, never markup). The raw `row[key]` is unchanged and still used for sorting (or `sortValue`). */ format?: (value: unknown, row: TableRow) => string; /** Escape hatch for a rich, interactive cell (mirrors `renderDetail`): given the raw value, the row, and its index in the rendered page, return a Lit template, a DOM node, or a string. When set it OWNS the cell body and supersedes `format`; the consumer is responsible for escaping/trust. Sort still uses `sortValue` or the raw `row[key]`, never this output. Use it sparingly — a copy button, a status chip, a link — not to bypass the text-only cell model wholesale. */ renderCell?: (value: unknown, row: TableRow, index: number) => unknown; /** Render this column's cells in the monospace/tabular typescale. */ mono?: boolean; /** Lay a magnitude rail behind this column's figure so a value reads against the other values in the same column. Numeric columns only — the rail is sized from the raw `row[key]` (or `sortValue`), never the formatted text. In `server` mode the table only ever holds one page, so a comparable scale is impossible without `barMax`: the rail is suppressed until one is given rather than drawn against a per-page maximum. */ bar?: boolean; /** Denominator for `bar`. A number pins the scale. A function receives the rows the table currently holds — in client mode that is the whole dataset; in `server` mode it is one page, so server consumers should pass a number. */ barMax?: number | ((rows: TableRow[]) => number); /** Header group label. Consecutive columns sharing the same `group` render under one spanning `scope="colgroup"` cell in an extra header row; columns without a group keep a blank group-row cell. */ group?: string; /** Declared width in px. Also the width `resetColumnWidth` returns to. */ width?: number; /** Resize floor in px. */ minWidth?: number; /** Start pinned to the frame's left edge. */ pinned?: boolean; /** Frozen by configuration: the identity column. Implies `pinned`, and can be neither unpinned nor hidden — the grid chrome renders those actions disabled rather than absent. */ locked?: boolean; /** Per-column override of the element's `resizable-columns`. */ resizable?: boolean; /** This column's copy MAY wrap. Without it `setColumnWrapped` is a no-op — wrapping a numeric column is never right. */ wrappable?: boolean; /** Start wrapped (clamped to three lines) instead of one line with ellipsis. */ wrap?: boolean; /** Start hidden. A hidden column is not destroyed — it stays in `columns` so the chrome can offer it back. */ hidden?: boolean; /** Aggregate this column contributes to the `totals` row. */ total?: "sum" | "max" | "avg"; /** Formatter for this column's `total`. Separate from `format` because a cell formatter is handed a row, and an aggregate has no row to hand it. Without one the raw number is shown. */ formatTotal?: (value: number, rows: TableRow[]) => string; } export type { TableRow }; export type SortDirection = "asc" | "desc"; export interface DataTableSortChangeDetail { key: string; direction: SortDirection; } export interface DataTablePageChangeDetail { page: number; } export interface DataTableRowClickDetail { row: TableRow; index: number; } export type DataTableExpandMode = "single" | "multiple"; export interface DataTableRowExpandDetail { row: TableRow; index: number; key: string; open: boolean; } export type DataTableDensity = "comfortable" | "compact"; /** Aggregate line for one `group-by` run, returned by `groupSummary`. */ export interface DataTableGroupSummary { /** Muted qualifier beside the group name (e.g. `"3 files"`). */ meta?: string; /** Trailing figure, end-aligned in the group row's last cell. */ value?: string | number; /** 0–1 share of the whole, drawn as the group row's magnitude rail. */ share?: number; } export interface DataTableGroup { key: string; label: string; rows: TableRow[]; open: boolean; summary: DataTableGroupSummary | null; } export interface DataTableGroupToggleDetail { key: string; open: boolean; rows: TableRow[]; } /** Marks one row as a group header that still renders its own cells, returned by `rowHeader` (ADR 0041). */ export interface DataTableRowHeader { /** Draw the fold chip in the first laid-out cell at this state. Omit for a header row that does not fold. */ open?: boolean; /** Accessible name for the fold control (e.g. the file the row names). */ label?: string; } export interface DataTableRowHeaderToggleDetail { row: TableRow; index: number; open: boolean; } export interface DataTableColumnResizeDetail { key: string; width: number; } export interface DataTableColumnPinDetail { key: string; pinned: boolean; } export interface DataTableColumnVisibilityDetail { key: string; visible: boolean; } export interface DataTableColumnWrapDetail { key: string; wrapped: boolean; } /** * @fires xm-data-table-page-change - Fired when the page changes (`detail.page`). * @fires xm-data-table-sort-change - Fired when the sort changes (`detail.key`, `detail.direction`). * @fires xm-data-table-row-click - Fired when a clickable row is activated (`detail.row`, `detail.index`). * @fires xm-data-table-row-expand - Fired when a row's detail region opens (`detail.row`, `detail.index`, `detail.key`, `detail.open`). * @fires xm-data-table-row-collapse - Fired when a row's detail region closes (`detail.row`, `detail.index`, `detail.key`, `detail.open`). * @fires xm-data-table-group-toggle - Fired when a `group-by` header row folds or unfolds (`detail.key`, `detail.open`, `detail.rows`). * @fires xm-data-table-row-header-toggle - Fired when a `row-header` row's fold chip is pressed (`detail.row`, `detail.index`, `detail.open`). * @fires xm-data-table-column-resize - Fired when a column's width changes (`detail.key`, `detail.width`). * @fires xm-data-table-column-pin - Fired when a column is pinned or released (`detail.key`, `detail.pinned`). * @fires xm-data-table-column-visibility - Fired when a column is hidden or restored (`detail.key`, `detail.visible`). * @fires xm-data-table-column-wrap - Fired when a column's text wrapping is toggled (`detail.key`, `detail.wrapped`). */ export declare class XmDataTable extends LitElement { static styles: CSSStyleSheet[]; columns: DataTableColumn[]; rows: TableRow[]; loading: boolean; pageSize: number; emptyHeading: string; emptyText: string; /** Server/controlled mode (AD-14): the consumer owns sort + paging. The table renders `rows` verbatim, never sorts/slices a copy, derives the page count from `total-items` (not `rows.length`), and emits intents only. */ server: boolean; /** Total item count for server-mode paging (pages = ceil(total-items/page-size)). */ totalItems: number; /** Controlled sort indicator for server mode (`{ key, direction }`); reflected, never reordered locally. */ sort: DataTableSortChangeDetail | null; /** Make rows clickable — emits xm-data-table-row-click (AD-15). */ rowClick: boolean; /** Pin the header rows while the table body scrolls inside the frame. Pair with `max-height` (or a host height) so the frame actually scrolls. */ stickyHeader: boolean; /** Scroll-viewport cap for `sticky-header` — any CSS length ("360px", "50vh"). */ maxHeight: string; /** Stretch the frame to fill the host's full height instead of collapsing to content height. Pair with `sticky-header` and a definite-height host so the table reaches the bottom edge and the scrollbar pins there even when the rows don't fill the viewport. */ fill: boolean; /** Drop the frame's hairline border + corner radius for edge-to-edge embedding (the frame becomes flush with its container). */ flush: boolean; /** Enable per-row expansion (ADR 0019): adds a leading expander column whose chevron button toggles an in-place detail row under its data row. */ expandable: boolean; /** `single` (default) keeps at most one row open; `multiple` allows any number. */ expandMode: DataTableExpandMode; /** Column key whose value is a row's stable expansion identity. Without it, identity falls back to the row's position, which drifts across sort order and server-mode page swaps — set it whenever rows have an id. */ rowKey: string; /** Lazy per-row detail content — called only while the row is open, with the row and its index in the rendered page. May return a Lit template, a DOM node, or a string (rendered as text). */ renderDetail: ((row: TableRow, index: number) => unknown) | null; /** Controlled expansion (AD-14): when non-null the consumer owns the open set — toggles emit intents only. `null` (default) is uncontrolled. */ expandedKeys: string[] | null; /** Row rhythm. `compact` tightens the vertical cell padding one step so a long list fits on one screen; the type scale and hairlines are unchanged. */ density: DataTableDensity; /** Column key whose value groups consecutive rows under a foldable header row (ADR 0030). Grouping runs over the rendered page, after sort. */ groupBy: string; /** Aggregate line for a group's header row, called with that group's rows. */ groupSummary: ((rows: TableRow[], key: string) => DataTableGroupSummary) | null; /** Escape hatch owning the group header's body (mirrors `renderCell`). When set it supersedes the summary chrome; the fold control stays table-owned. */ renderGroupRow: ((group: DataTableGroup) => unknown) | null; /** Controlled folding (AD-14): when non-null the consumer owns the collapsed set — toggles emit intents only. `null` (default) is uncontrolled. */ collapsedGroups: string[] | null; /** Marks a row as a group header that keeps its own cells (ADR 0041). Use it when the header of a group is itself a record — `group-by` cannot express that, because it renders per-column aggregates over the children. The row takes the group row's surface; returning `open` also puts the table's fold chip in the first laid-out cell, and folding stays the consumer's state: the chip emits an intent, never a local change. */ rowHeader: ((row: TableRow, index: number) => DataTableRowHeader | null) | null; /** Trailing per-row action rail — a copy button, a download, a row menu. Revealed on row hover and on `:focus-within`, so it stays keyboard-reachable in source order. Row-click never fires from it (AD-15). */ rowActions: ((row: TableRow, index: number) => unknown) | null; /** Keep the action rail visible at rest instead of revealing it on hover. */ actionsAlways: boolean; /** Fixed-width column layout (ADR 0034): each column renders at a declared pixel width via `` + `table-layout: fixed`. This is what makes sticky offsets computable, so it also gates pinning and resizing. Off by default — without it the table auto-sizes exactly as before. */ columnLayout: boolean; /** Drag a header's trailing edge to resize; double-click resets to the column's declared `width`. Requires `column-layout`. */ resizableColumns: boolean; /** Grow the columns to fill the scroll frame whenever the declared widths add up to less than the space available. Surplus is shared across the unpinned columns in proportion to their declared width; an overflowing table is untouched and still scrolls. Requires `column-layout`. */ fitColumns: boolean; /** Render the aggregate `` row built from each column's `total`. Under `sticky-header` it pins to the bottom of the scroll frame. */ totals: boolean; /** Copy for the totals row's first cell (e.g. "18 tools"). */ totalsLabel: string; /** Replace the frame's native scrollbars with overlay rails, so nothing reserves a band inside the frame. Wheel and trackpad are untouched. */ overlayScroll: boolean; /** Controlled column widths (AD-14). `null` (default) is uncontrolled. */ columnWidths: Record | null; /** Controlled pinned set (AD-14). `null` (default) is uncontrolled. */ pinnedColumns: string[] | null; /** Controlled hidden set (AD-14). `null` (default) is uncontrolled. */ hiddenColumns: string[] | null; /** Controlled wrapped set (AD-14). `null` (default) is uncontrolled. */ wrappedColumns: string[] | null; /** Trailing control inside a header cell — the column's own options menu. Mirrors `renderCell` / `rowActions`: the consumer owns the button and whatever it opens; sorting and resizing stay table-owned. */ renderHeaderAction: ((column: DataTableColumn, index: number) => unknown) | null; private _sortKey; private _sortDirection; private _currentPage; private _openKeys; private _collapsedKeys; private _widths; private _pinned; private _hidden; private _wrapped; private _frameWidth; private _railX; private _railXFrac; private _railY; private _railYFrac; private _fitted; private _columnDefaultsFrom; private _frameObserver; private _engine; private _engineColumns; private _engineColumnDefs; private readonly _coreRowModel; private readonly _sortedRowModel; private readonly _paginationRowModel; willUpdate(changed: Map): void; private _seedColumnState; private _syncEngine; private _rowModel; private _layout; private _fitEnabled; private _fitToFrame; private _canResize; private _cellStyle; private _headerCellStyle; private _cellClasses; disconnectedCallback(): void; protected updated(): void; private _onFrameResize; render(): TemplateResult; private _renderTable; private _tableStyle; private _renderSizingRow; private _renderColGroup; private _groupRuns; private _groupStartIndexes; private _renderGroupRow; private _renderHeaderCell; private _renderHeaderBadges; private _renderDataRows; private _renderDataRow; private _renderCell; private _renderRowFold; private _renderTotalsRow; private _aggregateValue; private _aggregate; private _barShareOfValue; private _renderBar; private _renderActionsCell; private _renderGroupHeaderRow; private _renderGroupAggregateRow; private _renderExpanderCell; private _renderDetailRow; private _renderSkeletonRows; private _renderEmptyPanel; private _emptyPanelStyle; private _renderFooter; private _renderRails; private get _scroller(); private _onFrameScroll; private _syncRails; private _onRailXDown; private _onRailYDown; private _dragRail; /** Pin a column to the left edge, or release it. A `locked` column ignores this. */ pinColumn(key: string, pinned: boolean): void; /** Take a column out of the view, or put it back. A `locked` column ignores this. */ setColumnHidden(key: string, hidden: boolean): void; /** Wrap a column's copy to three lines, or return it to one line + ellipsis. Only a `wrappable` column responds. */ setColumnWrapped(key: string, wrapped: boolean): void; /** Set a column's width in px, clamped to its own floor and the shared cap. */ setColumnWidth(key: string, width: number): void; /** Return a column to its declared `width`. */ resetColumnWidth(key: string): void; /** Set (or clear, with `null`) the sort from outside the header row — a column options menu, or restoring a saved layout. Applies locally in client mode and emits either way, exactly like a header click; `sort` stays the server-mode controlled property and is untouched here. A cleared sort emits with an empty `key`. */ setSort(key: string | null, direction?: SortDirection): void; private _emit; private _onResizeStart; private _isEmpty; private _rowKeyOf; private _resolveKeyInPage; private _colSpan; private _rowGroups; private _showBar; private _clampShare; private _barShare; private _barValue; private _barScale; private _isNumericCol; private _effectivePageSize; private _pageCount; private _pagedRows; private _toggleSort; private _fromInteractiveDescendant; private _onRowClick; private _onRowKey; private _emitRowClick; private _toggleExpand; private _emitRowToggle; private _onExpanderKey; private _onDetailKey; private _collapseRow; private _focusExpander; private _toggleGroup; private _onHeaderKey; private _onPageChange; } declare global { interface HTMLElementTagNameMap { "xm-data-table": XmDataTable; } }