argus-table 0.0.0-stage → 0.0.1

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.
Files changed (37) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +66 -2
  3. package/dist/_virtual/_@oxc-project_runtime@0.156.0/helpers/esm/decorate.js +8 -0
  4. package/dist/argus-table.js +492 -0
  5. package/dist/argus-table.js.map +1 -0
  6. package/dist/cdn/argus-table.js +4743 -0
  7. package/dist/cdn/argus-table.js.map +1 -0
  8. package/dist/controllers/resize-drag.js +44 -0
  9. package/dist/controllers/resize-drag.js.map +1 -0
  10. package/dist/controllers/table-controller.js +24 -0
  11. package/dist/controllers/table-controller.js.map +1 -0
  12. package/dist/controllers/virtual-controller.js +83 -0
  13. package/dist/controllers/virtual-controller.js.map +1 -0
  14. package/dist/core/src/columns/resolve-columns.js +62 -0
  15. package/dist/core/src/columns/resolve-columns.js.map +1 -0
  16. package/dist/core/src/state/controllable.js +31 -0
  17. package/dist/core/src/state/controllable.js.map +1 -0
  18. package/dist/core/src/table/create-table.js +185 -0
  19. package/dist/core/src/table/create-table.js.map +1 -0
  20. package/dist/core/src/virtual/row-pool.js +64 -0
  21. package/dist/core/src/virtual/row-pool.js.map +1 -0
  22. package/dist/custom-elements.json +1131 -0
  23. package/dist/events.js +22 -0
  24. package/dist/events.js.map +1 -0
  25. package/dist/index.d.ts +236 -0
  26. package/dist/index.js +3 -0
  27. package/dist/render/body.js +49 -0
  28. package/dist/render/body.js.map +1 -0
  29. package/dist/render/cells.js +27 -0
  30. package/dist/render/cells.js.map +1 -0
  31. package/dist/render/content.js +35 -0
  32. package/dist/render/content.js.map +1 -0
  33. package/dist/render/header.js +62 -0
  34. package/dist/render/header.js.map +1 -0
  35. package/dist/styles/base.js +235 -0
  36. package/dist/styles/base.js.map +1 -0
  37. package/package.json +61 -3
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Joessst-Dev and Argus Table contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,67 @@
1
- # Temporary Holding Version
1
+ # argus-table
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ `<argus-table>` is a data table web component built with Lit and TanStack. It works in any framework, or none.
4
+
5
+ - **Virtualized rows:** 100,000 rows scroll smoothly, and the DOM stays the same size.
6
+ - **Pinned columns** at the start or end, which mirror in right-to-left layouts.
7
+ - **Sorting and resizing:** by mouse or keyboard, controlled or uncontrolled.
8
+ - **Accessible:** the WAI-ARIA grid pattern, with one tab stop and arrow-key navigation.
9
+ - **Plain CSS styling:** `--argus-*` tokens and `::part()` names. Cell content is light DOM, so your own CSS and components work inside cells.
10
+
11
+ **[Docs and live examples](https://joessst-dev.github.io/argus-table/)** · [GitHub](https://github.com/Joessst-Dev/argus-table)
12
+
13
+ ## Install
14
+
15
+ ```bash
16
+ npm i argus-table @argus-table/themes
17
+ ```
18
+
19
+ ## A first table
20
+
21
+ ```html
22
+ <argus-table id="orders" aria-label="Orders" style="height: 400px"></argus-table>
23
+
24
+ <script type="module">
25
+ import 'argus-table';
26
+ import '@argus-table/themes/default.css';
27
+
28
+ const table = document.querySelector('#orders');
29
+ table.columns = [
30
+ { id: 'id', header: 'Order', accessor: 'id', width: 90, pin: 'start' },
31
+ {
32
+ id: 'customer',
33
+ header: 'Customer',
34
+ accessor: 'customer.name',
35
+ width: { flex: 1 },
36
+ sortable: true,
37
+ },
38
+ { id: 'total', header: 'Total', accessor: 'total', width: 110, align: 'end', sortable: true },
39
+ ];
40
+ table.data = await (await fetch('/api/orders')).json();
41
+ table.getRowId = (order) => order.id;
42
+ table.addEventListener('argus-row-click', (e) => console.log(e.detail.row));
43
+ </script>
44
+ ```
45
+
46
+ - **Height:** the table virtualizes its rows, so it needs a bounded height. Its default is 400 px.
47
+ - **Label:** give it a name with `aria-label` or `aria-labelledby`.
48
+ - **Columns and data** are JavaScript properties, because they hold objects and functions.
49
+ - **Cells:** a column's `cell` function can return a string, a number, a DOM node or a Lit template.
50
+
51
+ ## Without a bundler
52
+
53
+ The CDN build has every dependency bundled:
54
+
55
+ ```html
56
+ <script
57
+ type="module"
58
+ src="https://cdn.jsdelivr.net/npm/argus-table/dist/cdn/argus-table.js"
59
+ ></script>
60
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@argus-table/themes/dist/default.css" />
61
+ ```
62
+
63
+ ## Status
64
+
65
+ This is v0.1, an early release. Names can still change before 1.0. Infinite loading, pinned rows, auto column widths, column reordering and framework wrappers come in v0.2. See the [roadmap](https://github.com/Joessst-Dev/argus-table/blob/main/docs/plan/plan/12-roadmap.md).
66
+
67
+ MIT licensed.
@@ -0,0 +1,8 @@
1
+ // \0@oxc-project+runtime@0.156.0/helpers/esm/decorate.js
2
+ function __decorate(decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ }
8
+ export { __decorate as default };
@@ -0,0 +1,492 @@
1
+ import { controllable } from "./core/src/state/controllable.js";
2
+ import { startResizeDrag } from "./controllers/resize-drag.js";
3
+ import { TableController } from "./controllers/table-controller.js";
4
+ import { VirtualController } from "./controllers/virtual-controller.js";
5
+ import { CELL_CLICK, COLUMN_RESIZE, RANGE_CHANGE, ROW_CLICK, SORT_CHANGE, fire } from "./events.js";
6
+ import { renderBody } from "./render/body.js";
7
+ import { renderLightDom } from "./render/cells.js";
8
+ import { renderHeader } from "./render/header.js";
9
+ import { baseStyles } from "./styles/base.js";
10
+ import __decorate from "./_virtual/_@oxc-project_runtime@0.156.0/helpers/esm/decorate.js";
11
+ import { LitElement, html } from "lit";
12
+ import { customElement, property } from "lit/decorators.js";
13
+ import { styleMap } from "lit/directives/style-map.js";
14
+ // src/argus-table.ts
15
+ /**
16
+ * The host carries the grid role, so rows rendered in the shadow root become
17
+ * its children in the accessibility tree. ElementInternals sets the default;
18
+ * `connectedCallback` also adds a `role` attribute, because tools such as
19
+ * axe-core can't see the ElementInternals role. A `role` the page sets wins.
20
+ */
21
+ function attachGridInternals(host) {
22
+ const internals = host.attachInternals();
23
+ internals.role = "grid";
24
+ return internals;
25
+ }
26
+ /**
27
+ * The sortable column a header click came from, if any. Works for slotted content
28
+ * too. Clicks on a resize handle never sort, so a click that ends a drag doesn't.
29
+ */
30
+ function sortColumnOf(event) {
31
+ for (const target of event.composedPath()) {
32
+ if (!(target instanceof HTMLElement)) continue;
33
+ if (target.dataset.resizeCol !== void 0) return void 0;
34
+ if (target.dataset.sortCol !== void 0) return target.dataset.sortCol;
35
+ if (target.getAttribute("part") === "header-row") return void 0;
36
+ }
37
+ }
38
+ var RESIZE_STEP = 10;
39
+ /** The grid cell or column header an element is, if any. */
40
+ function gridCell(target) {
41
+ if (!(target instanceof HTMLElement) || target.dataset.col === void 0) return void 0;
42
+ const role = target.getAttribute("role");
43
+ return role === "gridcell" || role === "columnheader" ? target : void 0;
44
+ }
45
+ /** Row index of a rendered row element (`aria-rowindex` minus the header); `-1` for the header. */
46
+ function rowIndexOf(row) {
47
+ return Number(row?.getAttribute("aria-rowindex") ?? 1) - 2;
48
+ }
49
+ /** CSS grid track for a column: fixed px, or a flex share with the min width as floor. */
50
+ function track(column) {
51
+ return column.flex === void 0 ? `${String(column.width)}px` : `minmax(${String(column.minWidth)}px, ${String(column.flex)}fr)`;
52
+ }
53
+ var ArgusTable = class ArgusTable extends LitElement {
54
+ constructor(..._args) {
55
+ super(..._args);
56
+ this.columns = [];
57
+ this.data = [];
58
+ this.density = "comfortable";
59
+ this.overscan = 6;
60
+ this.#sorting = controllable([]);
61
+ this.#sizing = controllable({});
62
+ this.#internals = attachGridInternals(this);
63
+ this.#table = new TableController();
64
+ this.#virtual = new VirtualController(this, () => this.renderRoot.querySelector("[part~=\"table\"]"));
65
+ this.#rowPx = 40;
66
+ this.#window = {
67
+ rows: [],
68
+ totalSize: 0,
69
+ range: null
70
+ };
71
+ this.#focusPending = false;
72
+ this.#lastRange = "";
73
+ this.#headerHandlers = {
74
+ click: (event) => {
75
+ const id = sortColumnOf(event);
76
+ if (id !== void 0) this.#requestSort(id);
77
+ },
78
+ pointerdown: (event) => {
79
+ if (event.button !== 0) return;
80
+ const handle = event.composedPath()[0];
81
+ if (!(handle instanceof HTMLElement)) return;
82
+ const id = handle.dataset.resizeCol;
83
+ const cell = handle.parentElement;
84
+ if (id === void 0 || !cell) return;
85
+ startResizeDrag(handle, event, cell.getBoundingClientRect().width, this.#direction(), (w) => {
86
+ this.#requestResize(id, w);
87
+ });
88
+ }
89
+ };
90
+ this.#onKeydown = (event) => {
91
+ const cell = gridCell(event.composedPath()[0] ?? event.target ?? this);
92
+ const id = cell?.dataset.col;
93
+ if (!cell || id === void 0) return;
94
+ if (event.key === "Tab" && event.shiftKey) {
95
+ const scroller = event.currentTarget;
96
+ scroller.tabIndex = -1;
97
+ cell.addEventListener("focusout", () => {
98
+ scroller.removeAttribute("tabindex");
99
+ }, { once: true });
100
+ return;
101
+ }
102
+ const inHeader = cell.getAttribute("role") === "columnheader";
103
+ const { key } = event;
104
+ if (inHeader && event.altKey && (key === "ArrowLeft" || key === "ArrowRight")) {
105
+ event.preventDefault();
106
+ const towardEnd = key === "ArrowRight" === (this.#direction() === 1);
107
+ this.#requestResize(id, this.#currentWidth(id, cell) + (towardEnd ? RESIZE_STEP : -10));
108
+ return;
109
+ }
110
+ if (inHeader && (key === "Enter" || key === " ") && cell.dataset.sortCol !== void 0) {
111
+ event.preventDefault();
112
+ this.#requestSort(id);
113
+ return;
114
+ }
115
+ if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) return;
116
+ if (!inHeader && key === "Enter") {
117
+ event.preventDefault();
118
+ this.#activate(event.composedPath());
119
+ return;
120
+ }
121
+ const model = this.#table.model;
122
+ const columns = model.columns;
123
+ let row = inHeader ? -1 : rowIndexOf(cell.parentElement);
124
+ let col = columns.findIndex((c) => c.id === id);
125
+ switch (key) {
126
+ case "ArrowDown":
127
+ row = Math.min(row + 1, model.rowCount - 1);
128
+ break;
129
+ case "ArrowUp":
130
+ row = Math.max(row - 1, -1);
131
+ break;
132
+ case "ArrowRight":
133
+ col += this.#direction();
134
+ break;
135
+ case "ArrowLeft":
136
+ col -= this.#direction();
137
+ break;
138
+ case "Home":
139
+ col = 0;
140
+ break;
141
+ case "End":
142
+ col = columns.length - 1;
143
+ break;
144
+ default: return;
145
+ }
146
+ event.preventDefault();
147
+ const target = columns[Math.min(Math.max(col, 0), columns.length - 1)];
148
+ if (target) this.#moveTo(row, target.id, model);
149
+ };
150
+ this.#onFocusin = (event) => {
151
+ const scroller = event.currentTarget;
152
+ if (event.composedPath()[0] === scroller) {
153
+ const from = event.relatedTarget;
154
+ if (!(from instanceof Node && scroller.contains(from))) {
155
+ this.#focusPending = true;
156
+ this.requestUpdate();
157
+ }
158
+ return;
159
+ }
160
+ for (const target of event.composedPath()) {
161
+ const cell = gridCell(target);
162
+ if (cell?.dataset.col !== void 0) {
163
+ const index = cell.getAttribute("role") === "columnheader" ? -1 : rowIndexOf(cell.parentElement);
164
+ const { col } = cell.dataset;
165
+ if (index !== this.#active?.index || col !== this.#active.col) {
166
+ this.#active = {
167
+ index,
168
+ rowId: this.#table.model.rowAt(index)?.id,
169
+ col
170
+ };
171
+ this.requestUpdate();
172
+ }
173
+ return;
174
+ }
175
+ if (target === event.currentTarget) return;
176
+ }
177
+ };
178
+ this.#onClick = (event) => {
179
+ this.#activate(event.composedPath());
180
+ };
181
+ }
182
+ static {
183
+ this.styles = baseStyles;
184
+ }
185
+ /**
186
+ * The sort order, e.g. `[{ id: 'total', desc: true }]`; `[]` when unsorted. Unset,
187
+ * the table keeps it itself and header clicks change it. Once set, it is
188
+ * controlled: a click only fires `argus-sort-change` with the proposed order,
189
+ * and nothing changes until you set the property. Set `undefined` to hand it back.
190
+ */
191
+ get sorting() {
192
+ return this.#sorting.value;
193
+ }
194
+ set sorting(value) {
195
+ this.#sorting.set(value);
196
+ }
197
+ /**
198
+ * Resized widths in px by column id, e.g. `{ name: 240 }`. A resized flex column
199
+ * becomes fixed. Uncontrolled until set, like `sorting`: once set, a resize only
200
+ * fires `argus-column-resize` with the proposed widths. `undefined` hands it back.
201
+ */
202
+ get columnSizing() {
203
+ return this.#sizing.value;
204
+ }
205
+ set columnSizing(value) {
206
+ this.#sizing.set(value);
207
+ }
208
+ #sorting;
209
+ #sizing;
210
+ /** A resize the table applied itself; its event fires once the update is done. */
211
+ #appliedResize;
212
+ /** A sort the table applied itself; its event fires once the update is done. */
213
+ #appliedSort;
214
+ #internals;
215
+ #table;
216
+ #virtual;
217
+ #rowPx;
218
+ #probeObserver;
219
+ #window;
220
+ #active;
221
+ /** Move focus to the active cell after the next update (keyboard navigation). */
222
+ #focusPending;
223
+ #lastRange;
224
+ connectedCallback() {
225
+ super.connectedCallback();
226
+ if (!this.hasAttribute("role")) this.setAttribute("role", "grid");
227
+ if (this.hasUpdated) this.#observeProbe();
228
+ }
229
+ disconnectedCallback() {
230
+ super.disconnectedCallback();
231
+ this.#probeObserver?.disconnect();
232
+ }
233
+ /** Scrolls so that the row at `index` (in the current row order) is at the top. */
234
+ async scrollToRow(index) {
235
+ await this.updateComplete;
236
+ this.#virtual.scrollToIndex(index);
237
+ await this.updateComplete;
238
+ }
239
+ willUpdate(changed) {
240
+ if (changed.has("columns") || changed.has("data") || changed.has("getRowId") || changed.has("sorting") || changed.has("columnSizing")) this.#table.update({
241
+ columns: this.columns,
242
+ data: this.data,
243
+ getRowId: this.getRowId,
244
+ sorting: this.#sorting.value,
245
+ columnSizing: this.#sizing.value
246
+ });
247
+ const model = this.#table.model;
248
+ this.#reconcileActive(model);
249
+ const active = this.#active;
250
+ this.#internals.ariaRowCount = String(model.rowCount + 1);
251
+ this.#internals.ariaColCount = String(model.columns.length);
252
+ this.#virtual.configure({
253
+ count: model.rowCount,
254
+ rowHeight: this.#rowPx,
255
+ overscan: Math.max(0, this.overscan),
256
+ rowIdAt: (index) => model.rowAt(index)?.id ?? String(index),
257
+ keep: active?.rowId === void 0 ? void 0 : {
258
+ id: active.rowId,
259
+ index: active.index
260
+ }
261
+ });
262
+ }
263
+ render() {
264
+ const model = this.#table.model;
265
+ const columns = model.columns;
266
+ this.#window = this.#virtual.computeWindow();
267
+ const pinned = (side) => `${String(columns.reduce((sum, c) => c.pin === side ? sum + c.width : sum, 0))}px`;
268
+ const style = {
269
+ "--_cols": columns.map(track).join(" "),
270
+ "--_width": `${String(model.totalWidth)}px`,
271
+ "--_pin-start": pinned("start"),
272
+ "--_pin-end": pinned("end")
273
+ };
274
+ const active = this.#active;
275
+ const bodyActive = active && active.index >= 0 ? {
276
+ index: active.index,
277
+ col: active.col
278
+ } : void 0;
279
+ if (this.rowHeight !== void 0 && this.rowHeight > 0) style["--_row-height"] = `${String(this.rowHeight)}px`;
280
+ 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>`;
292
+ }
293
+ firstUpdated() {
294
+ this.#observeProbe();
295
+ this.#measureRowHeight();
296
+ }
297
+ updated(changed) {
298
+ renderLightDom(this, this.#table.model, this.#window.rows);
299
+ if (this.#focusPending) {
300
+ this.#focusPending = false;
301
+ this.#focusActive();
302
+ }
303
+ if (changed.has("density") || changed.has("rowHeight")) this.#measureRowHeight();
304
+ if (this.#appliedResize) {
305
+ const detail = this.#appliedResize;
306
+ this.#appliedResize = void 0;
307
+ fire(this, COLUMN_RESIZE, detail);
308
+ }
309
+ if (this.#appliedSort) {
310
+ const sorting = this.#appliedSort;
311
+ this.#appliedSort = void 0;
312
+ fire(this, SORT_CHANGE, { sorting });
313
+ }
314
+ const { range } = this.#window;
315
+ const key = range ? `${String(range.start)}:${String(range.end)}` : "";
316
+ if (range && key !== this.#lastRange) {
317
+ this.#lastRange = key;
318
+ fire(this, RANGE_CHANGE, {
319
+ start: range.start,
320
+ end: range.end
321
+ });
322
+ }
323
+ }
324
+ #headerHandlers;
325
+ /**
326
+ * Keys on a focused cell or header. Keys from content inside a cell (an input,
327
+ * a button) are left to that content.
328
+ */
329
+ #onKeydown;
330
+ /** Focus that lands in a cell (a click, or content inside it) makes that cell active. */
331
+ #onFocusin;
332
+ /** `argus-cell-click`, then `argus-row-click`, for clicks in the body. */
333
+ #onClick;
334
+ /**
335
+ * Fires `argus-cell-click` (when a cell is on the path), then `argus-row-click`,
336
+ * for the body row on an event path. Clicks and Enter on a body cell use it.
337
+ */
338
+ #activate(path) {
339
+ let columnId;
340
+ for (const target of path) {
341
+ if (!(target instanceof HTMLElement)) continue;
342
+ const role = target.getAttribute("role");
343
+ if (role === "gridcell") columnId = target.dataset.col;
344
+ if (role !== "row") continue;
345
+ const model = this.#table.model;
346
+ const index = rowIndexOf(target);
347
+ const row = index >= 0 ? model.rowAt(index) : void 0;
348
+ if (!row) return;
349
+ if (columnId !== void 0) fire(this, CELL_CLICK, {
350
+ row: row.original,
351
+ columnId,
352
+ value: model.valueAt(index, columnId)
353
+ });
354
+ fire(this, ROW_CLICK, {
355
+ row: row.original,
356
+ rowId: row.id,
357
+ index
358
+ });
359
+ return;
360
+ }
361
+ }
362
+ /** Makes a cell active and focuses it after the update. The keep-rendered rule renders its row. */
363
+ #moveTo(index, col, model) {
364
+ this.#active = {
365
+ index,
366
+ rowId: index >= 0 ? model.rowAt(index)?.id : void 0,
367
+ col
368
+ };
369
+ this.#focusPending = true;
370
+ this.requestUpdate();
371
+ }
372
+ #focusActive() {
373
+ const active = this.#active;
374
+ if (!active) return;
375
+ const col = CSS.escape(active.col);
376
+ const selector = active.index === -1 ? `[part~='header-row'] > [data-col="${col}"]` : `[part~='body'] > [aria-rowindex="${String(active.index + 2)}"] > [data-col="${col}"]`;
377
+ const cell = this.renderRoot.querySelector(selector);
378
+ if (!cell) return;
379
+ cell.scrollIntoView({
380
+ block: "nearest",
381
+ inline: "nearest"
382
+ });
383
+ cell.focus({ preventScroll: true });
384
+ }
385
+ /**
386
+ * Keeps the active cell valid after columns or rows change: on the same row id
387
+ * when it still exists (it may have moved), else at the same index, clamped.
388
+ * Defaults to the first header. If its row went away while the grid had focus,
389
+ * focus follows to the replacement.
390
+ */
391
+ #reconcileActive(model) {
392
+ const columns = model.columns;
393
+ const first = columns[0];
394
+ if (!first) {
395
+ this.#active = void 0;
396
+ return;
397
+ }
398
+ const prev = this.#active ?? {
399
+ index: -1,
400
+ rowId: void 0,
401
+ col: first.id
402
+ };
403
+ const col = columns.some((c) => c.id === prev.col) ? prev.col : first.id;
404
+ let { index, rowId } = prev;
405
+ if (rowId !== void 0) {
406
+ const found = model.indexOf(rowId);
407
+ if (found === void 0) {
408
+ index = Math.min(index, model.rowCount - 1);
409
+ rowId = model.rowAt(index)?.id;
410
+ if (this.matches(":focus-within")) this.#focusPending = true;
411
+ } else index = found;
412
+ }
413
+ if (index !== prev.index || rowId !== prev.rowId || col !== prev.col || !this.#active) this.#active = {
414
+ index,
415
+ rowId,
416
+ col
417
+ };
418
+ }
419
+ /**
420
+ * The width a resize starts from: the latest requested width (key presses can
421
+ * come faster than renders), else the model's fixed width, else the rendered
422
+ * width of a flex column.
423
+ */
424
+ #currentWidth(columnId, cell) {
425
+ const sized = this.#sizing.value[columnId];
426
+ if (sized !== void 0) return sized;
427
+ const view = this.#table.model.columns.find((c) => c.id === columnId);
428
+ return view && view.flex === void 0 ? view.width : cell.getBoundingClientRect().width;
429
+ }
430
+ #direction() {
431
+ return getComputedStyle(this).direction === "rtl" ? -1 : 1;
432
+ }
433
+ /** Sets one column's width (clamped by core), through the controlled/uncontrolled rules. */
434
+ #requestResize(columnId, width) {
435
+ const next = this.#table.model.nextSizing(columnId, width);
436
+ const target = next?.[columnId];
437
+ if (!next || target === void 0 || target === this.#sizing.value[columnId]) return;
438
+ const old = this.#sizing.value;
439
+ const { value, applied } = this.#sizing.request(next);
440
+ const detail = {
441
+ columnId,
442
+ width: target,
443
+ sizing: value
444
+ };
445
+ if (applied) {
446
+ this.#appliedResize = detail;
447
+ this.requestUpdate("columnSizing", old);
448
+ } else fire(this, COLUMN_RESIZE, detail);
449
+ }
450
+ /** asc → desc → unsorted, through the controlled/uncontrolled rules. */
451
+ #requestSort(columnId) {
452
+ const next = this.#table.model.nextSorting(columnId);
453
+ if (!next) return;
454
+ const old = this.#sorting.value;
455
+ const { value, applied } = this.#sorting.request(next);
456
+ if (applied) {
457
+ this.#appliedSort = value;
458
+ this.requestUpdate("sorting", old);
459
+ } else fire(this, SORT_CHANGE, { sorting: value });
460
+ }
461
+ #observeProbe() {
462
+ const probe = this.renderRoot.querySelector(".probe");
463
+ if (!probe) return;
464
+ this.#probeObserver ??= new ResizeObserver(() => {
465
+ this.#measureRowHeight();
466
+ });
467
+ this.#probeObserver.observe(probe);
468
+ }
469
+ /** Reads the resolved row height, so themes and density change the virtualizer too. */
470
+ #measureRowHeight() {
471
+ const px = this.renderRoot.querySelector(".probe")?.getBoundingClientRect().height ?? 0;
472
+ if (px > 0 && Math.abs(px - this.#rowPx) > .01) {
473
+ this.#rowPx = px;
474
+ this.requestUpdate();
475
+ }
476
+ }
477
+ };
478
+ __decorate([property({ attribute: false })], ArgusTable.prototype, "columns", void 0);
479
+ __decorate([property({ attribute: false })], ArgusTable.prototype, "data", void 0);
480
+ __decorate([property({ attribute: false })], ArgusTable.prototype, "getRowId", void 0);
481
+ __decorate([property({
482
+ attribute: "row-height",
483
+ type: Number
484
+ })], ArgusTable.prototype, "rowHeight", void 0);
485
+ __decorate([property({ reflect: true })], ArgusTable.prototype, "density", void 0);
486
+ __decorate([property({ type: Number })], ArgusTable.prototype, "overscan", void 0);
487
+ __decorate([property({ attribute: false })], ArgusTable.prototype, "sorting", null);
488
+ __decorate([property({ attribute: false })], ArgusTable.prototype, "columnSizing", null);
489
+ ArgusTable = __decorate([customElement("argus-table")], ArgusTable);
490
+ export { ArgusTable };
491
+
492
+ //# sourceMappingURL=argus-table.js.map
@@ -0,0 +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":";;;;;;;;;;;;;;;;;;;;AA+BA,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;;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;EACS,KAAA,SAAA;EAEgB,KAAA,UAAA;GAAE,MAAM,CAAC;GAAG,WAAW;GAAG,OAAO;EAAK;EAG/C,KAAA,gBAAA;EACH,KAAA,aAAA;EAqHc,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;;;EAjUyC,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;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,UAAU,SACd,GAAG,OAAO,QAAQ,QAAQ,KAAK,MAAO,EAAE,QAAQ,OAAO,MAAM,EAAE,QAAQ,KAAM,CAAC,CAAC,EAAE;EACnF,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;;;cAGD,SAAS,KAAK,EAAE;iBACb,KAAK,WAAW;iBAChB,KAAK,WAAW;eAClB,KAAK,SAAS;;QAErB,aAAa,SAAS,KAAK,iBAAiB,QAAQ,UAAU,KAAK,OAAO,MAAM,KAAA,CAAS,EAAE;QAE3F,MAAM,aAAa,IACf,IAAI,8DACJ,WAAW,KAAK,QAAQ,MAAM,SAAS,KAAK,QAAQ,WAAW,UAAU,EAC9E;;;CAGL;CAEA,eAAwC;EACtC,KAAK,cAAc;EACnB,KAAK,kBAAkB;CACzB;CAEA,QAA2B,SAAqC;EAC9D,eAAe,MAAM,KAAK,OAAO,OAAO,KAAK,QAAQ,IAAI;EACzD,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;AA/cGA,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"}