argus-table 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,5 @@
1
1
  import { controllable } from "./core/src/state/controllable.js";
2
+ import { PinScrollController } from "./controllers/pin-scroll-controller.js";
2
3
  import { startResizeDrag } from "./controllers/resize-drag.js";
3
4
  import { TableController } from "./controllers/table-controller.js";
4
5
  import { VirtualController } from "./controllers/virtual-controller.js";
@@ -8,7 +9,7 @@ import { renderLightDom } from "./render/cells.js";
8
9
  import { renderHeader } from "./render/header.js";
9
10
  import { baseStyles } from "./styles/base.js";
10
11
  import __decorate from "./_virtual/_@oxc-project_runtime@0.156.0/helpers/esm/decorate.js";
11
- import { LitElement, html } from "lit";
12
+ import { LitElement, html, nothing } from "lit";
12
13
  import { customElement, property } from "lit/decorators.js";
13
14
  import { styleMap } from "lit/directives/style-map.js";
14
15
  // src/argus-table.ts
@@ -46,6 +47,24 @@ function gridCell(target) {
46
47
  function rowIndexOf(row) {
47
48
  return Number(row?.getAttribute("aria-rowindex") ?? 1) - 2;
48
49
  }
50
+ /**
51
+ * The pinned edge's shadow, drawn once per side over the scroller rather than
52
+ * per row: a shadow on every pinned cell repaints on every scroll frame. It
53
+ * covers the pinned columns and casts `--argus-pinned-shadow` onto the rest;
54
+ * PinScrollController sizes it to the visible area.
55
+ */
56
+ function pinShadow(side, width, rows, rowPx) {
57
+ if (width === 0) return nothing;
58
+ return html`<div
59
+ class="pin-shadow"
60
+ data-side=${side}
61
+ aria-hidden="true"
62
+ style=${styleMap({
63
+ inlineSize: `${String(width)}px`,
64
+ "--_content-h": `${String((rows + 1) * rowPx)}px`
65
+ })}
66
+ ></div>`;
67
+ }
49
68
  /** CSS grid track for a column: fixed px, or a flex share with the min width as floor. */
50
69
  function track(column) {
51
70
  return column.flex === void 0 ? `${String(column.width)}px` : `minmax(${String(column.minWidth)}px, ${String(column.flex)}fr)`;
@@ -62,6 +81,7 @@ var ArgusTable = class ArgusTable extends LitElement {
62
81
  this.#internals = attachGridInternals(this);
63
82
  this.#table = new TableController();
64
83
  this.#virtual = new VirtualController(this, () => this.renderRoot.querySelector("[part~=\"table\"]"));
84
+ this.#pinScroll = new PinScrollController(this, () => this.renderRoot.querySelector("[part~=\"table\"]"));
65
85
  this.#rowPx = 40;
66
86
  this.#window = {
67
87
  rows: [],
@@ -214,6 +234,7 @@ var ArgusTable = class ArgusTable extends LitElement {
214
234
  #internals;
215
235
  #table;
216
236
  #virtual;
237
+ #pinScroll;
217
238
  #rowPx;
218
239
  #probeObserver;
219
240
  #window;
@@ -264,7 +285,8 @@ var ArgusTable = class ArgusTable extends LitElement {
264
285
  const model = this.#table.model;
265
286
  const columns = model.columns;
266
287
  this.#window = this.#virtual.computeWindow();
267
- const pinned = (side) => `${String(columns.reduce((sum, c) => c.pin === side ? sum + c.width : sum, 0))}px`;
288
+ const pinnedWidth = (side) => columns.reduce((sum, c) => c.pin === side ? sum + c.width : sum, 0);
289
+ const pinned = (side) => `${String(pinnedWidth(side))}px`;
268
290
  const style = {
269
291
  "--_cols": columns.map(track).join(" "),
270
292
  "--_width": `${String(model.totalWidth)}px`,
@@ -278,17 +300,19 @@ var ArgusTable = class ArgusTable extends LitElement {
278
300
  } : void 0;
279
301
  if (this.rowHeight !== void 0 && this.rowHeight > 0) style["--_row-height"] = `${String(this.rowHeight)}px`;
280
302
  return html`<div
281
- part="table"
282
- role="presentation"
283
- style=${styleMap(style)}
284
- @keydown=${this.#onKeydown}
285
- @focusin=${this.#onFocusin}
286
- @click=${this.#onClick}
287
- >
288
- ${renderHeader(columns, this.#headerHandlers, active?.index === -1 ? active.col : void 0)}
289
- ${model.rowCount === 0 ? html`<div part="empty"><slot name="empty">No rows</slot></div>` : renderBody(this.#window.rows, columns, this.#window.totalSize, bodyActive)}
290
- <div class="probe" aria-hidden="true"></div>
291
- </div>`;
303
+ part="table"
304
+ role="presentation"
305
+ style=${styleMap(style)}
306
+ @keydown=${this.#onKeydown}
307
+ @focusin=${this.#onFocusin}
308
+ @click=${this.#onClick}
309
+ >
310
+ ${renderHeader(columns, this.#headerHandlers, active?.index === -1 ? active.col : void 0)}
311
+ ${model.rowCount === 0 ? html`<div part="empty"><slot name="empty">No rows</slot></div>` : renderBody(this.#window.rows, columns, this.#window.totalSize, bodyActive)}
312
+ <div class="probe" aria-hidden="true"></div>
313
+ </div>
314
+ ${pinShadow("start", pinnedWidth("start"), model.rowCount, this.#rowPx)}
315
+ ${pinShadow("end", pinnedWidth("end"), model.rowCount, this.#rowPx)}`;
292
316
  }
293
317
  firstUpdated() {
294
318
  this.#observeProbe();
@@ -296,6 +320,7 @@ var ArgusTable = class ArgusTable extends LitElement {
296
320
  }
297
321
  updated(changed) {
298
322
  renderLightDom(this, this.#table.model, this.#window.rows);
323
+ if (changed.has("columns") || changed.has("columnSizing")) this.#pinScroll.measure();
299
324
  if (this.#focusPending) {
300
325
  this.#focusPending = false;
301
326
  this.#focusActive();
@@ -1 +1 @@
1
- {"version":3,"file":"argus-table.js","names":["_decorate"],"sources":["../src/argus-table.ts"],"sourcesContent":["import {\n LitElement,\n html,\n type CSSResultGroup,\n type PropertyValues,\n type TemplateResult,\n} from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport { styleMap } from 'lit/directives/style-map.js';\nimport {\n controllable,\n type ColumnResizeDetail,\n type ColumnView,\n type TableModel,\n} from '@argus-table/core';\nimport { startResizeDrag } from './controllers/resize-drag.js';\nimport { TableController } from './controllers/table-controller.js';\nimport { VirtualController, type VirtualWindow } from './controllers/virtual-controller.js';\nimport { CELL_CLICK, COLUMN_RESIZE, fire, RANGE_CHANGE, ROW_CLICK, SORT_CHANGE } from './events.js';\nimport { renderBody, type ActiveCell } from './render/body.js';\nimport { renderLightDom } from './render/cells.js';\nimport { renderHeader } from './render/header.js';\nimport { baseStyles } from './styles/base.js';\nimport type { CellContent, ColumnDef, ColumnSizingState, Density, SortState } from './types.js';\n\n/**\n * The host carries the grid role, so rows rendered in the shadow root become\n * its children in the accessibility tree. ElementInternals sets the default;\n * `connectedCallback` also adds a `role` attribute, because tools such as\n * axe-core can't see the ElementInternals role. A `role` the page sets wins.\n */\nfunction attachGridInternals(host: HTMLElement): ElementInternals {\n const internals = host.attachInternals();\n internals.role = 'grid';\n return internals;\n}\n\n/**\n * The sortable column a header click came from, if any. Works for slotted content\n * too. Clicks on a resize handle never sort, so a click that ends a drag doesn't.\n */\nfunction sortColumnOf(event: Event): string | undefined {\n for (const target of event.composedPath()) {\n if (!(target instanceof HTMLElement)) continue;\n if (target.dataset.resizeCol !== undefined) return undefined;\n if (target.dataset.sortCol !== undefined) return target.dataset.sortCol;\n if (target.getAttribute('part') === 'header-row') return undefined;\n }\n return undefined;\n}\n\nconst RESIZE_STEP = 10;\n\n/**\n * The cell holding the grid's single tab stop. `index` is the row's index in the\n * row model, `-1` for the header row; `rowId` keeps it on the same row when the\n * order changes.\n */\ninterface Active {\n index: number;\n rowId: string | undefined;\n col: string;\n}\n\n/** The grid cell or column header an element is, if any. */\nfunction gridCell(target: EventTarget): HTMLElement | undefined {\n if (!(target instanceof HTMLElement) || target.dataset.col === undefined) return undefined;\n const role = target.getAttribute('role');\n return role === 'gridcell' || role === 'columnheader' ? target : undefined;\n}\n\n/** Row index of a rendered row element (`aria-rowindex` minus the header); `-1` for the header. */\nfunction rowIndexOf(row: Element | null): number {\n return Number(row?.getAttribute('aria-rowindex') ?? 1) - 2;\n}\n\n/** CSS grid track for a column: fixed px, or a flex share with the min width as floor. */\nfunction track<T>(column: ColumnView<T, CellContent>): string {\n return column.flex === undefined\n ? `${String(column.width)}px`\n : `minmax(${String(column.minWidth)}px, ${String(column.flex)}fr)`;\n}\n\n/**\n * A virtualized data table.\n *\n * Set `columns` and `data` as properties. Header and cell content is rendered\n * into the element's light DOM and slotted into the cells, so page CSS and the\n * page's own components work inside cells.\n *\n * @summary Data table with pinned rows and columns and infinite loading.\n *\n * @fires argus-sort-change - A sortable header was clicked, or activated with Enter or Space. `detail: { sorting }`: the new order, or the proposed one when `sorting` is controlled.\n * @fires argus-column-resize - A column was resized by dragging its handle or with Alt+Arrow on its header. `detail: { columnId, width, sizing }`: the new widths, or the proposed ones when `columnSizing` is controlled. Fires on every frame of a drag.\n * @fires argus-cell-click - A body cell was clicked, or Enter was pressed on it. `detail: { row, columnId, value }`. Fires before `argus-row-click`.\n * @fires argus-row-click - A body row was clicked, or Enter was pressed on one of its cells. `detail: { row, rowId, index }`.\n * @fires argus-range-change - The visible rows changed. `detail: { start, end }` (row indices, inclusive).\n *\n * @slot empty - Shown instead of the body when there are no rows. Default: \"No rows\".\n *\n * @csspart table - The scroll container that holds header and body.\n * @csspart header - The sticky header row group.\n * @csspart header-row - The header row.\n * @csspart header-cell - Every header cell.\n * @csspart header-cell-sorted - The header of the sorted column. The direction is in `aria-sort`.\n * @csspart resize-handle - The drag handle on the inline-end edge of a resizable column's header.\n * @csspart body - The row group holding the rendered body rows.\n * @csspart row - Every body row.\n * @csspart row-even - Body rows with an even index (0-based).\n * @csspart row-odd - Body rows with an odd index.\n * @csspart cell - Every body cell.\n * @csspart cell-sorted - Body cells in the sorted column.\n * @csspart cell-pinned - Body cells in a pinned column, on either side.\n * @csspart cell-pinned-edge - The pinned body cell next to the scrolling columns (one per pinned side).\n * @csspart empty - The empty-state container.\n *\n * @cssprop [--argus-bg=Canvas] - Background of the table and rows.\n * @cssprop [--argus-fg=CanvasText] - Text colour.\n * @cssprop [--argus-font=inherit] - Font shorthand for the table.\n * @cssprop [--argus-header-bg] - Header background.\n * @cssprop [--argus-header-fg=inherit] - Header text colour.\n * @cssprop [--argus-border-color] - Colour of the row borders.\n * @cssprop [--argus-border-width=1px] - Width of the row borders.\n * @cssprop [--argus-row-height] - Height of a row. Default per density: 32px, 40px, 52px.\n * @cssprop [--argus-cell-padding-inline] - Inline padding of cells. Default per density: 8px, 12px, 16px.\n * @cssprop [--argus-row-stripe-bg] - Background of odd rows. Default: no stripes.\n * @cssprop [--argus-pinned-shadow] - Shadow on the pinned edge, as a `box-shadow` falling toward the end side (`4px 0 6px -4px …`). Mirrored for the end edge and in RTL; `none` removes it.\n * @cssprop [--argus-resize-handle-color] - Colour of the resize handle's line, shown on hover, focus and drag.\n * @cssprop [--argus-focus-ring=2px solid Highlight] - Outline of the focused header or cell.\n * @cssprop [--argus-row-hover-bg] - Background of the hovered row. Default: no change.\n * @cssprop [--argus-row-selected-bg] - Reserved for row selection, which comes after v0.1. The table doesn't read it yet.\n */\n@customElement('argus-table')\nexport class ArgusTable<T = unknown> extends LitElement {\n static override styles: CSSResultGroup = baseStyles;\n\n /** Column definitions. Property only: functions can't be attributes. */\n @property({ attribute: false })\n columns: readonly ColumnDef<T>[] = [];\n\n /** The rows to show. Replace the array to update; changes inside it aren't detected. */\n @property({ attribute: false })\n data: readonly T[] = [];\n\n /** Returns a stable id for a row. Default: the row's index in `data`. */\n @property({ attribute: false })\n getRowId?: (row: T, index: number) => string;\n\n /** Row height in px. Unset: the density (or the `--argus-row-height` token) decides. */\n @property({ attribute: 'row-height', type: Number })\n rowHeight?: number;\n\n /** Row density: `compact`, `comfortable` or `spacious`. */\n @property({ reflect: true })\n density: Density = 'comfortable';\n\n /** Rows rendered above and below the visible ones. */\n @property({ type: Number })\n overscan = 6;\n\n /**\n * The sort order, e.g. `[{ id: 'total', desc: true }]`; `[]` when unsorted. Unset,\n * the table keeps it itself and header clicks change it. Once set, it is\n * controlled: a click only fires `argus-sort-change` with the proposed order,\n * and nothing changes until you set the property. Set `undefined` to hand it back.\n */\n @property({ attribute: false })\n get sorting(): SortState {\n return this.#sorting.value;\n }\n set sorting(value: SortState | undefined) {\n // Lit wraps this accessor and requests the update with the old value.\n this.#sorting.set(value);\n }\n\n /**\n * Resized widths in px by column id, e.g. `{ name: 240 }`. A resized flex column\n * becomes fixed. Uncontrolled until set, like `sorting`: once set, a resize only\n * fires `argus-column-resize` with the proposed widths. `undefined` hands it back.\n */\n @property({ attribute: false })\n get columnSizing(): ColumnSizingState {\n return this.#sizing.value;\n }\n set columnSizing(value: ColumnSizingState | undefined) {\n this.#sizing.set(value);\n }\n\n readonly #sorting = controllable<SortState>([]);\n readonly #sizing = controllable<ColumnSizingState>({});\n /** A resize the table applied itself; its event fires once the update is done. */\n #appliedResize: ColumnResizeDetail | undefined;\n /** A sort the table applied itself; its event fires once the update is done. */\n #appliedSort: SortState | undefined;\n readonly #internals = attachGridInternals(this);\n readonly #table = new TableController<T>();\n // Looked up lazily: the virtualizer's hostUpdated runs before firstUpdated.\n readonly #virtual = new VirtualController(this, () =>\n this.renderRoot.querySelector<HTMLElement>('[part~=\"table\"]'),\n );\n #rowPx = 40;\n #probeObserver?: ResizeObserver;\n #window: VirtualWindow = { rows: [], totalSize: 0, range: null };\n #active: Active | undefined;\n /** Move focus to the active cell after the next update (keyboard navigation). */\n #focusPending = false;\n #lastRange = '';\n\n override connectedCallback(): void {\n super.connectedCallback();\n if (!this.hasAttribute('role')) this.setAttribute('role', 'grid');\n if (this.hasUpdated) this.#observeProbe();\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n this.#probeObserver?.disconnect();\n }\n\n /** Scrolls so that the row at `index` (in the current row order) is at the top. */\n async scrollToRow(index: number): Promise<void> {\n await this.updateComplete;\n this.#virtual.scrollToIndex(index);\n await this.updateComplete;\n }\n\n protected override willUpdate(changed: PropertyValues<this>): void {\n if (\n changed.has('columns') ||\n changed.has('data') ||\n changed.has('getRowId') ||\n changed.has('sorting') ||\n changed.has('columnSizing')\n ) {\n this.#table.update({\n columns: this.columns,\n data: this.data,\n getRowId: this.getRowId,\n sorting: this.#sorting.value,\n columnSizing: this.#sizing.value,\n });\n }\n const model = this.#table.model;\n this.#reconcileActive(model);\n const active = this.#active;\n this.#internals.ariaRowCount = String(model.rowCount + 1);\n this.#internals.ariaColCount = String(model.columns.length);\n this.#virtual.configure({\n count: model.rowCount,\n rowHeight: this.#rowPx,\n overscan: Math.max(0, this.overscan),\n rowIdAt: (index) => model.rowAt(index)?.id ?? String(index),\n // The active row stays rendered, so the tab stop and focus survive scrolling.\n keep: active?.rowId === undefined ? undefined : { id: active.rowId, index: active.index },\n });\n }\n\n protected override render(): TemplateResult {\n const model = this.#table.model;\n const columns = model.columns;\n this.#window = this.#virtual.computeWindow();\n const pinned = (side: 'start' | 'end') =>\n `${String(columns.reduce((sum, c) => (c.pin === side ? sum + c.width : sum), 0))}px`;\n const style: Record<string, string> = {\n '--_cols': columns.map(track).join(' '),\n '--_width': `${String(model.totalWidth)}px`,\n '--_pin-start': pinned('start'),\n '--_pin-end': pinned('end'),\n };\n const active = this.#active;\n const bodyActive: ActiveCell | undefined =\n active && active.index >= 0 ? { index: active.index, col: active.col } : undefined;\n if (this.rowHeight !== undefined && this.rowHeight > 0) {\n style['--_row-height'] = `${String(this.rowHeight)}px`;\n }\n return html`<div\n part=\"table\"\n role=\"presentation\"\n style=${styleMap(style)}\n @keydown=${this.#onKeydown}\n @focusin=${this.#onFocusin}\n @click=${this.#onClick}\n >\n ${renderHeader(columns, this.#headerHandlers, active?.index === -1 ? active.col : undefined)}\n ${\n model.rowCount === 0\n ? html`<div part=\"empty\"><slot name=\"empty\">No rows</slot></div>`\n : renderBody(this.#window.rows, columns, this.#window.totalSize, bodyActive)\n }\n <div class=\"probe\" aria-hidden=\"true\"></div>\n </div>`;\n }\n\n protected override firstUpdated(): void {\n this.#observeProbe();\n this.#measureRowHeight();\n }\n\n protected override updated(changed: PropertyValues<this>): void {\n renderLightDom(this, this.#table.model, this.#window.rows);\n if (this.#focusPending) {\n this.#focusPending = false;\n this.#focusActive();\n }\n if (changed.has('density') || changed.has('rowHeight')) this.#measureRowHeight();\n if (this.#appliedResize) {\n const detail = this.#appliedResize;\n this.#appliedResize = undefined;\n fire(this, COLUMN_RESIZE, detail);\n }\n if (this.#appliedSort) {\n const sorting = this.#appliedSort;\n this.#appliedSort = undefined;\n fire(this, SORT_CHANGE, { sorting });\n }\n const { range } = this.#window;\n const key = range ? `${String(range.start)}:${String(range.end)}` : '';\n if (range && key !== this.#lastRange) {\n this.#lastRange = key;\n fire(this, RANGE_CHANGE, { start: range.start, end: range.end });\n }\n }\n\n readonly #headerHandlers = {\n click: (event: MouseEvent) => {\n const id = sortColumnOf(event);\n if (id !== undefined) this.#requestSort(id);\n },\n pointerdown: (event: PointerEvent) => {\n if (event.button !== 0) return;\n const handle = event.composedPath()[0];\n if (!(handle instanceof HTMLElement)) return;\n const id = handle.dataset.resizeCol;\n const cell = handle.parentElement;\n if (id === undefined || !cell) return;\n startResizeDrag(handle, event, cell.getBoundingClientRect().width, this.#direction(), (w) => {\n this.#requestResize(id, w);\n });\n },\n };\n\n /**\n * Keys on a focused cell or header. Keys from content inside a cell (an input,\n * a button) are left to that content.\n */\n readonly #onKeydown = (event: KeyboardEvent) => {\n const cell = gridCell(event.composedPath()[0] ?? event.target ?? this);\n const id = cell?.dataset.col;\n if (!cell || id === undefined) return;\n if (event.key === 'Tab' && event.shiftKey) {\n // Firefox makes the scroll container a tab stop, so Shift+Tab would stop\n // on it before leaving the grid. Take it out of the tab order for this\n // one move only: a tabindex there for good would expose it as a node of\n // its own between the grid and its rows.\n const scroller = event.currentTarget as HTMLElement;\n scroller.tabIndex = -1;\n // Put it back once focus has actually moved (a timer can run first).\n cell.addEventListener(\n 'focusout',\n () => {\n scroller.removeAttribute('tabindex');\n },\n { once: true },\n );\n return;\n }\n const inHeader = cell.getAttribute('role') === 'columnheader';\n const { key } = event;\n if (inHeader && event.altKey && (key === 'ArrowLeft' || key === 'ArrowRight')) {\n event.preventDefault();\n // Logical: the arrow pointing toward the inline end grows the column.\n const towardEnd = (key === 'ArrowRight') === (this.#direction() === 1);\n this.#requestResize(\n id,\n this.#currentWidth(id, cell) + (towardEnd ? RESIZE_STEP : -RESIZE_STEP),\n );\n return;\n }\n if (inHeader && (key === 'Enter' || key === ' ') && cell.dataset.sortCol !== undefined) {\n event.preventDefault();\n this.#requestSort(id);\n return;\n }\n if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) return;\n if (!inHeader && key === 'Enter') {\n // Enter on a body cell does what a click does.\n event.preventDefault();\n this.#activate(event.composedPath());\n return;\n }\n const model = this.#table.model;\n const columns = model.columns;\n let row = inHeader ? -1 : rowIndexOf(cell.parentElement);\n let col = columns.findIndex((c) => c.id === id);\n switch (key) {\n case 'ArrowDown':\n row = Math.min(row + 1, model.rowCount - 1);\n break;\n case 'ArrowUp':\n row = Math.max(row - 1, -1);\n break;\n case 'ArrowRight':\n col += this.#direction();\n break;\n case 'ArrowLeft':\n col -= this.#direction();\n break;\n case 'Home':\n col = 0;\n break;\n case 'End':\n col = columns.length - 1;\n break;\n default:\n return;\n }\n event.preventDefault();\n const target = columns[Math.min(Math.max(col, 0), columns.length - 1)];\n if (target) this.#moveTo(row, target.id, model);\n };\n\n /** Focus that lands in a cell (a click, or content inside it) makes that cell active. */\n readonly #onFocusin = (event: FocusEvent) => {\n // Tab into the grid landed on the scroll container (Firefox makes it a tab\n // stop): move on to the grid's one tab stop, the active cell. Only when focus\n // came from outside the grid: coming from inside means the user is leaving,\n // and sending them back would trap the keyboard.\n const scroller = event.currentTarget as HTMLElement;\n if (event.composedPath()[0] === scroller) {\n const from = event.relatedTarget;\n if (!(from instanceof Node && scroller.contains(from))) {\n this.#focusPending = true;\n this.requestUpdate();\n }\n return;\n }\n for (const target of event.composedPath()) {\n const cell = gridCell(target);\n if (cell?.dataset.col !== undefined) {\n const index =\n cell.getAttribute('role') === 'columnheader' ? -1 : rowIndexOf(cell.parentElement);\n const { col } = cell.dataset;\n if (index !== this.#active?.index || col !== this.#active.col) {\n this.#active = { index, rowId: this.#table.model.rowAt(index)?.id, col };\n this.requestUpdate();\n }\n return;\n }\n if (target === event.currentTarget) return;\n }\n };\n\n /** `argus-cell-click`, then `argus-row-click`, for clicks in the body. */\n readonly #onClick = (event: MouseEvent) => {\n this.#activate(event.composedPath());\n };\n\n /**\n * Fires `argus-cell-click` (when a cell is on the path), then `argus-row-click`,\n * for the body row on an event path. Clicks and Enter on a body cell use it.\n */\n #activate(path: EventTarget[]): void {\n let columnId: string | undefined;\n for (const target of path) {\n if (!(target instanceof HTMLElement)) continue;\n const role = target.getAttribute('role');\n if (role === 'gridcell') columnId = target.dataset.col;\n if (role !== 'row') continue;\n const model = this.#table.model;\n const index = rowIndexOf(target);\n const row = index >= 0 ? model.rowAt(index) : undefined;\n if (!row) return;\n if (columnId !== undefined) {\n fire(this, CELL_CLICK, {\n row: row.original,\n columnId,\n value: model.valueAt(index, columnId),\n });\n }\n fire(this, ROW_CLICK, { row: row.original, rowId: row.id, index });\n return;\n }\n }\n\n /** Makes a cell active and focuses it after the update. The keep-rendered rule renders its row. */\n #moveTo(index: number, col: string, model: TableModel<T, CellContent>): void {\n this.#active = { index, rowId: index >= 0 ? model.rowAt(index)?.id : undefined, col };\n this.#focusPending = true;\n this.requestUpdate();\n }\n\n #focusActive(): void {\n const active = this.#active;\n if (!active) return;\n const col = CSS.escape(active.col);\n const selector =\n active.index === -1\n ? `[part~='header-row'] > [data-col=\"${col}\"]`\n : `[part~='body'] > [aria-rowindex=\"${String(active.index + 2)}\"] > [data-col=\"${col}\"]`;\n const cell = this.renderRoot.querySelector<HTMLElement>(selector);\n if (!cell) return;\n cell.scrollIntoView({ block: 'nearest', inline: 'nearest' });\n cell.focus({ preventScroll: true });\n }\n\n /**\n * Keeps the active cell valid after columns or rows change: on the same row id\n * when it still exists (it may have moved), else at the same index, clamped.\n * Defaults to the first header. If its row went away while the grid had focus,\n * focus follows to the replacement.\n */\n #reconcileActive(model: TableModel<T, CellContent>): void {\n const columns = model.columns;\n const first = columns[0];\n if (!first) {\n this.#active = undefined;\n return;\n }\n const prev = this.#active ?? { index: -1, rowId: undefined, col: first.id };\n const col = columns.some((c) => c.id === prev.col) ? prev.col : first.id;\n let { index, rowId } = prev;\n if (rowId !== undefined) {\n const found = model.indexOf(rowId);\n if (found === undefined) {\n index = Math.min(index, model.rowCount - 1);\n rowId = model.rowAt(index)?.id;\n if (this.matches(':focus-within')) this.#focusPending = true;\n } else {\n index = found;\n }\n }\n if (index !== prev.index || rowId !== prev.rowId || col !== prev.col || !this.#active) {\n this.#active = { index, rowId, col };\n }\n }\n\n /**\n * The width a resize starts from: the latest requested width (key presses can\n * come faster than renders), else the model's fixed width, else the rendered\n * width of a flex column.\n */\n #currentWidth(columnId: string, cell: HTMLElement): number {\n const sized = this.#sizing.value[columnId];\n if (sized !== undefined) return sized;\n const view = this.#table.model.columns.find((c) => c.id === columnId);\n return view && view.flex === undefined ? view.width : cell.getBoundingClientRect().width;\n }\n\n #direction(): 1 | -1 {\n return getComputedStyle(this).direction === 'rtl' ? -1 : 1;\n }\n\n /** Sets one column's width (clamped by core), through the controlled/uncontrolled rules. */\n #requestResize(columnId: string, width: number): void {\n const next = this.#table.model.nextSizing(columnId, width);\n const target = next?.[columnId];\n if (!next || target === undefined || target === this.#sizing.value[columnId]) return;\n const old = this.#sizing.value;\n const { value, applied } = this.#sizing.request(next);\n const detail = { columnId, width: target, sizing: value };\n if (applied) {\n this.#appliedResize = detail;\n this.requestUpdate('columnSizing', old);\n } else {\n fire(this, COLUMN_RESIZE, detail);\n }\n }\n\n /** asc → desc → unsorted, through the controlled/uncontrolled rules. */\n #requestSort(columnId: string): void {\n const next = this.#table.model.nextSorting(columnId);\n if (!next) return;\n const old = this.#sorting.value;\n const { value, applied } = this.#sorting.request(next);\n if (applied) {\n this.#appliedSort = value;\n this.requestUpdate('sorting', old);\n } else {\n fire(this, SORT_CHANGE, { sorting: value });\n }\n }\n\n #observeProbe(): void {\n const probe = this.renderRoot.querySelector('.probe');\n if (!probe) return;\n this.#probeObserver ??= new ResizeObserver(() => {\n this.#measureRowHeight();\n });\n this.#probeObserver.observe(probe);\n }\n\n /** Reads the resolved row height, so themes and density change the virtualizer too. */\n #measureRowHeight(): void {\n const probe = this.renderRoot.querySelector('.probe');\n const px = probe?.getBoundingClientRect().height ?? 0;\n if (px > 0 && Math.abs(px - this.#rowPx) > 0.01) {\n this.#rowPx = px;\n this.requestUpdate();\n }\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'argus-table': ArgusTable;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"argus-table.js","names":["_decorate"],"sources":["../src/argus-table.ts"],"sourcesContent":["import {\n LitElement,\n html,\n nothing,\n type CSSResultGroup,\n type PropertyValues,\n type TemplateResult,\n} from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport { styleMap } from 'lit/directives/style-map.js';\nimport {\n controllable,\n type ColumnResizeDetail,\n type ColumnView,\n type TableModel,\n} from '@argus-table/core';\nimport { PinScrollController } from './controllers/pin-scroll-controller.js';\nimport { startResizeDrag } from './controllers/resize-drag.js';\nimport { TableController } from './controllers/table-controller.js';\nimport { VirtualController, type VirtualWindow } from './controllers/virtual-controller.js';\nimport { CELL_CLICK, COLUMN_RESIZE, fire, RANGE_CHANGE, ROW_CLICK, SORT_CHANGE } from './events.js';\nimport { renderBody, type ActiveCell } from './render/body.js';\nimport { renderLightDom } from './render/cells.js';\nimport { renderHeader } from './render/header.js';\nimport { baseStyles } from './styles/base.js';\nimport type { CellContent, ColumnDef, ColumnSizingState, Density, SortState } from './types.js';\n\n/**\n * The host carries the grid role, so rows rendered in the shadow root become\n * its children in the accessibility tree. ElementInternals sets the default;\n * `connectedCallback` also adds a `role` attribute, because tools such as\n * axe-core can't see the ElementInternals role. A `role` the page sets wins.\n */\nfunction attachGridInternals(host: HTMLElement): ElementInternals {\n const internals = host.attachInternals();\n internals.role = 'grid';\n return internals;\n}\n\n/**\n * The sortable column a header click came from, if any. Works for slotted content\n * too. Clicks on a resize handle never sort, so a click that ends a drag doesn't.\n */\nfunction sortColumnOf(event: Event): string | undefined {\n for (const target of event.composedPath()) {\n if (!(target instanceof HTMLElement)) continue;\n if (target.dataset.resizeCol !== undefined) return undefined;\n if (target.dataset.sortCol !== undefined) return target.dataset.sortCol;\n if (target.getAttribute('part') === 'header-row') return undefined;\n }\n return undefined;\n}\n\nconst RESIZE_STEP = 10;\n\n/**\n * The cell holding the grid's single tab stop. `index` is the row's index in the\n * row model, `-1` for the header row; `rowId` keeps it on the same row when the\n * order changes.\n */\ninterface Active {\n index: number;\n rowId: string | undefined;\n col: string;\n}\n\n/** The grid cell or column header an element is, if any. */\nfunction gridCell(target: EventTarget): HTMLElement | undefined {\n if (!(target instanceof HTMLElement) || target.dataset.col === undefined) return undefined;\n const role = target.getAttribute('role');\n return role === 'gridcell' || role === 'columnheader' ? target : undefined;\n}\n\n/** Row index of a rendered row element (`aria-rowindex` minus the header); `-1` for the header. */\nfunction rowIndexOf(row: Element | null): number {\n return Number(row?.getAttribute('aria-rowindex') ?? 1) - 2;\n}\n\n/**\n * The pinned edge's shadow, drawn once per side over the scroller rather than\n * per row: a shadow on every pinned cell repaints on every scroll frame. It\n * covers the pinned columns and casts `--argus-pinned-shadow` onto the rest;\n * PinScrollController sizes it to the visible area.\n */\nfunction pinShadow(side: 'start' | 'end', width: number, rows: number, rowPx: number) {\n if (width === 0) return nothing;\n return html`<div\n class=\"pin-shadow\"\n data-side=${side}\n aria-hidden=\"true\"\n style=${styleMap({\n inlineSize: `${String(width)}px`,\n '--_content-h': `${String((rows + 1) * rowPx)}px`,\n })}\n ></div>`;\n}\n\n/** CSS grid track for a column: fixed px, or a flex share with the min width as floor. */\nfunction track<T>(column: ColumnView<T, CellContent>): string {\n return column.flex === undefined\n ? `${String(column.width)}px`\n : `minmax(${String(column.minWidth)}px, ${String(column.flex)}fr)`;\n}\n\n/**\n * A virtualized data table.\n *\n * Set `columns` and `data` as properties. Header and cell content is rendered\n * into the element's light DOM and slotted into the cells, so page CSS and the\n * page's own components work inside cells.\n *\n * @summary Data table with pinned rows and columns and infinite loading.\n *\n * @fires argus-sort-change - A sortable header was clicked, or activated with Enter or Space. `detail: { sorting }`: the new order, or the proposed one when `sorting` is controlled.\n * @fires argus-column-resize - A column was resized by dragging its handle or with Alt+Arrow on its header. `detail: { columnId, width, sizing }`: the new widths, or the proposed ones when `columnSizing` is controlled. Fires on every frame of a drag.\n * @fires argus-cell-click - A body cell was clicked, or Enter was pressed on it. `detail: { row, columnId, value }`. Fires before `argus-row-click`.\n * @fires argus-row-click - A body row was clicked, or Enter was pressed on one of its cells. `detail: { row, rowId, index }`.\n * @fires argus-range-change - The visible rows changed. `detail: { start, end }` (row indices, inclusive).\n *\n * @slot empty - Shown instead of the body when there are no rows. Default: \"No rows\".\n *\n * @csspart table - The scroll container that holds header and body.\n * @csspart header - The sticky header row group.\n * @csspart header-row - The header row.\n * @csspart header-cell - Every header cell.\n * @csspart header-cell-sorted - The header of the sorted column. The direction is in `aria-sort`.\n * @csspart resize-handle - The drag handle on the inline-end edge of a resizable column's header.\n * @csspart body - The row group holding the rendered body rows.\n * @csspart row - Every body row.\n * @csspart row-even - Body rows with an even index (0-based).\n * @csspart row-odd - Body rows with an odd index.\n * @csspart cell - Every body cell.\n * @csspart cell-sorted - Body cells in the sorted column.\n * @csspart cell-pinned - Body cells in a pinned column, on either side.\n * @csspart cell-pinned-edge - The pinned body cell next to the scrolling columns (one per pinned side).\n * @csspart empty - The empty-state container.\n *\n * @cssprop [--argus-bg=Canvas] - Background of the table and rows.\n * @cssprop [--argus-fg=CanvasText] - Text colour.\n * @cssprop [--argus-font=inherit] - Font shorthand for the table.\n * @cssprop [--argus-header-bg] - Header background.\n * @cssprop [--argus-header-fg=inherit] - Header text colour.\n * @cssprop [--argus-border-color] - Colour of the row borders.\n * @cssprop [--argus-border-width=1px] - Width of the row borders.\n * @cssprop [--argus-row-height] - Height of a row. Default per density: 32px, 40px, 52px.\n * @cssprop [--argus-cell-padding-inline] - Inline padding of cells. Default per density: 8px, 12px, 16px.\n * @cssprop [--argus-row-stripe-bg] - Background of odd rows. Default: no stripes.\n * @cssprop [--argus-pinned-shadow] - Shadow on the pinned edge, as a `box-shadow` falling toward the end side (`4px 0 6px -4px …`). Mirrored for the end edge and in RTL; `none` removes it.\n * @cssprop [--argus-resize-handle-color] - Colour of the resize handle's line, shown on hover, focus and drag.\n * @cssprop [--argus-focus-ring=2px solid Highlight] - Outline of the focused header or cell.\n * @cssprop [--argus-row-hover-bg] - Background of the hovered row. Default: no change.\n * @cssprop [--argus-row-selected-bg] - Reserved for row selection, which comes after v0.1. The table doesn't read it yet.\n */\n@customElement('argus-table')\nexport class ArgusTable<T = unknown> extends LitElement {\n static override styles: CSSResultGroup = baseStyles;\n\n /** Column definitions. Property only: functions can't be attributes. */\n @property({ attribute: false })\n columns: readonly ColumnDef<T>[] = [];\n\n /** The rows to show. Replace the array to update; changes inside it aren't detected. */\n @property({ attribute: false })\n data: readonly T[] = [];\n\n /** Returns a stable id for a row. Default: the row's index in `data`. */\n @property({ attribute: false })\n getRowId?: (row: T, index: number) => string;\n\n /** Row height in px. Unset: the density (or the `--argus-row-height` token) decides. */\n @property({ attribute: 'row-height', type: Number })\n rowHeight?: number;\n\n /** Row density: `compact`, `comfortable` or `spacious`. */\n @property({ reflect: true })\n density: Density = 'comfortable';\n\n /** Rows rendered above and below the visible ones. */\n @property({ type: Number })\n overscan = 6;\n\n /**\n * The sort order, e.g. `[{ id: 'total', desc: true }]`; `[]` when unsorted. Unset,\n * the table keeps it itself and header clicks change it. Once set, it is\n * controlled: a click only fires `argus-sort-change` with the proposed order,\n * and nothing changes until you set the property. Set `undefined` to hand it back.\n */\n @property({ attribute: false })\n get sorting(): SortState {\n return this.#sorting.value;\n }\n set sorting(value: SortState | undefined) {\n // Lit wraps this accessor and requests the update with the old value.\n this.#sorting.set(value);\n }\n\n /**\n * Resized widths in px by column id, e.g. `{ name: 240 }`. A resized flex column\n * becomes fixed. Uncontrolled until set, like `sorting`: once set, a resize only\n * fires `argus-column-resize` with the proposed widths. `undefined` hands it back.\n */\n @property({ attribute: false })\n get columnSizing(): ColumnSizingState {\n return this.#sizing.value;\n }\n set columnSizing(value: ColumnSizingState | undefined) {\n this.#sizing.set(value);\n }\n\n readonly #sorting = controllable<SortState>([]);\n readonly #sizing = controllable<ColumnSizingState>({});\n /** A resize the table applied itself; its event fires once the update is done. */\n #appliedResize: ColumnResizeDetail | undefined;\n /** A sort the table applied itself; its event fires once the update is done. */\n #appliedSort: SortState | undefined;\n readonly #internals = attachGridInternals(this);\n readonly #table = new TableController<T>();\n // Looked up lazily: the virtualizer's hostUpdated runs before firstUpdated.\n readonly #virtual = new VirtualController(this, () =>\n this.renderRoot.querySelector<HTMLElement>('[part~=\"table\"]'),\n );\n readonly #pinScroll = new PinScrollController(this, () =>\n this.renderRoot.querySelector<HTMLElement>('[part~=\"table\"]'),\n );\n #rowPx = 40;\n #probeObserver?: ResizeObserver;\n #window: VirtualWindow = { rows: [], totalSize: 0, range: null };\n #active: Active | undefined;\n /** Move focus to the active cell after the next update (keyboard navigation). */\n #focusPending = false;\n #lastRange = '';\n\n override connectedCallback(): void {\n super.connectedCallback();\n if (!this.hasAttribute('role')) this.setAttribute('role', 'grid');\n if (this.hasUpdated) this.#observeProbe();\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n this.#probeObserver?.disconnect();\n }\n\n /** Scrolls so that the row at `index` (in the current row order) is at the top. */\n async scrollToRow(index: number): Promise<void> {\n await this.updateComplete;\n this.#virtual.scrollToIndex(index);\n await this.updateComplete;\n }\n\n protected override willUpdate(changed: PropertyValues<this>): void {\n if (\n changed.has('columns') ||\n changed.has('data') ||\n changed.has('getRowId') ||\n changed.has('sorting') ||\n changed.has('columnSizing')\n ) {\n this.#table.update({\n columns: this.columns,\n data: this.data,\n getRowId: this.getRowId,\n sorting: this.#sorting.value,\n columnSizing: this.#sizing.value,\n });\n }\n const model = this.#table.model;\n this.#reconcileActive(model);\n const active = this.#active;\n this.#internals.ariaRowCount = String(model.rowCount + 1);\n this.#internals.ariaColCount = String(model.columns.length);\n this.#virtual.configure({\n count: model.rowCount,\n rowHeight: this.#rowPx,\n overscan: Math.max(0, this.overscan),\n rowIdAt: (index) => model.rowAt(index)?.id ?? String(index),\n // The active row stays rendered, so the tab stop and focus survive scrolling.\n keep: active?.rowId === undefined ? undefined : { id: active.rowId, index: active.index },\n });\n }\n\n protected override render(): TemplateResult {\n const model = this.#table.model;\n const columns = model.columns;\n this.#window = this.#virtual.computeWindow();\n const pinnedWidth = (side: 'start' | 'end') =>\n columns.reduce((sum, c) => (c.pin === side ? sum + c.width : sum), 0);\n const pinned = (side: 'start' | 'end') => `${String(pinnedWidth(side))}px`;\n const style: Record<string, string> = {\n '--_cols': columns.map(track).join(' '),\n '--_width': `${String(model.totalWidth)}px`,\n '--_pin-start': pinned('start'),\n '--_pin-end': pinned('end'),\n };\n const active = this.#active;\n const bodyActive: ActiveCell | undefined =\n active && active.index >= 0 ? { index: active.index, col: active.col } : undefined;\n if (this.rowHeight !== undefined && this.rowHeight > 0) {\n style['--_row-height'] = `${String(this.rowHeight)}px`;\n }\n return html`<div\n part=\"table\"\n role=\"presentation\"\n style=${styleMap(style)}\n @keydown=${this.#onKeydown}\n @focusin=${this.#onFocusin}\n @click=${this.#onClick}\n >\n ${renderHeader(columns, this.#headerHandlers, active?.index === -1 ? active.col : undefined)}\n ${\n model.rowCount === 0\n ? html`<div part=\"empty\"><slot name=\"empty\">No rows</slot></div>`\n : renderBody(this.#window.rows, columns, this.#window.totalSize, bodyActive)\n }\n <div class=\"probe\" aria-hidden=\"true\"></div>\n </div>\n ${pinShadow('start', pinnedWidth('start'), model.rowCount, this.#rowPx)}\n ${pinShadow('end', pinnedWidth('end'), model.rowCount, this.#rowPx)}`;\n }\n\n protected override firstUpdated(): void {\n this.#observeProbe();\n this.#measureRowHeight();\n }\n\n protected override updated(changed: PropertyValues<this>): void {\n renderLightDom(this, this.#table.model, this.#window.rows);\n if (changed.has('columns') || changed.has('columnSizing')) this.#pinScroll.measure();\n if (this.#focusPending) {\n this.#focusPending = false;\n this.#focusActive();\n }\n if (changed.has('density') || changed.has('rowHeight')) this.#measureRowHeight();\n if (this.#appliedResize) {\n const detail = this.#appliedResize;\n this.#appliedResize = undefined;\n fire(this, COLUMN_RESIZE, detail);\n }\n if (this.#appliedSort) {\n const sorting = this.#appliedSort;\n this.#appliedSort = undefined;\n fire(this, SORT_CHANGE, { sorting });\n }\n const { range } = this.#window;\n const key = range ? `${String(range.start)}:${String(range.end)}` : '';\n if (range && key !== this.#lastRange) {\n this.#lastRange = key;\n fire(this, RANGE_CHANGE, { start: range.start, end: range.end });\n }\n }\n\n readonly #headerHandlers = {\n click: (event: MouseEvent) => {\n const id = sortColumnOf(event);\n if (id !== undefined) this.#requestSort(id);\n },\n pointerdown: (event: PointerEvent) => {\n if (event.button !== 0) return;\n const handle = event.composedPath()[0];\n if (!(handle instanceof HTMLElement)) return;\n const id = handle.dataset.resizeCol;\n const cell = handle.parentElement;\n if (id === undefined || !cell) return;\n startResizeDrag(handle, event, cell.getBoundingClientRect().width, this.#direction(), (w) => {\n this.#requestResize(id, w);\n });\n },\n };\n\n /**\n * Keys on a focused cell or header. Keys from content inside a cell (an input,\n * a button) are left to that content.\n */\n readonly #onKeydown = (event: KeyboardEvent) => {\n const cell = gridCell(event.composedPath()[0] ?? event.target ?? this);\n const id = cell?.dataset.col;\n if (!cell || id === undefined) return;\n if (event.key === 'Tab' && event.shiftKey) {\n // Firefox makes the scroll container a tab stop, so Shift+Tab would stop\n // on it before leaving the grid. Take it out of the tab order for this\n // one move only: a tabindex there for good would expose it as a node of\n // its own between the grid and its rows.\n const scroller = event.currentTarget as HTMLElement;\n scroller.tabIndex = -1;\n // Put it back once focus has actually moved (a timer can run first).\n cell.addEventListener(\n 'focusout',\n () => {\n scroller.removeAttribute('tabindex');\n },\n { once: true },\n );\n return;\n }\n const inHeader = cell.getAttribute('role') === 'columnheader';\n const { key } = event;\n if (inHeader && event.altKey && (key === 'ArrowLeft' || key === 'ArrowRight')) {\n event.preventDefault();\n // Logical: the arrow pointing toward the inline end grows the column.\n const towardEnd = (key === 'ArrowRight') === (this.#direction() === 1);\n this.#requestResize(\n id,\n this.#currentWidth(id, cell) + (towardEnd ? RESIZE_STEP : -RESIZE_STEP),\n );\n return;\n }\n if (inHeader && (key === 'Enter' || key === ' ') && cell.dataset.sortCol !== undefined) {\n event.preventDefault();\n this.#requestSort(id);\n return;\n }\n if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) return;\n if (!inHeader && key === 'Enter') {\n // Enter on a body cell does what a click does.\n event.preventDefault();\n this.#activate(event.composedPath());\n return;\n }\n const model = this.#table.model;\n const columns = model.columns;\n let row = inHeader ? -1 : rowIndexOf(cell.parentElement);\n let col = columns.findIndex((c) => c.id === id);\n switch (key) {\n case 'ArrowDown':\n row = Math.min(row + 1, model.rowCount - 1);\n break;\n case 'ArrowUp':\n row = Math.max(row - 1, -1);\n break;\n case 'ArrowRight':\n col += this.#direction();\n break;\n case 'ArrowLeft':\n col -= this.#direction();\n break;\n case 'Home':\n col = 0;\n break;\n case 'End':\n col = columns.length - 1;\n break;\n default:\n return;\n }\n event.preventDefault();\n const target = columns[Math.min(Math.max(col, 0), columns.length - 1)];\n if (target) this.#moveTo(row, target.id, model);\n };\n\n /** Focus that lands in a cell (a click, or content inside it) makes that cell active. */\n readonly #onFocusin = (event: FocusEvent) => {\n // Tab into the grid landed on the scroll container (Firefox makes it a tab\n // stop): move on to the grid's one tab stop, the active cell. Only when focus\n // came from outside the grid: coming from inside means the user is leaving,\n // and sending them back would trap the keyboard.\n const scroller = event.currentTarget as HTMLElement;\n if (event.composedPath()[0] === scroller) {\n const from = event.relatedTarget;\n if (!(from instanceof Node && scroller.contains(from))) {\n this.#focusPending = true;\n this.requestUpdate();\n }\n return;\n }\n for (const target of event.composedPath()) {\n const cell = gridCell(target);\n if (cell?.dataset.col !== undefined) {\n const index =\n cell.getAttribute('role') === 'columnheader' ? -1 : rowIndexOf(cell.parentElement);\n const { col } = cell.dataset;\n if (index !== this.#active?.index || col !== this.#active.col) {\n this.#active = { index, rowId: this.#table.model.rowAt(index)?.id, col };\n this.requestUpdate();\n }\n return;\n }\n if (target === event.currentTarget) return;\n }\n };\n\n /** `argus-cell-click`, then `argus-row-click`, for clicks in the body. */\n readonly #onClick = (event: MouseEvent) => {\n this.#activate(event.composedPath());\n };\n\n /**\n * Fires `argus-cell-click` (when a cell is on the path), then `argus-row-click`,\n * for the body row on an event path. Clicks and Enter on a body cell use it.\n */\n #activate(path: EventTarget[]): void {\n let columnId: string | undefined;\n for (const target of path) {\n if (!(target instanceof HTMLElement)) continue;\n const role = target.getAttribute('role');\n if (role === 'gridcell') columnId = target.dataset.col;\n if (role !== 'row') continue;\n const model = this.#table.model;\n const index = rowIndexOf(target);\n const row = index >= 0 ? model.rowAt(index) : undefined;\n if (!row) return;\n if (columnId !== undefined) {\n fire(this, CELL_CLICK, {\n row: row.original,\n columnId,\n value: model.valueAt(index, columnId),\n });\n }\n fire(this, ROW_CLICK, { row: row.original, rowId: row.id, index });\n return;\n }\n }\n\n /** Makes a cell active and focuses it after the update. The keep-rendered rule renders its row. */\n #moveTo(index: number, col: string, model: TableModel<T, CellContent>): void {\n this.#active = { index, rowId: index >= 0 ? model.rowAt(index)?.id : undefined, col };\n this.#focusPending = true;\n this.requestUpdate();\n }\n\n #focusActive(): void {\n const active = this.#active;\n if (!active) return;\n const col = CSS.escape(active.col);\n const selector =\n active.index === -1\n ? `[part~='header-row'] > [data-col=\"${col}\"]`\n : `[part~='body'] > [aria-rowindex=\"${String(active.index + 2)}\"] > [data-col=\"${col}\"]`;\n const cell = this.renderRoot.querySelector<HTMLElement>(selector);\n if (!cell) return;\n cell.scrollIntoView({ block: 'nearest', inline: 'nearest' });\n cell.focus({ preventScroll: true });\n }\n\n /**\n * Keeps the active cell valid after columns or rows change: on the same row id\n * when it still exists (it may have moved), else at the same index, clamped.\n * Defaults to the first header. If its row went away while the grid had focus,\n * focus follows to the replacement.\n */\n #reconcileActive(model: TableModel<T, CellContent>): void {\n const columns = model.columns;\n const first = columns[0];\n if (!first) {\n this.#active = undefined;\n return;\n }\n const prev = this.#active ?? { index: -1, rowId: undefined, col: first.id };\n const col = columns.some((c) => c.id === prev.col) ? prev.col : first.id;\n let { index, rowId } = prev;\n if (rowId !== undefined) {\n const found = model.indexOf(rowId);\n if (found === undefined) {\n index = Math.min(index, model.rowCount - 1);\n rowId = model.rowAt(index)?.id;\n if (this.matches(':focus-within')) this.#focusPending = true;\n } else {\n index = found;\n }\n }\n if (index !== prev.index || rowId !== prev.rowId || col !== prev.col || !this.#active) {\n this.#active = { index, rowId, col };\n }\n }\n\n /**\n * The width a resize starts from: the latest requested width (key presses can\n * come faster than renders), else the model's fixed width, else the rendered\n * width of a flex column.\n */\n #currentWidth(columnId: string, cell: HTMLElement): number {\n const sized = this.#sizing.value[columnId];\n if (sized !== undefined) return sized;\n const view = this.#table.model.columns.find((c) => c.id === columnId);\n return view && view.flex === undefined ? view.width : cell.getBoundingClientRect().width;\n }\n\n #direction(): 1 | -1 {\n return getComputedStyle(this).direction === 'rtl' ? -1 : 1;\n }\n\n /** Sets one column's width (clamped by core), through the controlled/uncontrolled rules. */\n #requestResize(columnId: string, width: number): void {\n const next = this.#table.model.nextSizing(columnId, width);\n const target = next?.[columnId];\n if (!next || target === undefined || target === this.#sizing.value[columnId]) return;\n const old = this.#sizing.value;\n const { value, applied } = this.#sizing.request(next);\n const detail = { columnId, width: target, sizing: value };\n if (applied) {\n this.#appliedResize = detail;\n this.requestUpdate('columnSizing', old);\n } else {\n fire(this, COLUMN_RESIZE, detail);\n }\n }\n\n /** asc → desc → unsorted, through the controlled/uncontrolled rules. */\n #requestSort(columnId: string): void {\n const next = this.#table.model.nextSorting(columnId);\n if (!next) return;\n const old = this.#sorting.value;\n const { value, applied } = this.#sorting.request(next);\n if (applied) {\n this.#appliedSort = value;\n this.requestUpdate('sorting', old);\n } else {\n fire(this, SORT_CHANGE, { sorting: value });\n }\n }\n\n #observeProbe(): void {\n const probe = this.renderRoot.querySelector('.probe');\n if (!probe) return;\n this.#probeObserver ??= new ResizeObserver(() => {\n this.#measureRowHeight();\n });\n this.#probeObserver.observe(probe);\n }\n\n /** Reads the resolved row height, so themes and density change the virtualizer too. */\n #measureRowHeight(): void {\n const probe = this.renderRoot.querySelector('.probe');\n const px = probe?.getBoundingClientRect().height ?? 0;\n if (px > 0 && Math.abs(px - this.#rowPx) > 0.01) {\n this.#rowPx = px;\n this.requestUpdate();\n }\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'argus-table': ArgusTable;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAiCA,SAAS,oBAAoB,MAAqC;CAChE,MAAM,YAAY,KAAK,gBAAgB;CACvC,UAAU,OAAO;CACjB,OAAO;AACT;;;;;AAMA,SAAS,aAAa,OAAkC;CACtD,KAAK,MAAM,UAAU,MAAM,aAAa,GAAG;EACzC,IAAI,EAAE,kBAAkB,cAAc;EACtC,IAAI,OAAO,QAAQ,cAAc,KAAA,GAAW,OAAO,KAAA;EACnD,IAAI,OAAO,QAAQ,YAAY,KAAA,GAAW,OAAO,OAAO,QAAQ;EAChE,IAAI,OAAO,aAAa,MAAM,MAAM,cAAc,OAAO,KAAA;CAC3D;AAEF;AAEA,IAAM,cAAc;;AAcpB,SAAS,SAAS,QAA8C;CAC9D,IAAI,EAAE,kBAAkB,gBAAgB,OAAO,QAAQ,QAAQ,KAAA,GAAW,OAAO,KAAA;CACjF,MAAM,OAAO,OAAO,aAAa,MAAM;CACvC,OAAO,SAAS,cAAc,SAAS,iBAAiB,SAAS,KAAA;AACnE;;AAGA,SAAS,WAAW,KAA6B;CAC/C,OAAO,OAAO,KAAK,aAAa,eAAe,KAAK,CAAC,IAAI;AAC3D;;;;;;;AAQA,SAAS,UAAU,MAAuB,OAAe,MAAc,OAAe;CACpF,IAAI,UAAU,GAAG,OAAO;CACxB,OAAO,IAAI;;gBAEG,KAAK;;YAET,SAAS;EACf,YAAY,GAAG,OAAO,KAAK,EAAE;EAC7B,gBAAgB,GAAG,QAAQ,OAAO,KAAK,KAAK,EAAE;CAChD,CAAC,EAAE;;AAEP;;AAGA,SAAS,MAAS,QAA4C;CAC5D,OAAO,OAAO,SAAS,KAAA,IACnB,GAAG,OAAO,OAAO,KAAK,EAAE,MACxB,UAAU,OAAO,OAAO,QAAQ,EAAE,MAAM,OAAO,OAAO,IAAI,EAAE;AAClE;AAoDO,IAAM,aAAN,MAAM,mBAAgC,WAAW;;;EAKnB,KAAA,UAAA,CAAC;EAIf,KAAA,OAAA,CAAC;EAYH,KAAA,UAAA;EAIR,KAAA,WAAA;EA8BS,KAAA,WAAA,aAAwB,CAAC,CAAC;EAC3B,KAAA,UAAA,aAAgC,CAAC,CAAC;EAK/B,KAAA,aAAA,oBAAoB,IAAI;EAC5B,KAAA,SAAA,IAAI,gBAAmB;EAErB,KAAA,WAAA,IAAI,kBAAkB,YACxC,KAAK,WAAW,cAA2B,mBAAiB,CAC9D;EACsB,KAAA,aAAA,IAAI,oBAAoB,YAC5C,KAAK,WAAW,cAA2B,mBAAiB,CAC9D;EACS,KAAA,SAAA;EAEgB,KAAA,UAAA;GAAE,MAAM,CAAC;GAAG,WAAW;GAAG,OAAO;EAAK;EAG/C,KAAA,gBAAA;EACH,KAAA,aAAA;EAyHc,KAAA,kBAAA;GACzB,QAAQ,UAAsB;IAC5B,MAAM,KAAK,aAAa,KAAK;IAC7B,IAAI,OAAO,KAAA,GAAW,KAAK,aAAa,EAAE;GAC5C;GACA,cAAc,UAAwB;IACpC,IAAI,MAAM,WAAW,GAAG;IACxB,MAAM,SAAS,MAAM,aAAa,CAAC,CAAC;IACpC,IAAI,EAAE,kBAAkB,cAAc;IACtC,MAAM,KAAK,OAAO,QAAQ;IAC1B,MAAM,OAAO,OAAO;IACpB,IAAI,OAAO,KAAA,KAAa,CAAC,MAAM;IAC/B,gBAAgB,QAAQ,OAAO,KAAK,sBAAsB,CAAC,CAAC,OAAO,KAAK,WAAW,IAAI,MAAM;KAC3F,KAAK,eAAe,IAAI,CAAC;IAC3B,CAAC;GACH;EACF;EAMuB,KAAA,cAAA,UAAyB;GAC9C,MAAM,OAAO,SAAS,MAAM,aAAa,CAAC,CAAC,MAAM,MAAM,UAAU,IAAI;GACrE,MAAM,KAAK,MAAM,QAAQ;GACzB,IAAI,CAAC,QAAQ,OAAO,KAAA,GAAW;GAC/B,IAAI,MAAM,QAAQ,SAAS,MAAM,UAAU;IAKzC,MAAM,WAAW,MAAM;IACvB,SAAS,WAAW;IAEpB,KAAK,iBACH,kBACM;KACJ,SAAS,gBAAgB,UAAU;IACrC,GACA,EAAE,MAAM,KAAK,CACf;IACA;GACF;GACA,MAAM,WAAW,KAAK,aAAa,MAAM,MAAM;GAC/C,MAAM,EAAE,QAAQ;GAChB,IAAI,YAAY,MAAM,WAAW,QAAQ,eAAe,QAAQ,eAAe;IAC7E,MAAM,eAAe;IAErB,MAAM,YAAa,QAAQ,kBAAmB,KAAK,WAAW,MAAM;IACpE,KAAK,eACH,IACA,KAAK,cAAc,IAAI,IAAI,KAAK,YAAY,cAAc,IAC5D;IACA;GACF;GACA,IAAI,aAAa,QAAQ,WAAW,QAAQ,QAAQ,KAAK,QAAQ,YAAY,KAAA,GAAW;IACtF,MAAM,eAAe;IACrB,KAAK,aAAa,EAAE;IACpB;GACF;GACA,IAAI,MAAM,UAAU,MAAM,WAAW,MAAM,WAAW,MAAM,UAAU;GACtE,IAAI,CAAC,YAAY,QAAQ,SAAS;IAEhC,MAAM,eAAe;IACrB,KAAK,UAAU,MAAM,aAAa,CAAC;IACnC;GACF;GACA,MAAM,QAAQ,KAAK,OAAO;GAC1B,MAAM,UAAU,MAAM;GACtB,IAAI,MAAM,WAAW,KAAK,WAAW,KAAK,aAAa;GACvD,IAAI,MAAM,QAAQ,WAAW,MAAM,EAAE,OAAO,EAAE;GAC9C,QAAQ,KAAR;IACE,KAAK;KACH,MAAM,KAAK,IAAI,MAAM,GAAG,MAAM,WAAW,CAAC;KAC1C;IACF,KAAK;KACH,MAAM,KAAK,IAAI,MAAM,GAAG,EAAE;KAC1B;IACF,KAAK;KACH,OAAO,KAAK,WAAW;KACvB;IACF,KAAK;KACH,OAAO,KAAK,WAAW;KACvB;IACF,KAAK;KACH,MAAM;KACN;IACF,KAAK;KACH,MAAM,QAAQ,SAAS;KACvB;IACF,SACE;GACJ;GACA,MAAM,eAAe;GACrB,MAAM,SAAS,QAAQ,KAAK,IAAI,KAAK,IAAI,KAAK,CAAC,GAAG,QAAQ,SAAS,CAAC;GACpE,IAAI,QAAQ,KAAK,QAAQ,KAAK,OAAO,IAAI,KAAK;EAChD;EAGuB,KAAA,cAAA,UAAsB;GAK3C,MAAM,WAAW,MAAM;GACvB,IAAI,MAAM,aAAa,CAAC,CAAC,OAAO,UAAU;IACxC,MAAM,OAAO,MAAM;IACnB,IAAI,EAAE,gBAAgB,QAAQ,SAAS,SAAS,IAAI,IAAI;KACtD,KAAK,gBAAgB;KACrB,KAAK,cAAc;IACrB;IACA;GACF;GACA,KAAK,MAAM,UAAU,MAAM,aAAa,GAAG;IACzC,MAAM,OAAO,SAAS,MAAM;IAC5B,IAAI,MAAM,QAAQ,QAAQ,KAAA,GAAW;KACnC,MAAM,QACJ,KAAK,aAAa,MAAM,MAAM,iBAAiB,KAAK,WAAW,KAAK,aAAa;KACnF,MAAM,EAAE,QAAQ,KAAK;KACrB,IAAI,UAAU,KAAK,SAAS,SAAS,QAAQ,KAAK,QAAQ,KAAK;MAC7D,KAAK,UAAU;OAAE;OAAO,OAAO,KAAK,OAAO,MAAM,MAAM,KAAK,CAAC,EAAE;OAAI;MAAI;MACvE,KAAK,cAAc;KACrB;KACA;IACF;IACA,IAAI,WAAW,MAAM,eAAe;GACtC;EACF;EAGqB,KAAA,YAAA,UAAsB;GACzC,KAAK,UAAU,MAAM,aAAa,CAAC;EACrC;;;EAxUyC,KAAA,SAAA;;;;;;;;CAgCzC,IACI,UAAqB;EACvB,OAAO,KAAK,SAAS;CACvB;CACA,IAAI,QAAQ,OAA8B;EAExC,KAAK,SAAS,IAAI,KAAK;CACzB;;;;;;CAOA,IACI,eAAkC;EACpC,OAAO,KAAK,QAAQ;CACtB;CACA,IAAI,aAAa,OAAsC;EACrD,KAAK,QAAQ,IAAI,KAAK;CACxB;CAEA;CACA;;CAEA;;CAEA;CACA;CACA;CAEA;CAGA;CAGA;CACA;CACA;CACA;;CAEA;CACA;CAEA,oBAAmC;EACjC,MAAM,kBAAkB;EACxB,IAAI,CAAC,KAAK,aAAa,MAAM,GAAG,KAAK,aAAa,QAAQ,MAAM;EAChE,IAAI,KAAK,YAAY,KAAK,cAAc;CAC1C;CAEA,uBAAsC;EACpC,MAAM,qBAAqB;EAC3B,KAAK,gBAAgB,WAAW;CAClC;;CAGA,MAAM,YAAY,OAA8B;EAC9C,MAAM,KAAK;EACX,KAAK,SAAS,cAAc,KAAK;EACjC,MAAM,KAAK;CACb;CAEA,WAA8B,SAAqC;EACjE,IACE,QAAQ,IAAI,SAAS,KACrB,QAAQ,IAAI,MAAM,KAClB,QAAQ,IAAI,UAAU,KACtB,QAAQ,IAAI,SAAS,KACrB,QAAQ,IAAI,cAAc,GAE1B,KAAK,OAAO,OAAO;GACjB,SAAS,KAAK;GACd,MAAM,KAAK;GACX,UAAU,KAAK;GACf,SAAS,KAAK,SAAS;GACvB,cAAc,KAAK,QAAQ;EAC7B,CAAC;EAEH,MAAM,QAAQ,KAAK,OAAO;EAC1B,KAAK,iBAAiB,KAAK;EAC3B,MAAM,SAAS,KAAK;EACpB,KAAK,WAAW,eAAe,OAAO,MAAM,WAAW,CAAC;EACxD,KAAK,WAAW,eAAe,OAAO,MAAM,QAAQ,MAAM;EAC1D,KAAK,SAAS,UAAU;GACtB,OAAO,MAAM;GACb,WAAW,KAAK;GAChB,UAAU,KAAK,IAAI,GAAG,KAAK,QAAQ;GACnC,UAAU,UAAU,MAAM,MAAM,KAAK,CAAC,EAAE,MAAM,OAAO,KAAK;GAE1D,MAAM,QAAQ,UAAU,KAAA,IAAY,KAAA,IAAY;IAAE,IAAI,OAAO;IAAO,OAAO,OAAO;GAAM;EAC1F,CAAC;CACH;CAEA,SAA4C;EAC1C,MAAM,QAAQ,KAAK,OAAO;EAC1B,MAAM,UAAU,MAAM;EACtB,KAAK,UAAU,KAAK,SAAS,cAAc;EAC3C,MAAM,eAAe,SACnB,QAAQ,QAAQ,KAAK,MAAO,EAAE,QAAQ,OAAO,MAAM,EAAE,QAAQ,KAAM,CAAC;EACtE,MAAM,UAAU,SAA0B,GAAG,OAAO,YAAY,IAAI,CAAC,EAAE;EACvE,MAAM,QAAgC;GACpC,WAAW,QAAQ,IAAI,KAAK,CAAC,CAAC,KAAK,GAAG;GACtC,YAAY,GAAG,OAAO,MAAM,UAAU,EAAE;GACxC,gBAAgB,OAAO,OAAO;GAC9B,cAAc,OAAO,KAAK;EAC5B;EACA,MAAM,SAAS,KAAK;EACpB,MAAM,aACJ,UAAU,OAAO,SAAS,IAAI;GAAE,OAAO,OAAO;GAAO,KAAK,OAAO;EAAI,IAAI,KAAA;EAC3E,IAAI,KAAK,cAAc,KAAA,KAAa,KAAK,YAAY,GACnD,MAAM,mBAAmB,GAAG,OAAO,KAAK,SAAS,EAAE;EAErD,OAAO,IAAI;;;gBAGC,SAAS,KAAK,EAAE;mBACb,KAAK,WAAW;mBAChB,KAAK,WAAW;iBAClB,KAAK,SAAS;;UAErB,aAAa,SAAS,KAAK,iBAAiB,QAAQ,UAAU,KAAK,OAAO,MAAM,KAAA,CAAS,EAAE;UAE3F,MAAM,aAAa,IACf,IAAI,8DACJ,WAAW,KAAK,QAAQ,MAAM,SAAS,KAAK,QAAQ,WAAW,UAAU,EAC9E;;;QAGD,UAAU,SAAS,YAAY,OAAO,GAAG,MAAM,UAAU,KAAK,MAAM,EAAE;QACtE,UAAU,OAAO,YAAY,KAAK,GAAG,MAAM,UAAU,KAAK,MAAM;CACtE;CAEA,eAAwC;EACtC,KAAK,cAAc;EACnB,KAAK,kBAAkB;CACzB;CAEA,QAA2B,SAAqC;EAC9D,eAAe,MAAM,KAAK,OAAO,OAAO,KAAK,QAAQ,IAAI;EACzD,IAAI,QAAQ,IAAI,SAAS,KAAK,QAAQ,IAAI,cAAc,GAAG,KAAK,WAAW,QAAQ;EACnF,IAAI,KAAK,eAAe;GACtB,KAAK,gBAAgB;GACrB,KAAK,aAAa;EACpB;EACA,IAAI,QAAQ,IAAI,SAAS,KAAK,QAAQ,IAAI,WAAW,GAAG,KAAK,kBAAkB;EAC/E,IAAI,KAAK,gBAAgB;GACvB,MAAM,SAAS,KAAK;GACpB,KAAK,iBAAiB,KAAA;GACtB,KAAK,MAAM,eAAe,MAAM;EAClC;EACA,IAAI,KAAK,cAAc;GACrB,MAAM,UAAU,KAAK;GACrB,KAAK,eAAe,KAAA;GACpB,KAAK,MAAM,aAAa,EAAE,QAAQ,CAAC;EACrC;EACA,MAAM,EAAE,UAAU,KAAK;EACvB,MAAM,MAAM,QAAQ,GAAG,OAAO,MAAM,KAAK,EAAE,GAAG,OAAO,MAAM,GAAG,MAAM;EACpE,IAAI,SAAS,QAAQ,KAAK,YAAY;GACpC,KAAK,aAAa;GAClB,KAAK,MAAM,cAAc;IAAE,OAAO,MAAM;IAAO,KAAK,MAAM;GAAI,CAAC;EACjE;CACF;CAEA;;;;;CAsBA;;CA6EA;;CA+BA;;;;;CAQA,UAAU,MAA2B;EACnC,IAAI;EACJ,KAAK,MAAM,UAAU,MAAM;GACzB,IAAI,EAAE,kBAAkB,cAAc;GACtC,MAAM,OAAO,OAAO,aAAa,MAAM;GACvC,IAAI,SAAS,YAAY,WAAW,OAAO,QAAQ;GACnD,IAAI,SAAS,OAAO;GACpB,MAAM,QAAQ,KAAK,OAAO;GAC1B,MAAM,QAAQ,WAAW,MAAM;GAC/B,MAAM,MAAM,SAAS,IAAI,MAAM,MAAM,KAAK,IAAI,KAAA;GAC9C,IAAI,CAAC,KAAK;GACV,IAAI,aAAa,KAAA,GACf,KAAK,MAAM,YAAY;IACrB,KAAK,IAAI;IACT;IACA,OAAO,MAAM,QAAQ,OAAO,QAAQ;GACtC,CAAC;GAEH,KAAK,MAAM,WAAW;IAAE,KAAK,IAAI;IAAU,OAAO,IAAI;IAAI;GAAM,CAAC;GACjE;EACF;CACF;;CAGA,QAAQ,OAAe,KAAa,OAAyC;EAC3E,KAAK,UAAU;GAAE;GAAO,OAAO,SAAS,IAAI,MAAM,MAAM,KAAK,CAAC,EAAE,KAAK,KAAA;GAAW;EAAI;EACpF,KAAK,gBAAgB;EACrB,KAAK,cAAc;CACrB;CAEA,eAAqB;EACnB,MAAM,SAAS,KAAK;EACpB,IAAI,CAAC,QAAQ;EACb,MAAM,MAAM,IAAI,OAAO,OAAO,GAAG;EACjC,MAAM,WACJ,OAAO,UAAU,KACb,qCAAqC,IAAI,MACzC,oCAAoC,OAAO,OAAO,QAAQ,CAAC,EAAE,kBAAkB,IAAI;EACzF,MAAM,OAAO,KAAK,WAAW,cAA2B,QAAQ;EAChE,IAAI,CAAC,MAAM;EACX,KAAK,eAAe;GAAE,OAAO;GAAW,QAAQ;EAAU,CAAC;EAC3D,KAAK,MAAM,EAAE,eAAe,KAAK,CAAC;CACpC;;;;;;;CAQA,iBAAiB,OAAyC;EACxD,MAAM,UAAU,MAAM;EACtB,MAAM,QAAQ,QAAQ;EACtB,IAAI,CAAC,OAAO;GACV,KAAK,UAAU,KAAA;GACf;EACF;EACA,MAAM,OAAO,KAAK,WAAW;GAAE,OAAO;GAAI,OAAO,KAAA;GAAW,KAAK,MAAM;EAAG;EAC1E,MAAM,MAAM,QAAQ,MAAM,MAAM,EAAE,OAAO,KAAK,GAAG,IAAI,KAAK,MAAM,MAAM;EACtE,IAAI,EAAE,OAAO,UAAU;EACvB,IAAI,UAAU,KAAA,GAAW;GACvB,MAAM,QAAQ,MAAM,QAAQ,KAAK;GACjC,IAAI,UAAU,KAAA,GAAW;IACvB,QAAQ,KAAK,IAAI,OAAO,MAAM,WAAW,CAAC;IAC1C,QAAQ,MAAM,MAAM,KAAK,CAAC,EAAE;IAC5B,IAAI,KAAK,QAAQ,eAAe,GAAG,KAAK,gBAAgB;GAC1D,OACE,QAAQ;EAEZ;EACA,IAAI,UAAU,KAAK,SAAS,UAAU,KAAK,SAAS,QAAQ,KAAK,OAAO,CAAC,KAAK,SAC5E,KAAK,UAAU;GAAE;GAAO;GAAO;EAAI;CAEvC;;;;;;CAOA,cAAc,UAAkB,MAA2B;EACzD,MAAM,QAAQ,KAAK,QAAQ,MAAM;EACjC,IAAI,UAAU,KAAA,GAAW,OAAO;EAChC,MAAM,OAAO,KAAK,OAAO,MAAM,QAAQ,MAAM,MAAM,EAAE,OAAO,QAAQ;EACpE,OAAO,QAAQ,KAAK,SAAS,KAAA,IAAY,KAAK,QAAQ,KAAK,sBAAsB,CAAC,CAAC;CACrF;CAEA,aAAqB;EACnB,OAAO,iBAAiB,IAAI,CAAC,CAAC,cAAc,QAAQ,KAAK;CAC3D;;CAGA,eAAe,UAAkB,OAAqB;EACpD,MAAM,OAAO,KAAK,OAAO,MAAM,WAAW,UAAU,KAAK;EACzD,MAAM,SAAS,OAAO;EACtB,IAAI,CAAC,QAAQ,WAAW,KAAA,KAAa,WAAW,KAAK,QAAQ,MAAM,WAAW;EAC9E,MAAM,MAAM,KAAK,QAAQ;EACzB,MAAM,EAAE,OAAO,YAAY,KAAK,QAAQ,QAAQ,IAAI;EACpD,MAAM,SAAS;GAAE;GAAU,OAAO;GAAQ,QAAQ;EAAM;EACxD,IAAI,SAAS;GACX,KAAK,iBAAiB;GACtB,KAAK,cAAc,gBAAgB,GAAG;EACxC,OACE,KAAK,MAAM,eAAe,MAAM;CAEpC;;CAGA,aAAa,UAAwB;EACnC,MAAM,OAAO,KAAK,OAAO,MAAM,YAAY,QAAQ;EACnD,IAAI,CAAC,MAAM;EACX,MAAM,MAAM,KAAK,SAAS;EAC1B,MAAM,EAAE,OAAO,YAAY,KAAK,SAAS,QAAQ,IAAI;EACrD,IAAI,SAAS;GACX,KAAK,eAAe;GACpB,KAAK,cAAc,WAAW,GAAG;EACnC,OACE,KAAK,MAAM,aAAa,EAAE,SAAS,MAAM,CAAC;CAE9C;CAEA,gBAAsB;EACpB,MAAM,QAAQ,KAAK,WAAW,cAAc,QAAQ;EACpD,IAAI,CAAC,OAAO;EACZ,KAAK,mBAAmB,IAAI,qBAAqB;GAC/C,KAAK,kBAAkB;EACzB,CAAC;EACD,KAAK,eAAe,QAAQ,KAAK;CACnC;;CAGA,oBAA0B;EAExB,MAAM,KADQ,KAAK,WAAW,cAAc,QACjC,CAAA,EAAO,sBAAsB,CAAC,CAAC,UAAU;EACpD,IAAI,KAAK,KAAK,KAAK,IAAI,KAAK,KAAK,MAAM,IAAI,KAAM;GAC/C,KAAK,SAAS;GACd,KAAK,cAAc;EACrB;CACF;AACF;AAtdGA,WAAA,CAAA,SAAS,EAAE,WAAW,MAAM,CAAC,CAAA,GAAA,WAAA,WAAA,WAAA,KAAA,CAAA;AAI7BA,WAAA,CAAA,SAAS,EAAE,WAAW,MAAM,CAAC,CAAA,GAAA,WAAA,WAAA,QAAA,KAAA,CAAA;AAI7BA,WAAA,CAAA,SAAS,EAAE,WAAW,MAAM,CAAC,CAAA,GAAA,WAAA,WAAA,YAAA,KAAA,CAAA;AAI7BA,WAAA,CAAA,SAAS;CAAE,WAAW;CAAc,MAAM;AAAO,CAAC,CAAA,GAAA,WAAA,WAAA,aAAA,KAAA,CAAA;AAIlDA,WAAA,CAAA,SAAS,EAAE,SAAS,KAAK,CAAC,CAAA,GAAA,WAAA,WAAA,WAAA,KAAA,CAAA;AAI1BA,WAAA,CAAA,SAAS,EAAE,MAAM,OAAO,CAAC,CAAA,GAAA,WAAA,WAAA,YAAA,KAAA,CAAA;AASzBA,WAAA,CAAA,SAAS,EAAE,WAAW,MAAM,CAAC,CAAA,GAAA,WAAA,WAAA,WAAA,IAAA;AAc7BA,WAAA,CAAA,SAAS,EAAE,WAAW,MAAM,CAAC,CAAA,GAAA,WAAA,WAAA,gBAAA,IAAA;AAhD/B,aAAAA,WAAA,CAAA,cAAc,aAAa,CAAA,GAAA,UAAA"}