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 `