argus-table 0.2.1 → 0.3.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.
- package/README.md +1 -1
- package/dist/argus-column.js +24 -1
- package/dist/argus-column.js.map +1 -1
- package/dist/argus-table.js +1026 -167
- package/dist/argus-table.js.map +1 -1
- package/dist/cdn/argus-table.js +74 -50
- package/dist/cdn/argus-table.js.map +1 -1
- package/dist/controllers/auto-width.js +11 -0
- package/dist/controllers/auto-width.js.map +1 -1
- package/dist/controllers/column-window.js +139 -0
- package/dist/controllers/column-window.js.map +1 -0
- package/dist/controllers/data-controller.js +9 -4
- package/dist/controllers/data-controller.js.map +1 -1
- package/dist/controllers/plugins.js +102 -0
- package/dist/controllers/plugins.js.map +1 -0
- package/dist/controllers/reorder-drag.js +9 -10
- package/dist/controllers/reorder-drag.js.map +1 -1
- package/dist/controllers/row-measure.js +121 -0
- package/dist/controllers/row-measure.js.map +1 -0
- package/dist/controllers/virtual-controller.js +316 -10
- package/dist/controllers/virtual-controller.js.map +1 -1
- package/dist/core/src/columns/column-order.js +14 -10
- package/dist/core/src/columns/column-order.js.map +1 -1
- package/dist/core/src/columns/resolve-columns.js +18 -4
- package/dist/core/src/columns/resolve-columns.js.map +1 -1
- package/dist/core/src/data/page-cache.js +4 -0
- package/dist/core/src/data/page-cache.js.map +1 -1
- package/dist/core/src/filters/filter.js +185 -0
- package/dist/core/src/filters/filter.js.map +1 -0
- package/dist/core/src/state/selection.js +70 -0
- package/dist/core/src/state/selection.js.map +1 -0
- package/dist/core/src/table/create-table.js +239 -64
- package/dist/core/src/table/create-table.js.map +1 -1
- package/dist/core/src/table/filtered-row-model.js +48 -0
- package/dist/core/src/table/filtered-row-model.js.map +1 -0
- package/dist/core/src/table/sorted-row-model.js +13 -2
- package/dist/core/src/table/sorted-row-model.js.map +1 -1
- package/dist/core/src/virtual/column-window.js +72 -0
- package/dist/core/src/virtual/column-window.js.map +1 -0
- package/dist/core/src/virtual/row-pool.js +2 -1
- package/dist/core/src/virtual/row-pool.js.map +1 -1
- package/dist/core/src/virtual/row-sizes.js +156 -0
- package/dist/core/src/virtual/row-sizes.js.map +1 -0
- package/dist/core/src/virtual/scroll-scale.js +41 -3
- package/dist/core/src/virtual/scroll-scale.js.map +1 -1
- package/dist/custom-elements.json +1320 -74
- package/dist/events.js +9 -1
- package/dist/events.js.map +1 -1
- package/dist/index.d.ts +609 -74
- package/dist/index.js +3 -2
- package/dist/interactive.js +22 -0
- package/dist/interactive.js.map +1 -0
- package/dist/max-height.js +22 -8
- package/dist/max-height.js.map +1 -1
- package/dist/render/body.js +47 -7
- package/dist/render/body.js.map +1 -1
- package/dist/render/cells.js +14 -11
- package/dist/render/cells.js.map +1 -1
- package/dist/render/content-directive.js +115 -0
- package/dist/render/content-directive.js.map +1 -0
- package/dist/render/content.js +20 -9
- package/dist/render/content.js.map +1 -1
- package/dist/render/header.js +24 -5
- package/dist/render/header.js.map +1 -1
- package/dist/render/pinned-rows.js +1 -1
- package/dist/render/pinned-rows.js.map +1 -1
- package/dist/styles/base.js +1 -1
- package/dist/styles/base.js.map +1 -1
- package/package.json +1 -1
package/dist/argus-table.js
CHANGED
|
@@ -1,18 +1,25 @@
|
|
|
1
|
+
import { clickSelection, sameSelection, selectAll, selectRange, toggleRow } from "./core/src/state/selection.js";
|
|
1
2
|
import { controllable } from "./core/src/state/controllable.js";
|
|
2
3
|
import { RowLayout } from "./core/src/grid/row-layout.js";
|
|
4
|
+
import "./core/src/table/create-table.js";
|
|
3
5
|
import { announce } from "./announce.js";
|
|
4
6
|
import { AutoWidths, measureColumns } from "./controllers/auto-width.js";
|
|
5
7
|
import { ColumnChildrenController } from "./controllers/column-children.js";
|
|
8
|
+
import { ColumnWindowController } from "./controllers/column-window.js";
|
|
6
9
|
import { DataController } from "./controllers/data-controller.js";
|
|
7
10
|
import { PinScrollController } from "./controllers/pin-scroll-controller.js";
|
|
11
|
+
import { PluginController, sameItems } from "./controllers/plugins.js";
|
|
12
|
+
import { RowMeasure } from "./controllers/row-measure.js";
|
|
8
13
|
import { canStartReorder, dropTarget, startReorderDrag } from "./controllers/reorder-drag.js";
|
|
9
14
|
import { startResizeDrag } from "./controllers/resize-drag.js";
|
|
10
15
|
import { TableController } from "./controllers/table-controller.js";
|
|
11
16
|
import { VirtualController } from "./controllers/virtual-controller.js";
|
|
12
|
-
import { CELL_CLICK, COLUMN_REORDER, COLUMN_RESIZE, LOAD_ERROR, RANGE_CHANGE, ROW_CLICK, SORT_CHANGE, fire } from "./events.js";
|
|
17
|
+
import { CELL_CLICK, COLUMN_FILTER, COLUMN_HIDE, COLUMN_PIN, COLUMN_REORDER, COLUMN_RESIZE, LOAD_ERROR, RANGE_CHANGE, ROW_CLICK, ROW_SELECT, SORT_CHANGE, fire } from "./events.js";
|
|
18
|
+
import { fromInteractive } from "./interactive.js";
|
|
13
19
|
import { bodyCap, watchMaxElementHeight } from "./max-height.js";
|
|
14
20
|
import { renderBody } from "./render/body.js";
|
|
15
21
|
import { renderLightDom } from "./render/cells.js";
|
|
22
|
+
import { content, flushContent } from "./render/content-directive.js";
|
|
16
23
|
import { renderHeader } from "./render/header.js";
|
|
17
24
|
import { renderPinnedRows } from "./render/pinned-rows.js";
|
|
18
25
|
import { baseStyles } from "./styles/base.js";
|
|
@@ -34,17 +41,27 @@ function attachGridInternals(host) {
|
|
|
34
41
|
}
|
|
35
42
|
/**
|
|
36
43
|
* The sortable column a header click came from, if any. Works for slotted content
|
|
37
|
-
* too. Clicks on a resize handle never sort, so a click that ends a drag doesn't
|
|
44
|
+
* too. Clicks on a resize handle never sort, so a click that ends a drag doesn't,
|
|
45
|
+
* and neither do clicks on interactive content in the header (a button, an input).
|
|
38
46
|
*/
|
|
39
47
|
function sortColumnOf(event) {
|
|
40
48
|
for (const target of event.composedPath()) {
|
|
41
49
|
if (!(target instanceof HTMLElement)) continue;
|
|
42
50
|
if (target.dataset.resizeCol !== void 0) return void 0;
|
|
51
|
+
if (target.matches("button,input,select,textarea,a[href],label,[contenteditable],[popover],dialog")) return void 0;
|
|
43
52
|
if (target.dataset.sortCol !== void 0) return target.dataset.sortCol;
|
|
44
53
|
if (target.getAttribute("part") === "header-row") return void 0;
|
|
45
54
|
}
|
|
46
55
|
}
|
|
47
56
|
var RESIZE_STEP = 10;
|
|
57
|
+
/** `row-height`: px (`48` or `48px`) or `auto`; anything else is unset. */
|
|
58
|
+
var rowHeightConverter = { fromAttribute(value) {
|
|
59
|
+
if (value === null) return void 0;
|
|
60
|
+
const text = value.trim().toLowerCase();
|
|
61
|
+
if (text === "auto") return "auto";
|
|
62
|
+
const px = Number(text.replace(/px$/, ""));
|
|
63
|
+
return text !== "" && px > 0 ? px : void 0;
|
|
64
|
+
} };
|
|
48
65
|
var HEADER = {
|
|
49
66
|
section: "header",
|
|
50
67
|
index: 0
|
|
@@ -70,7 +87,7 @@ function gridRowOf(row, layout) {
|
|
|
70
87
|
* covers the pinned columns and casts `--argus-pinned-shadow` onto the rest;
|
|
71
88
|
* PinScrollController sizes it to the visible area.
|
|
72
89
|
*/
|
|
73
|
-
function pinShadow(side, width,
|
|
90
|
+
function pinShadow(side, width, contentPx) {
|
|
74
91
|
if (width === 0) return nothing;
|
|
75
92
|
return html`<div
|
|
76
93
|
class="pin-shadow"
|
|
@@ -78,7 +95,7 @@ function pinShadow(side, width, rows, rowPx) {
|
|
|
78
95
|
aria-hidden="true"
|
|
79
96
|
style=${styleMap({
|
|
80
97
|
inlineSize: `${String(width)}px`,
|
|
81
|
-
"--_content-h": `${String(
|
|
98
|
+
"--_content-h": `${String(contentPx)}px`
|
|
82
99
|
})}
|
|
83
100
|
></div>`;
|
|
84
101
|
}
|
|
@@ -87,17 +104,275 @@ function track(column) {
|
|
|
87
104
|
return column.flex === void 0 ? `${String(column.width)}px` : `minmax(${String(column.minWidth)}px, ${String(column.flex)}fr)`;
|
|
88
105
|
}
|
|
89
106
|
var ArgusTable = class ArgusTable extends LitElement {
|
|
90
|
-
|
|
91
|
-
|
|
107
|
+
static {
|
|
108
|
+
this.styles = baseStyles;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Column definitions. Property only: functions can't be attributes. While empty,
|
|
112
|
+
* the table reads its `<argus-column>` children instead. `null` or `undefined`
|
|
113
|
+
* means `[]`.
|
|
114
|
+
*/
|
|
115
|
+
get columns() {
|
|
116
|
+
return this.#columnsProp;
|
|
117
|
+
}
|
|
118
|
+
set columns(value) {
|
|
119
|
+
this.#columnsProp = value ?? [];
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* The rows to show. Replace the array to update; changes inside it aren't
|
|
123
|
+
* detected. `null` or `undefined` means `[]`.
|
|
124
|
+
*/
|
|
125
|
+
get data() {
|
|
126
|
+
return this.#dataProp;
|
|
127
|
+
}
|
|
128
|
+
set data(value) {
|
|
129
|
+
this.#dataProp = value ?? [];
|
|
130
|
+
}
|
|
131
|
+
/** Rows rendered above and below the visible ones. `null` or `undefined` means the default, 6. */
|
|
132
|
+
get overscan() {
|
|
133
|
+
return this.#overscan;
|
|
134
|
+
}
|
|
135
|
+
set overscan(value) {
|
|
136
|
+
this.#overscan = value ?? 6;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* The sort order, e.g. `[{ id: 'total', desc: true }]`; `[]` when unsorted. Unset,
|
|
140
|
+
* the table keeps it itself and header clicks change it. Once set, it is
|
|
141
|
+
* controlled: a click only fires `argus-sort-change` with the proposed order,
|
|
142
|
+
* and nothing changes until you set the property. Set `undefined` to hand it back.
|
|
143
|
+
*/
|
|
144
|
+
get sorting() {
|
|
145
|
+
return this.#sorting.value;
|
|
146
|
+
}
|
|
147
|
+
set sorting(value) {
|
|
148
|
+
this.#sorting.set(value);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Resized widths in px by column id, e.g. `{ name: 240 }`. A resized flex column
|
|
152
|
+
* becomes fixed. Uncontrolled until set, like `sorting`: once set, a resize only
|
|
153
|
+
* fires `argus-column-resize` with the proposed widths. `undefined` hands it back.
|
|
154
|
+
*/
|
|
155
|
+
get columnSizing() {
|
|
156
|
+
return this.#sizing.value;
|
|
157
|
+
}
|
|
158
|
+
set columnSizing(value) {
|
|
159
|
+
this.#sizing.set(value);
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* The column order: column ids as the table shows them, e.g.
|
|
163
|
+
* `['id', 'total', 'customer']`. `[]` (the default) keeps the order of `columns`.
|
|
164
|
+
* Each pin group (start-pinned, scrolling, end-pinned) takes its columns in this
|
|
165
|
+
* order; unknown ids are ignored and columns it doesn't list follow the listed
|
|
166
|
+
* ones of their group. After a move it holds every column id. Uncontrolled
|
|
167
|
+
* until set, like `sorting`: once set, a move only fires `argus-column-reorder`
|
|
168
|
+
* with the proposed order. `undefined` hands it back.
|
|
169
|
+
*/
|
|
170
|
+
get columnOrder() {
|
|
171
|
+
return this.#order.value;
|
|
172
|
+
}
|
|
173
|
+
set columnOrder(value) {
|
|
174
|
+
this.#order.set(value);
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Pins by column id, over the columns' own `pin`: `'start'` or `'end'` pins a
|
|
178
|
+
* column, `false` unpins one whose definition pins it, e.g.
|
|
179
|
+
* `{ total: 'end', id: false }`. Columns it doesn't list keep their `pin`. A pinned
|
|
180
|
+
* column joins the edge of its group nearest its place in `columnOrder`, and stops
|
|
181
|
+
* flexing. Uncontrolled until set, like `sorting`: once set, pinning from a plugin
|
|
182
|
+
* only fires `argus-column-pin` with the proposed pins. `undefined` hands it back.
|
|
183
|
+
*/
|
|
184
|
+
get columnPinning() {
|
|
185
|
+
return this.#pinning.value;
|
|
186
|
+
}
|
|
187
|
+
set columnPinning(value) {
|
|
188
|
+
this.#pinning.set(value);
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Visibility by column id, over the columns' own `hidden`: `false` hides a column,
|
|
192
|
+
* `true` shows one defined as `hidden`, e.g. `{ email: false }`. Columns it
|
|
193
|
+
* doesn't list keep their `hidden`. A hidden column isn't rendered, but sorting by
|
|
194
|
+
* it still works and it keeps its place in `columnOrder`. Uncontrolled until set,
|
|
195
|
+
* like `sorting`: once set, hiding or showing from a plugin only fires
|
|
196
|
+
* `argus-column-hide` with the proposed visibility. `undefined` hands it back.
|
|
197
|
+
*/
|
|
198
|
+
get columnVisibility() {
|
|
199
|
+
return this.#visibility.value;
|
|
200
|
+
}
|
|
201
|
+
set columnVisibility(value) {
|
|
202
|
+
this.#visibility.set(value);
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Filters by column id, e.g. `{ city: { kind: 'set', values: ['Berlin'] } }` or
|
|
206
|
+
* `{ total: { kind: 'number', op: 'gt', value: 100 } }`. A row shows when it passes
|
|
207
|
+
* every filter and `quickFilter`. Filters on hidden columns still apply; unknown ids
|
|
208
|
+
* are ignored. A column's `filterFn` replaces the built-in matching. With a
|
|
209
|
+
* `dataSource`, the filters go to `getRows()` and the server filters. Users set
|
|
210
|
+
* filters through plugins (the column menu) on columns with a `filter` kind.
|
|
211
|
+
* Uncontrolled until set, like `sorting`: once set, a change from a plugin only
|
|
212
|
+
* fires `argus-column-filter` with the proposed filters. `undefined` hands it back.
|
|
213
|
+
*/
|
|
214
|
+
get columnFilters() {
|
|
215
|
+
return this.#filters.value;
|
|
216
|
+
}
|
|
217
|
+
set columnFilters(value) {
|
|
218
|
+
this.#filters.set(value);
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Shows only rows that contain each word of this text in one of their shown
|
|
222
|
+
* columns, ignoring case, e.g. from a search box. Values are matched as text
|
|
223
|
+
* (dates as `2026-11-10`); objects aren't searched. With a `dataSource`, it goes
|
|
224
|
+
* to `getRows()` and the server filters. `''` (the default) shows every row.
|
|
225
|
+
*/
|
|
226
|
+
get quickFilter() {
|
|
227
|
+
return this.#quickFilter;
|
|
228
|
+
}
|
|
229
|
+
set quickFilter(value) {
|
|
230
|
+
this.#quickFilter = value ?? "";
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* The selected rows, as row ids (from `getRowId`, or the row's index as a
|
|
234
|
+
* string), e.g. `['p4', 'p7']`. Uncontrolled until set, like `sorting`: once
|
|
235
|
+
* set, a click only fires `argus-row-select` with the proposed ids.
|
|
236
|
+
* `undefined` hands it back. The table never drops ids on its own: they stay
|
|
237
|
+
* through sorting, a new `data` or reloaded pages. With a `dataSource`,
|
|
238
|
+
* Ctrl/Cmd+A selects the loaded rows only.
|
|
239
|
+
*/
|
|
240
|
+
get selection() {
|
|
241
|
+
return this.#selection.value;
|
|
242
|
+
}
|
|
243
|
+
set selection(value) {
|
|
244
|
+
this.#selection.set(value);
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Add-ons, such as `headerMenu()` from `@argus-table/header-menu`. Each one can
|
|
248
|
+
* change the column definitions, handle keys on a focused header or cell, and
|
|
249
|
+
* use the table through a small API. They run in array order. Replace the
|
|
250
|
+
* array to change them; an array with the same plugins in the same order
|
|
251
|
+
* changes nothing. `null` or `undefined` means `[]`.
|
|
252
|
+
*/
|
|
253
|
+
get plugins() {
|
|
254
|
+
return this.#pluginsProp;
|
|
255
|
+
}
|
|
256
|
+
set plugins(value) {
|
|
257
|
+
const next = value ?? [];
|
|
258
|
+
if (!sameItems(next, this.#pluginsProp)) this.#pluginsProp = next;
|
|
259
|
+
}
|
|
260
|
+
#columnsProp;
|
|
261
|
+
#pluginsProp;
|
|
262
|
+
#dataProp;
|
|
263
|
+
#overscan;
|
|
264
|
+
#sorting;
|
|
265
|
+
#sizing;
|
|
266
|
+
#order;
|
|
267
|
+
#pinning;
|
|
268
|
+
#visibility;
|
|
269
|
+
#filters;
|
|
270
|
+
#quickFilter;
|
|
271
|
+
/** Filters the table applied itself, by column; their events fire once the update is done. */
|
|
272
|
+
#appliedFilters;
|
|
273
|
+
/** Announce the row count after the update: the filters changed. */
|
|
274
|
+
#announceRows;
|
|
275
|
+
/** Pins the table applied itself, by column; their events fire once the update is done. */
|
|
276
|
+
#appliedPins;
|
|
277
|
+
/** Hidden or shown columns the table applied itself; their events fire once the update is done. */
|
|
278
|
+
#appliedHides;
|
|
279
|
+
#selection;
|
|
280
|
+
/** `selection` as a set, for the rows rendered; rebuilt when `selection` changes. */
|
|
281
|
+
#selectedIds;
|
|
282
|
+
/** The row a Shift range starts from: the last row clicked or toggled without Shift. */
|
|
283
|
+
#anchor;
|
|
284
|
+
/** A selection the table applied itself; its event fires once the update is done. */
|
|
285
|
+
#appliedSelection;
|
|
286
|
+
/** Whether the checkbox column was last shown; a change re-measures the pinned columns. */
|
|
287
|
+
#selectionColumn;
|
|
288
|
+
#selectionColumnChanged;
|
|
289
|
+
/** The select-all checkbox's state, kept until `selection`, `data` or `getRowId` change. */
|
|
290
|
+
#checkAll;
|
|
291
|
+
/** Moves the table applied itself, by column; their events fire once the update is done. */
|
|
292
|
+
#appliedReorders;
|
|
293
|
+
/** The header being dragged to a new position, if any. */
|
|
294
|
+
#drag;
|
|
295
|
+
/** A move from the keyboard, announced once it has rendered: the column and its old position. */
|
|
296
|
+
#announceMove;
|
|
297
|
+
/** Resizes the table applied itself, by column; their events fire once the update is done. */
|
|
298
|
+
#appliedResizes;
|
|
299
|
+
/** Columns `autosizeColumns()` renders out of view for its measurement. */
|
|
300
|
+
#measureKeep;
|
|
301
|
+
/** Measured widths of `width: 'auto'` columns. */
|
|
302
|
+
#autoWidths;
|
|
303
|
+
/** The auto widths last passed to the model; a change re-measures the pinned columns' offsets. */
|
|
304
|
+
#pushedAutoWidths;
|
|
305
|
+
#autoWidthsChanged;
|
|
306
|
+
/** A sort the table applied itself; its event fires once the update is done. */
|
|
307
|
+
#appliedSort;
|
|
308
|
+
#internals;
|
|
309
|
+
#table;
|
|
310
|
+
#children;
|
|
311
|
+
/** What plugins get: closures over the table's private state. */
|
|
312
|
+
#pluginHost;
|
|
313
|
+
#plugins;
|
|
314
|
+
#columnsById;
|
|
315
|
+
/** The columns last passed to the model: `columns`, or the `<argus-column>` children. */
|
|
316
|
+
#columns;
|
|
317
|
+
#columnsChanged;
|
|
318
|
+
#warnedColumns;
|
|
319
|
+
/** End of the light-DOM part holding cell content, so later children stay outside it. */
|
|
320
|
+
#contentEnd;
|
|
321
|
+
/** The light-DOM part: disconnected with the table, so foreign content is released. */
|
|
322
|
+
#lightPart;
|
|
323
|
+
#lightConnected;
|
|
324
|
+
#data;
|
|
325
|
+
/** Scroll back to the top after the update (a new sort order for a data source). */
|
|
326
|
+
#scrollToTop;
|
|
327
|
+
#virtual;
|
|
328
|
+
#pinScroll;
|
|
329
|
+
#columnWindow;
|
|
330
|
+
/** The columns the last render rendered (all of them, unless `virtualizeColumns`). */
|
|
331
|
+
#rendered;
|
|
332
|
+
#rowPx;
|
|
333
|
+
#probeObserver;
|
|
334
|
+
/** The column template and row height the rows were last read for. */
|
|
335
|
+
#rowsKey;
|
|
336
|
+
/** Rows of different heights: reads the rendered rows (`rowHeight: 'auto'` only). */
|
|
337
|
+
#rowMeasure;
|
|
338
|
+
#window;
|
|
339
|
+
#active;
|
|
340
|
+
/** Move focus to the active cell after the next update (keyboard navigation). */
|
|
341
|
+
#focusPending;
|
|
342
|
+
#lastRange;
|
|
343
|
+
#unwatchMaxHeight;
|
|
344
|
+
/**
|
|
345
|
+
* The row `setState()` still has to scroll to: a data source's rows that far
|
|
346
|
+
* aren't counted yet. Scrolled to again whenever the row count changes.
|
|
347
|
+
*/
|
|
348
|
+
#scrollTarget;
|
|
349
|
+
constructor() {
|
|
350
|
+
super();
|
|
92
351
|
this.density = "comfortable";
|
|
352
|
+
this.virtualizeColumns = false;
|
|
353
|
+
this.selectionMode = "none";
|
|
354
|
+
this.selectionCheckboxes = false;
|
|
93
355
|
this.#columnsProp = [];
|
|
356
|
+
this.#pluginsProp = [];
|
|
94
357
|
this.#dataProp = [];
|
|
95
358
|
this.#overscan = 6;
|
|
96
359
|
this.#sorting = controllable([]);
|
|
97
360
|
this.#sizing = controllable({});
|
|
98
361
|
this.#order = controllable([]);
|
|
362
|
+
this.#pinning = controllable({});
|
|
363
|
+
this.#visibility = controllable({});
|
|
364
|
+
this.#filters = controllable({});
|
|
365
|
+
this.#quickFilter = "";
|
|
366
|
+
this.#appliedFilters = /* @__PURE__ */ new Map();
|
|
367
|
+
this.#announceRows = false;
|
|
368
|
+
this.#appliedPins = /* @__PURE__ */ new Map();
|
|
369
|
+
this.#appliedHides = /* @__PURE__ */ new Map();
|
|
370
|
+
this.#selection = controllable([]);
|
|
371
|
+
this.#selectionColumn = false;
|
|
372
|
+
this.#selectionColumnChanged = false;
|
|
99
373
|
this.#appliedReorders = /* @__PURE__ */ new Map();
|
|
100
374
|
this.#appliedResizes = /* @__PURE__ */ new Map();
|
|
375
|
+
this.#measureKeep = [];
|
|
101
376
|
this.#autoWidths = new AutoWidths(() => {
|
|
102
377
|
this.requestUpdate();
|
|
103
378
|
});
|
|
@@ -105,14 +380,64 @@ var ArgusTable = class ArgusTable extends LitElement {
|
|
|
105
380
|
this.#internals = attachGridInternals(this);
|
|
106
381
|
this.#table = new TableController();
|
|
107
382
|
this.#children = new ColumnChildrenController(this);
|
|
383
|
+
this.#pluginHost = {
|
|
384
|
+
element: this,
|
|
385
|
+
column: (id) => this.#columnInfo(id),
|
|
386
|
+
columns: () => this.#table.model.allColumns.flatMap((c) => {
|
|
387
|
+
const info = this.#columnInfo(c.id);
|
|
388
|
+
return info ? [info] : [];
|
|
389
|
+
}),
|
|
390
|
+
setPin: (columnId, pin) => {
|
|
391
|
+
this.#requestPin(columnId, pin);
|
|
392
|
+
},
|
|
393
|
+
setHidden: (columnId, hidden) => {
|
|
394
|
+
this.#requestHide(columnId, hidden);
|
|
395
|
+
},
|
|
396
|
+
setFilter: (columnId, filter) => {
|
|
397
|
+
this.#requestFilter(columnId, filter);
|
|
398
|
+
},
|
|
399
|
+
filterValues: (columnId, limit) => this.#table.model.filterValues(columnId, limit),
|
|
400
|
+
cellOf: (event) => this.#cellTarget(event),
|
|
401
|
+
setSort: (columnId, direction) => {
|
|
402
|
+
this.#setSort(columnId, direction);
|
|
403
|
+
},
|
|
404
|
+
focusHeader: (columnId) => {
|
|
405
|
+
this.#moveTo(HEADER, columnId, this.#table.model);
|
|
406
|
+
},
|
|
407
|
+
announce: (message) => {
|
|
408
|
+
announce(this.ownerDocument, message);
|
|
409
|
+
},
|
|
410
|
+
invalidateColumns: () => {
|
|
411
|
+
this.#plugins.invalidate();
|
|
412
|
+
this.requestUpdate();
|
|
413
|
+
},
|
|
414
|
+
requestUpdate: () => {
|
|
415
|
+
this.requestUpdate();
|
|
416
|
+
},
|
|
417
|
+
row: (index) => this.#table.model.rowAt(index)?.original,
|
|
418
|
+
content: (value) => content(value)
|
|
419
|
+
};
|
|
420
|
+
this.#plugins = new PluginController(this, this.#pluginHost);
|
|
108
421
|
this.#columns = [];
|
|
109
422
|
this.#columnsChanged = false;
|
|
110
423
|
this.#warnedColumns = false;
|
|
424
|
+
this.#lightConnected = true;
|
|
111
425
|
this.#data = new DataController(this);
|
|
112
426
|
this.#scrollToTop = false;
|
|
113
427
|
this.#virtual = new VirtualController(this, () => this.renderRoot.querySelector("[part~=\"table\"]"));
|
|
114
428
|
this.#pinScroll = new PinScrollController(this, () => this.renderRoot.querySelector("[part~=\"table\"]"));
|
|
429
|
+
this.#columnWindow = new ColumnWindowController(this, () => this.renderRoot.querySelector("[part~=\"table\"]"));
|
|
430
|
+
this.#rendered = [];
|
|
115
431
|
this.#rowPx = 40;
|
|
432
|
+
this.#rowsKey = "";
|
|
433
|
+
this.#rowMeasure = new RowMeasure(this, {
|
|
434
|
+
bodyIndex: (row) => {
|
|
435
|
+
const at = gridRowOf(row, layoutOf(this.#table.model));
|
|
436
|
+
return at?.section === "body" && this.#data.stateAt(at.index) === "loaded" ? at.index : void 0;
|
|
437
|
+
},
|
|
438
|
+
sizeOf: (index) => this.#virtual.sizeOf(index),
|
|
439
|
+
minimum: () => this.#rowPx
|
|
440
|
+
});
|
|
116
441
|
this.#window = {
|
|
117
442
|
rows: [],
|
|
118
443
|
totalSize: 0,
|
|
@@ -120,6 +445,9 @@ var ArgusTable = class ArgusTable extends LitElement {
|
|
|
120
445
|
};
|
|
121
446
|
this.#focusPending = false;
|
|
122
447
|
this.#lastRange = "";
|
|
448
|
+
this.#dropScrollTarget = () => {
|
|
449
|
+
this.#scrollTarget = void 0;
|
|
450
|
+
};
|
|
123
451
|
this.#onRetry = (event) => {
|
|
124
452
|
const button = event.currentTarget;
|
|
125
453
|
if (button.matches(":focus")) button.parentElement?.focus({ preventScroll: true });
|
|
@@ -164,6 +492,8 @@ var ArgusTable = class ArgusTable extends LitElement {
|
|
|
164
492
|
}, { once: true });
|
|
165
493
|
return;
|
|
166
494
|
}
|
|
495
|
+
const pluginTarget = id === "--argus-selection" ? void 0 : this.#cellTarget(event);
|
|
496
|
+
if (pluginTarget && this.#plugins.keydown(event, pluginTarget)) return;
|
|
167
497
|
const inHeader = cell.getAttribute("role") === "columnheader";
|
|
168
498
|
const { key } = event;
|
|
169
499
|
const arrow = key === "ArrowLeft" || key === "ArrowRight";
|
|
@@ -184,6 +514,7 @@ var ArgusTable = class ArgusTable extends LitElement {
|
|
|
184
514
|
this.#requestSort(id);
|
|
185
515
|
return;
|
|
186
516
|
}
|
|
517
|
+
if (this.#selectionKey(event, cell, id, inHeader, scroller)) return;
|
|
187
518
|
const corner = event.ctrlKey || event.metaKey;
|
|
188
519
|
if (event.altKey || event.shiftKey || corner && key !== "Home" && key !== "End") return;
|
|
189
520
|
const model = this.#table.model;
|
|
@@ -199,26 +530,13 @@ var ArgusTable = class ArgusTable extends LitElement {
|
|
|
199
530
|
const columns = model.columns;
|
|
200
531
|
let pos = layout.position(from);
|
|
201
532
|
const last = layout.total - 1;
|
|
202
|
-
const bodyFirst = layout.position({
|
|
203
|
-
section: "body",
|
|
204
|
-
index: 0
|
|
205
|
-
});
|
|
206
|
-
const bodyLast = bodyFirst + layout.body - 1;
|
|
207
|
-
const fit = Math.floor(scroller.clientHeight / this.#rowPx + .01);
|
|
208
|
-
const page = Math.max(1, fit - 1 - layout.top - layout.bottom);
|
|
209
533
|
let col = columns.findIndex((c) => c.id === id);
|
|
210
534
|
switch (key) {
|
|
211
535
|
case "PageDown":
|
|
212
|
-
if (layout.body > 0 && pos <= bodyLast) pos = Math.min(Math.max(pos, bodyFirst - 1) + page, bodyLast);
|
|
213
|
-
break;
|
|
214
536
|
case "PageUp":
|
|
215
|
-
if (layout.body > 0 && pos > bodyFirst) pos = Math.max(Math.min(pos, bodyLast + 1) - page, bodyFirst);
|
|
216
|
-
break;
|
|
217
537
|
case "ArrowDown":
|
|
218
|
-
pos = Math.min(pos + 1, last);
|
|
219
|
-
break;
|
|
220
538
|
case "ArrowUp":
|
|
221
|
-
pos =
|
|
539
|
+
pos = this.#verticalStep(key, pos, layout, scroller);
|
|
222
540
|
break;
|
|
223
541
|
case "ArrowRight":
|
|
224
542
|
col += this.#direction();
|
|
@@ -230,7 +548,7 @@ var ArgusTable = class ArgusTable extends LitElement {
|
|
|
230
548
|
col = 0;
|
|
231
549
|
if (corner) {
|
|
232
550
|
pos = 0;
|
|
233
|
-
|
|
551
|
+
this.#virtual.scrollToEdge("start");
|
|
234
552
|
}
|
|
235
553
|
break;
|
|
236
554
|
case "End":
|
|
@@ -241,8 +559,8 @@ var ArgusTable = class ArgusTable extends LitElement {
|
|
|
241
559
|
}
|
|
242
560
|
event.preventDefault();
|
|
243
561
|
const row = layout.at(pos) ?? HEADER;
|
|
244
|
-
if (key.startsWith("Page") && from.section === "body" && row.section === "body") this.#virtual.scrollBy((row.index - from.index)
|
|
245
|
-
if (corner && key === "End" && row.section === "bottom")
|
|
562
|
+
if (key.startsWith("Page") && from.section === "body" && row.section === "body") this.#virtual.scrollBy(this.#virtual.startOf(row.index) - this.#virtual.startOf(from.index));
|
|
563
|
+
if (corner && key === "End" && row.section === "bottom") this.#virtual.scrollToEdge("end");
|
|
246
564
|
const target = columns[Math.min(Math.max(col, 0), columns.length - 1)];
|
|
247
565
|
if (target) this.#moveTo(row, target.id, model);
|
|
248
566
|
};
|
|
@@ -277,125 +595,62 @@ var ArgusTable = class ArgusTable extends LitElement {
|
|
|
277
595
|
}
|
|
278
596
|
};
|
|
279
597
|
this.#onClick = (event) => {
|
|
280
|
-
|
|
598
|
+
const path = event.composedPath();
|
|
599
|
+
this.#activate(path);
|
|
600
|
+
const target = this.#mode() === "none" ? void 0 : this.#selectableRowOf(path);
|
|
601
|
+
if (!target) return;
|
|
602
|
+
const inSelectionCell = path.some((t) => t instanceof HTMLElement && t.dataset.col === "--argus-selection");
|
|
603
|
+
this.#clickRow(target, event, inSelectionCell || event.ctrlKey || event.metaKey);
|
|
281
604
|
};
|
|
605
|
+
this.#checkboxHandlers = {
|
|
606
|
+
row: (event) => {
|
|
607
|
+
const index = Number(event.currentTarget.dataset.row);
|
|
608
|
+
const id = this.#table.model.rowAt(index)?.id;
|
|
609
|
+
if (id !== void 0) this.#clickRow({
|
|
610
|
+
id,
|
|
611
|
+
index
|
|
612
|
+
}, event, true);
|
|
613
|
+
this.requestUpdate();
|
|
614
|
+
},
|
|
615
|
+
all: () => {
|
|
616
|
+
this.#toggleAll();
|
|
617
|
+
this.requestUpdate();
|
|
618
|
+
},
|
|
619
|
+
press: (event) => {
|
|
620
|
+
event.preventDefault();
|
|
621
|
+
event.currentTarget.parentElement?.focus({ preventScroll: true });
|
|
622
|
+
}
|
|
623
|
+
};
|
|
624
|
+
this.#onMousedown = (event) => {
|
|
625
|
+
if (!event.shiftKey || event.button !== 0 || this.#mode() !== "multi") return;
|
|
626
|
+
const path = event.composedPath();
|
|
627
|
+
if (!this.#selectableRowOf(path)) return;
|
|
628
|
+
event.preventDefault();
|
|
629
|
+
for (const target of path) {
|
|
630
|
+
const cell = gridCell(target);
|
|
631
|
+
if (cell) {
|
|
632
|
+
cell.focus({ preventScroll: true });
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
};
|
|
637
|
+
for (const type of [
|
|
638
|
+
"wheel",
|
|
639
|
+
"touchstart",
|
|
640
|
+
"pointerdown",
|
|
641
|
+
"keydown"
|
|
642
|
+
]) this.addEventListener(type, this.#dropScrollTarget, { passive: true });
|
|
282
643
|
}
|
|
283
|
-
|
|
284
|
-
this.styles = baseStyles;
|
|
285
|
-
}
|
|
286
|
-
/**
|
|
287
|
-
* Column definitions. Property only: functions can't be attributes. While empty,
|
|
288
|
-
* the table reads its `<argus-column>` children instead. `null` or `undefined`
|
|
289
|
-
* means `[]`.
|
|
290
|
-
*/
|
|
291
|
-
get columns() {
|
|
292
|
-
return this.#columnsProp;
|
|
293
|
-
}
|
|
294
|
-
set columns(value) {
|
|
295
|
-
this.#columnsProp = value ?? [];
|
|
296
|
-
}
|
|
297
|
-
/**
|
|
298
|
-
* The rows to show. Replace the array to update; changes inside it aren't
|
|
299
|
-
* detected. `null` or `undefined` means `[]`.
|
|
300
|
-
*/
|
|
301
|
-
get data() {
|
|
302
|
-
return this.#dataProp;
|
|
303
|
-
}
|
|
304
|
-
set data(value) {
|
|
305
|
-
this.#dataProp = value ?? [];
|
|
306
|
-
}
|
|
307
|
-
/** Rows rendered above and below the visible ones. `null` or `undefined` means the default, 6. */
|
|
308
|
-
get overscan() {
|
|
309
|
-
return this.#overscan;
|
|
310
|
-
}
|
|
311
|
-
set overscan(value) {
|
|
312
|
-
this.#overscan = value ?? 6;
|
|
313
|
-
}
|
|
314
|
-
/**
|
|
315
|
-
* The sort order, e.g. `[{ id: 'total', desc: true }]`; `[]` when unsorted. Unset,
|
|
316
|
-
* the table keeps it itself and header clicks change it. Once set, it is
|
|
317
|
-
* controlled: a click only fires `argus-sort-change` with the proposed order,
|
|
318
|
-
* and nothing changes until you set the property. Set `undefined` to hand it back.
|
|
319
|
-
*/
|
|
320
|
-
get sorting() {
|
|
321
|
-
return this.#sorting.value;
|
|
322
|
-
}
|
|
323
|
-
set sorting(value) {
|
|
324
|
-
this.#sorting.set(value);
|
|
325
|
-
}
|
|
326
|
-
/**
|
|
327
|
-
* Resized widths in px by column id, e.g. `{ name: 240 }`. A resized flex column
|
|
328
|
-
* becomes fixed. Uncontrolled until set, like `sorting`: once set, a resize only
|
|
329
|
-
* fires `argus-column-resize` with the proposed widths. `undefined` hands it back.
|
|
330
|
-
*/
|
|
331
|
-
get columnSizing() {
|
|
332
|
-
return this.#sizing.value;
|
|
333
|
-
}
|
|
334
|
-
set columnSizing(value) {
|
|
335
|
-
this.#sizing.set(value);
|
|
336
|
-
}
|
|
337
|
-
/**
|
|
338
|
-
* The column order: column ids as the table shows them, e.g.
|
|
339
|
-
* `['id', 'total', 'customer']`. `[]` (the default) keeps the order of `columns`.
|
|
340
|
-
* Each pin group (start-pinned, scrolling, end-pinned) takes its columns in this
|
|
341
|
-
* order; unknown ids are ignored and columns it doesn't list follow the listed
|
|
342
|
-
* ones of their group. After a move it holds every column id. Uncontrolled
|
|
343
|
-
* until set, like `sorting`: once set, a move only fires `argus-column-reorder`
|
|
344
|
-
* with the proposed order. `undefined` hands it back.
|
|
345
|
-
*/
|
|
346
|
-
get columnOrder() {
|
|
347
|
-
return this.#order.value;
|
|
348
|
-
}
|
|
349
|
-
set columnOrder(value) {
|
|
350
|
-
this.#order.set(value);
|
|
351
|
-
}
|
|
352
|
-
#columnsProp;
|
|
353
|
-
#dataProp;
|
|
354
|
-
#overscan;
|
|
355
|
-
#sorting;
|
|
356
|
-
#sizing;
|
|
357
|
-
#order;
|
|
358
|
-
/** Moves the table applied itself, by column; their events fire once the update is done. */
|
|
359
|
-
#appliedReorders;
|
|
360
|
-
/** The header being dragged to a new position, if any. */
|
|
361
|
-
#drag;
|
|
362
|
-
/** A move from the keyboard, announced once it has rendered: the column and its old position. */
|
|
363
|
-
#announceMove;
|
|
364
|
-
/** Resizes the table applied itself, by column; their events fire once the update is done. */
|
|
365
|
-
#appliedResizes;
|
|
366
|
-
/** Measured widths of `width: 'auto'` columns. */
|
|
367
|
-
#autoWidths;
|
|
368
|
-
/** The auto widths last passed to the model; a change re-measures the pinned columns' offsets. */
|
|
369
|
-
#pushedAutoWidths;
|
|
370
|
-
#autoWidthsChanged;
|
|
371
|
-
/** A sort the table applied itself; its event fires once the update is done. */
|
|
372
|
-
#appliedSort;
|
|
373
|
-
#internals;
|
|
374
|
-
#table;
|
|
375
|
-
#children;
|
|
376
|
-
/** The columns last passed to the model: `columns`, or the `<argus-column>` children. */
|
|
377
|
-
#columns;
|
|
378
|
-
#columnsChanged;
|
|
379
|
-
#warnedColumns;
|
|
380
|
-
/** End of the light-DOM part holding cell content, so later children stay outside it. */
|
|
381
|
-
#contentEnd;
|
|
382
|
-
#data;
|
|
383
|
-
/** Scroll back to the top after the update (a new sort order for a data source). */
|
|
384
|
-
#scrollToTop;
|
|
385
|
-
#virtual;
|
|
386
|
-
#pinScroll;
|
|
387
|
-
#rowPx;
|
|
388
|
-
#probeObserver;
|
|
389
|
-
#window;
|
|
390
|
-
#active;
|
|
391
|
-
/** Move focus to the active cell after the next update (keyboard navigation). */
|
|
392
|
-
#focusPending;
|
|
393
|
-
#lastRange;
|
|
394
|
-
#unwatchMaxHeight;
|
|
644
|
+
#dropScrollTarget;
|
|
395
645
|
connectedCallback() {
|
|
396
646
|
super.connectedCallback();
|
|
397
647
|
if (!this.hasAttribute("role")) this.setAttribute("role", "grid");
|
|
398
648
|
if (this.hasUpdated) this.#observeProbe();
|
|
649
|
+
if (this.#lightPart && !this.#lightConnected) {
|
|
650
|
+
this.#lightConnected = true;
|
|
651
|
+
this.#lightPart.setConnected(true);
|
|
652
|
+
flushContent(this);
|
|
653
|
+
}
|
|
399
654
|
this.#unwatchMaxHeight = watchMaxElementHeight(() => {
|
|
400
655
|
this.requestUpdate();
|
|
401
656
|
});
|
|
@@ -403,6 +658,12 @@ var ArgusTable = class ArgusTable extends LitElement {
|
|
|
403
658
|
disconnectedCallback() {
|
|
404
659
|
super.disconnectedCallback();
|
|
405
660
|
this.#probeObserver?.disconnect();
|
|
661
|
+
this.#rowMeasure.stop();
|
|
662
|
+
if (this.#lightPart && this.#lightConnected) {
|
|
663
|
+
this.#lightConnected = false;
|
|
664
|
+
this.#lightPart.setConnected(false);
|
|
665
|
+
flushContent(this);
|
|
666
|
+
}
|
|
406
667
|
this.#unwatchMaxHeight?.();
|
|
407
668
|
this.#unwatchMaxHeight = void 0;
|
|
408
669
|
}
|
|
@@ -447,33 +708,137 @@ var ArgusTable = class ArgusTable extends LitElement {
|
|
|
447
708
|
* would, so `argus-column-resize` fires for each column that changes, and with a
|
|
448
709
|
* controlled `columnSizing` nothing changes until you set it. Without `ids`, fits
|
|
449
710
|
* every resizable column; columns with `resizable: false` keep their width.
|
|
711
|
+
* With `virtualizeColumns`, columns out of view render for the measurement.
|
|
450
712
|
* Resolves once the table has re-rendered.
|
|
451
713
|
*/
|
|
452
714
|
async autosizeColumns(ids) {
|
|
453
715
|
await this.updateComplete;
|
|
454
716
|
const targets = this.#table.model.columns.filter((c) => c.resizable && (ids === void 0 || ids.includes(c.id))).map((c) => c.id);
|
|
455
|
-
|
|
717
|
+
if (this.virtualizeColumns) {
|
|
718
|
+
this.#measureKeep = targets;
|
|
719
|
+
this.requestUpdate();
|
|
720
|
+
await this.updateComplete;
|
|
721
|
+
}
|
|
722
|
+
const widths = measureColumns(this.renderRoot, targets);
|
|
723
|
+
if (this.#measureKeep.length > 0) {
|
|
724
|
+
this.#measureKeep = [];
|
|
725
|
+
this.requestUpdate();
|
|
726
|
+
}
|
|
727
|
+
this.#requestResizes(widths);
|
|
456
728
|
await this.updateComplete;
|
|
457
729
|
}
|
|
458
730
|
/** Scrolls so that the row at `index` (in the current row order) is at the top. */
|
|
459
731
|
async scrollToRow(index) {
|
|
460
732
|
await this.updateComplete;
|
|
461
733
|
this.#virtual.scrollToIndex(index);
|
|
734
|
+
await this.#settled();
|
|
735
|
+
}
|
|
736
|
+
/**
|
|
737
|
+
* Waits for the update, and with `rowHeight: 'auto'` for the ones measured
|
|
738
|
+
* rows request (a few at most: heights don't depend on positions).
|
|
739
|
+
*/
|
|
740
|
+
async #settled() {
|
|
741
|
+
let done = await this.updateComplete;
|
|
742
|
+
for (let i = 0; i < 8 && !done && this.rowHeight === "auto"; i++) done = await this.updateComplete;
|
|
743
|
+
}
|
|
744
|
+
/**
|
|
745
|
+
* The state users change, as one object of plain JSON for saving a user's
|
|
746
|
+
* layout: `sorting`, `columnSizing`, `columnOrder`, `columnPinning`,
|
|
747
|
+
* `columnVisibility`, `columnFilters`, `selection`, `quickFilter`, and `scroll`
|
|
748
|
+
* (the row at the top and the horizontal offset). Controlled properties are
|
|
749
|
+
* included with the values you set. The object is a copy: changing it changes
|
|
750
|
+
* nothing in the table.
|
|
751
|
+
*/
|
|
752
|
+
getState() {
|
|
753
|
+
const scroller = this.renderRoot.querySelector("[part~=\"table\"]");
|
|
754
|
+
return structuredClone({
|
|
755
|
+
sorting: this.#sorting.value,
|
|
756
|
+
columnSizing: this.#sizing.value,
|
|
757
|
+
columnOrder: this.#order.value,
|
|
758
|
+
columnPinning: this.#pinning.value,
|
|
759
|
+
columnVisibility: this.#visibility.value,
|
|
760
|
+
columnFilters: this.#filters.value,
|
|
761
|
+
selection: this.#selection.value,
|
|
762
|
+
quickFilter: this.#quickFilter,
|
|
763
|
+
scroll: {
|
|
764
|
+
row: this.#virtual.topRow,
|
|
765
|
+
left: Math.abs(scroller?.scrollLeft ?? 0)
|
|
766
|
+
}
|
|
767
|
+
});
|
|
768
|
+
}
|
|
769
|
+
/**
|
|
770
|
+
* Restores state from `getState()`, for example a layout saved in
|
|
771
|
+
* `localStorage`. Only the keys given change; ids of columns that no longer
|
|
772
|
+
* exist are ignored. Properties you control are left alone (set them
|
|
773
|
+
* yourself), and the others stay uncontrolled. No change events fire.
|
|
774
|
+
* `quickFilter` is set like the property: a search box of yours shows it only
|
|
775
|
+
* if you read it back. `scroll` is applied after the rest, so with a
|
|
776
|
+
* `dataSource` the restored rows load where it points. Resolves once the table
|
|
777
|
+
* has re-rendered there.
|
|
778
|
+
*/
|
|
779
|
+
async setState(state) {
|
|
780
|
+
this.#restore("sorting", this.#sorting, state.sorting);
|
|
781
|
+
this.#restore("columnSizing", this.#sizing, state.columnSizing);
|
|
782
|
+
this.#restore("columnOrder", this.#order, state.columnOrder);
|
|
783
|
+
this.#restore("columnPinning", this.#pinning, state.columnPinning);
|
|
784
|
+
this.#restore("columnVisibility", this.#visibility, state.columnVisibility);
|
|
785
|
+
this.#restore("columnFilters", this.#filters, state.columnFilters);
|
|
786
|
+
if (this.#restore("selection", this.#selection, state.selection)) this.#anchor = void 0;
|
|
787
|
+
if (state.quickFilter !== void 0) this.quickFilter = state.quickFilter;
|
|
788
|
+
this.#scrollTarget = void 0;
|
|
462
789
|
await this.updateComplete;
|
|
790
|
+
const { scroll } = state;
|
|
791
|
+
if (!scroll) return;
|
|
792
|
+
this.#virtual.scrollToIndex(scroll.row);
|
|
793
|
+
const count = this.#table.model.rowCount;
|
|
794
|
+
if (this.#data.rows?.totalKnown === false && scroll.row >= count) this.#scrollTarget = {
|
|
795
|
+
row: scroll.row,
|
|
796
|
+
count
|
|
797
|
+
};
|
|
798
|
+
const scroller = this.renderRoot.querySelector("[part~=\"table\"]");
|
|
799
|
+
if (scroller) scroller.scrollLeft = scroll.left * this.#direction();
|
|
800
|
+
await new Promise(requestAnimationFrame);
|
|
801
|
+
await this.#settled();
|
|
802
|
+
}
|
|
803
|
+
/** Scrolls on toward the row `setState()` restored once more rows are counted. */
|
|
804
|
+
#chaseScrollTarget(count) {
|
|
805
|
+
const target = this.#scrollTarget;
|
|
806
|
+
if (!target || target.count === count) return;
|
|
807
|
+
target.count = count;
|
|
808
|
+
this.#virtual.scrollToIndex(target.row);
|
|
809
|
+
if (target.row < count || this.#data.rows?.totalKnown !== false) this.#scrollTarget = void 0;
|
|
810
|
+
}
|
|
811
|
+
/**
|
|
812
|
+
* Applies a restored value to uncontrolled state without an event, as a user
|
|
813
|
+
* change would without the event. Returns whether it changed.
|
|
814
|
+
*/
|
|
815
|
+
#restore(name, state, value) {
|
|
816
|
+
if (value === void 0 || state.controlled) return false;
|
|
817
|
+
if (JSON.stringify(value) === JSON.stringify(state.value)) return false;
|
|
818
|
+
const old = state.value;
|
|
819
|
+
state.request(structuredClone(value));
|
|
820
|
+
this.requestUpdate(name, old);
|
|
821
|
+
return true;
|
|
463
822
|
}
|
|
464
823
|
willUpdate(changed) {
|
|
465
|
-
|
|
824
|
+
if (changed.has("plugins")) this.#plugins.set(this.plugins);
|
|
825
|
+
const columns = this.#plugins.transform(this.#effectiveColumns());
|
|
466
826
|
this.#columnsChanged = columns !== this.#columns;
|
|
467
827
|
this.#columns = columns;
|
|
468
|
-
|
|
828
|
+
const filtersChanged = changed.has("columnFilters") || changed.has("quickFilter");
|
|
829
|
+
if (changed.has("dataSource") || changed.has("sorting") || changed.has("getRowId") || filtersChanged) {
|
|
469
830
|
this.#data.configure({
|
|
470
831
|
source: this.dataSource,
|
|
471
832
|
sorting: this.#sorting.value,
|
|
833
|
+
filters: this.#filters.value,
|
|
834
|
+
quickFilter: this.#quickFilter,
|
|
472
835
|
getRowId: this.getRowId
|
|
473
836
|
});
|
|
474
|
-
if (this.dataSource && this.hasUpdated && changed.has("sorting")) this.#scrollToTop = !changed.has("dataSource");
|
|
837
|
+
if (this.dataSource && this.hasUpdated && (changed.has("sorting") || filtersChanged)) this.#scrollToTop = !changed.has("dataSource");
|
|
838
|
+
this.#scrollTarget = void 0;
|
|
475
839
|
}
|
|
476
|
-
if (
|
|
840
|
+
if (filtersChanged && this.hasUpdated) this.#announceRows = true;
|
|
841
|
+
if (this.#columnsChanged || changed.has("data") || changed.has("getRowId") || changed.has("sorting") || changed.has("columnSizing") || changed.has("columnOrder") || changed.has("columnPinning") || changed.has("columnVisibility") || changed.has("columnFilters") || changed.has("quickFilter") || changed.has("pinnedRows")) {
|
|
477
842
|
if (this.#columnsChanged) this.#autoWidths.sync(columns.filter((c) => c.width === "auto").map((c) => c.id));
|
|
478
843
|
this.#table.update({
|
|
479
844
|
columns,
|
|
@@ -482,15 +847,23 @@ var ArgusTable = class ArgusTable extends LitElement {
|
|
|
482
847
|
sorting: this.#sorting.value,
|
|
483
848
|
columnSizing: this.#sizing.value,
|
|
484
849
|
columnOrder: this.#order.value,
|
|
850
|
+
columnPinning: this.#pinning.value,
|
|
851
|
+
columnVisibility: this.#visibility.value,
|
|
852
|
+
columnFilters: this.#filters.value,
|
|
853
|
+
quickFilter: this.#quickFilter,
|
|
485
854
|
pinnedRows: this.pinnedRows
|
|
486
855
|
});
|
|
487
856
|
}
|
|
488
857
|
const autoWidths = this.#autoWidths.widths;
|
|
489
858
|
this.#autoWidthsChanged = autoWidths !== this.#pushedAutoWidths;
|
|
490
859
|
this.#pushedAutoWidths = autoWidths;
|
|
860
|
+
const selectionColumn = this.selectionCheckboxes && this.#mode() !== "none";
|
|
861
|
+
this.#selectionColumnChanged = selectionColumn !== this.#selectionColumn;
|
|
862
|
+
this.#selectionColumn = selectionColumn;
|
|
491
863
|
this.#table.update({
|
|
492
864
|
remote: this.#data.rows,
|
|
493
|
-
autoWidths
|
|
865
|
+
autoWidths,
|
|
866
|
+
selectionColumn
|
|
494
867
|
});
|
|
495
868
|
const model = this.#table.model;
|
|
496
869
|
this.#reconcileActive(model);
|
|
@@ -499,11 +872,19 @@ var ArgusTable = class ArgusTable extends LitElement {
|
|
|
499
872
|
const layout = layoutOf(model);
|
|
500
873
|
this.#internals.ariaRowCount = unknownTotal ? "-1" : String(layout.total);
|
|
501
874
|
this.#internals.ariaColCount = String(model.columns.length);
|
|
502
|
-
|
|
875
|
+
this.#internals.ariaMultiSelectable = this.#mode() === "multi" ? "true" : null;
|
|
876
|
+
const extra = this.#rowPx + this.#pinnedPx("top", layout) + this.#pinnedPx("bottom", layout);
|
|
877
|
+
const auto = this.rowHeight === "auto";
|
|
878
|
+
if (!auto) this.#rowMeasure.stop();
|
|
879
|
+
else if (changed.size > 0 || this.#columnsChanged) this.#rowMeasure.invalidate();
|
|
503
880
|
this.#virtual.configure({
|
|
504
881
|
count: model.rowCount,
|
|
505
882
|
rowHeight: this.#rowPx,
|
|
506
|
-
cap:
|
|
883
|
+
cap: (logical) => bodyCap(logical, extra),
|
|
884
|
+
auto: auto ? {
|
|
885
|
+
order: model.rowOrder,
|
|
886
|
+
indexOf: (id) => model.indexOf(id)
|
|
887
|
+
} : void 0,
|
|
507
888
|
overscan: Math.max(0, this.overscan),
|
|
508
889
|
rowIdAt: (index) => this.#rowId(model, index),
|
|
509
890
|
keep: active?.section !== "body" || active.rowId === void 0 ? void 0 : {
|
|
@@ -516,6 +897,8 @@ var ArgusTable = class ArgusTable extends LitElement {
|
|
|
516
897
|
const model = this.#table.model;
|
|
517
898
|
const columns = model.columns;
|
|
518
899
|
this.#window = this.#virtual.computeWindow();
|
|
900
|
+
const rendered = this.#columnWindow.compute(columns, this.virtualizeColumns, this.virtualizeColumns ? this.#keptColumns(model) : []);
|
|
901
|
+
this.#rendered = rendered;
|
|
519
902
|
const pinnedWidth = (side) => columns.reduce((sum, c) => c.pin === side ? sum + c.width : sum, 0);
|
|
520
903
|
const pinned = (side) => `${String(pinnedWidth(side))}px`;
|
|
521
904
|
const layout = layoutOf(model);
|
|
@@ -527,6 +910,16 @@ var ArgusTable = class ArgusTable extends LitElement {
|
|
|
527
910
|
"--_pin-top-rows": String(layout.top),
|
|
528
911
|
"--_pin-bottom-rows": String(layout.bottom)
|
|
529
912
|
};
|
|
913
|
+
const auto = this.rowHeight === "auto";
|
|
914
|
+
if (auto) {
|
|
915
|
+
const key = `${style["--_cols"] ?? ""}|${String(this.#rowPx)}`;
|
|
916
|
+
if (key !== this.#rowsKey) {
|
|
917
|
+
this.#rowsKey = key;
|
|
918
|
+
this.#rowMeasure.invalidate();
|
|
919
|
+
}
|
|
920
|
+
style["--_pin-top-h"] = `${String(this.#pinnedPx("top", layout))}px`;
|
|
921
|
+
style["--_pin-bottom-h"] = `${String(this.#pinnedPx("bottom", layout))}px`;
|
|
922
|
+
}
|
|
530
923
|
const active = this.#active;
|
|
531
924
|
const activeIn = (section) => active?.section === section ? {
|
|
532
925
|
index: active.index,
|
|
@@ -536,24 +929,27 @@ var ArgusTable = class ArgusTable extends LitElement {
|
|
|
536
929
|
section,
|
|
537
930
|
index: 0
|
|
538
931
|
});
|
|
539
|
-
if (this.rowHeight
|
|
932
|
+
if (typeof this.rowHeight === "number" && this.rowHeight > 0) style["--_row-height"] = `${String(this.rowHeight)}px`;
|
|
933
|
+
const contentPx = this.#rowPx + this.#pinnedPx("top", layout) + this.#pinnedPx("bottom", layout) + (model.rowCount === 0 ? 0 : this.#window.totalSize);
|
|
540
934
|
return html`<div
|
|
541
935
|
part="table"
|
|
542
936
|
role="presentation"
|
|
543
937
|
style=${styleMap(style)}
|
|
544
938
|
?data-dragging=${this.#drag !== void 0}
|
|
939
|
+
?data-auto-rows=${auto}
|
|
545
940
|
@keydown=${this.#onKeydown}
|
|
546
941
|
@focusin=${this.#onFocusin}
|
|
942
|
+
@mousedown=${this.#onMousedown}
|
|
547
943
|
@click=${this.#onClick}
|
|
548
944
|
>
|
|
549
|
-
${renderHeader(
|
|
550
|
-
${renderPinnedRows("top", layout.top,
|
|
551
|
-
${model.rowCount === 0 ? html`<div part="empty"><slot name="empty">No rows</slot></div>` : renderBody(this.#window.rows,
|
|
552
|
-
${renderPinnedRows("bottom", layout.bottom,
|
|
945
|
+
${renderHeader(rendered, this.#headerHandlers, activeIn("header")?.col, this.#drag, this.#checkAllOf(model))}
|
|
946
|
+
${renderPinnedRows("top", layout.top, rendered, firstIndex("top"), activeIn("top"))}
|
|
947
|
+
${model.rowCount === 0 ? html`<div part="empty"><slot name="empty">No rows</slot></div>` : renderBody(this.#window.rows, rendered, this.#window.totalSize, activeIn("body"), firstIndex("body"), this.#loading(), this.#selectedAt(model), this.#selectionColumn ? this.#checkboxHandlers : void 0, auto)}
|
|
948
|
+
${renderPinnedRows("bottom", layout.bottom, rendered, firstIndex("bottom"), activeIn("bottom"))}
|
|
553
949
|
<div class="probe" aria-hidden="true"></div>
|
|
554
950
|
</div>
|
|
555
|
-
${pinShadow("start", pinnedWidth("start"),
|
|
556
|
-
${pinShadow("end", pinnedWidth("end"),
|
|
951
|
+
${pinShadow("start", pinnedWidth("start"), contentPx)}
|
|
952
|
+
${pinShadow("end", pinnedWidth("end"), contentPx)}`;
|
|
557
953
|
}
|
|
558
954
|
firstUpdated() {
|
|
559
955
|
this.#observeProbe();
|
|
@@ -561,15 +957,20 @@ var ArgusTable = class ArgusTable extends LitElement {
|
|
|
561
957
|
}
|
|
562
958
|
updated(changed) {
|
|
563
959
|
const model = this.#table.model;
|
|
564
|
-
renderLightDom(this, model, this.#window.rows, this.#lightDomEnd());
|
|
960
|
+
const part = renderLightDom(this, model, this.#window.rows, this.#rendered, this.#lightDomEnd());
|
|
961
|
+
if (this.#lightPart && this.#lightPart !== part) this.#lightPart.setConnected(false);
|
|
962
|
+
this.#lightPart = part;
|
|
963
|
+
flushContent(this);
|
|
565
964
|
this.#measureAutoWidths(model);
|
|
965
|
+
const resized = this.rowHeight === "auto" && this.#measureRows();
|
|
566
966
|
if (this.#scrollToTop) {
|
|
567
967
|
this.#scrollToTop = false;
|
|
568
968
|
const scroller = this.renderRoot.querySelector("[part~=\"table\"]");
|
|
569
969
|
if (scroller) scroller.scrollTop = 0;
|
|
570
970
|
}
|
|
571
|
-
|
|
572
|
-
if (this.#
|
|
971
|
+
this.#chaseScrollTarget(model.rowCount);
|
|
972
|
+
if (this.#columnsChanged || changed.has("columnSizing") || changed.has("columnOrder") || changed.has("columnPinning") || changed.has("columnVisibility") || this.#autoWidthsChanged || this.#selectionColumnChanged) this.#pinScroll.measure();
|
|
973
|
+
if (this.#focusPending && !resized) {
|
|
573
974
|
this.#focusPending = false;
|
|
574
975
|
this.#focusActive();
|
|
575
976
|
}
|
|
@@ -588,11 +989,35 @@ var ArgusTable = class ArgusTable extends LitElement {
|
|
|
588
989
|
this.#appliedReorders.clear();
|
|
589
990
|
for (const detail of details) fire(this, COLUMN_REORDER, detail);
|
|
590
991
|
}
|
|
992
|
+
if (this.#appliedPins.size > 0) {
|
|
993
|
+
const details = [...this.#appliedPins.values()];
|
|
994
|
+
this.#appliedPins.clear();
|
|
995
|
+
for (const detail of details) fire(this, COLUMN_PIN, detail);
|
|
996
|
+
}
|
|
997
|
+
if (this.#appliedHides.size > 0) {
|
|
998
|
+
const details = [...this.#appliedHides.values()];
|
|
999
|
+
this.#appliedHides.clear();
|
|
1000
|
+
for (const detail of details) fire(this, COLUMN_HIDE, detail);
|
|
1001
|
+
}
|
|
1002
|
+
if (this.#appliedFilters.size > 0) {
|
|
1003
|
+
const details = [...this.#appliedFilters.values()];
|
|
1004
|
+
this.#appliedFilters.clear();
|
|
1005
|
+
for (const detail of details) fire(this, COLUMN_FILTER, detail);
|
|
1006
|
+
}
|
|
1007
|
+
if (this.#announceRows) {
|
|
1008
|
+
this.#announceRows = false;
|
|
1009
|
+
this.#announceRowCount(model);
|
|
1010
|
+
}
|
|
591
1011
|
if (this.#appliedSort) {
|
|
592
1012
|
const sorting = this.#appliedSort;
|
|
593
1013
|
this.#appliedSort = void 0;
|
|
594
1014
|
fire(this, SORT_CHANGE, { sorting });
|
|
595
1015
|
}
|
|
1016
|
+
if (this.#appliedSelection) {
|
|
1017
|
+
const selection = this.#appliedSelection;
|
|
1018
|
+
this.#appliedSelection = void 0;
|
|
1019
|
+
fire(this, ROW_SELECT, { selection });
|
|
1020
|
+
}
|
|
596
1021
|
const { range } = this.#window;
|
|
597
1022
|
const key = range ? `${String(range.start)}:${String(range.end)}` : "";
|
|
598
1023
|
if (range && key !== this.#lastRange) {
|
|
@@ -609,6 +1034,22 @@ var ArgusTable = class ArgusTable extends LitElement {
|
|
|
609
1034
|
for (const detail of this.#data.takeErrors()) fire(this, LOAD_ERROR, detail);
|
|
610
1035
|
}
|
|
611
1036
|
/**
|
|
1037
|
+
* Columns to render wherever they are, with `virtualizeColumns`: the active
|
|
1038
|
+
* cell's (its tab stop and focus survive scrolling sideways, and keyboard
|
|
1039
|
+
* moves land on a rendered cell), one being dragged, auto widths still to
|
|
1040
|
+
* measure, the targets of `autosizeColumns()`, and with a `dataSource` the
|
|
1041
|
+
* first data column, which holds the Retry buttons.
|
|
1042
|
+
*/
|
|
1043
|
+
#keptColumns(model) {
|
|
1044
|
+
return [
|
|
1045
|
+
this.#active?.col,
|
|
1046
|
+
this.#drag?.columnId,
|
|
1047
|
+
this.dataSource ? model.columns.find((c) => c.kind === "data")?.id : void 0,
|
|
1048
|
+
...this.#autoWidths.pending(model.columns),
|
|
1049
|
+
...this.#measureKeep
|
|
1050
|
+
];
|
|
1051
|
+
}
|
|
1052
|
+
/**
|
|
612
1053
|
* Measures the `width: 'auto'` columns once loaded body rows render, or right
|
|
613
1054
|
* away when there are no rows (the header and pinned rows only). While a data
|
|
614
1055
|
* source's first rows load, they keep the default width. A changed width
|
|
@@ -671,14 +1112,251 @@ var ArgusTable = class ArgusTable extends LitElement {
|
|
|
671
1112
|
* cell (an input, a button) are left to that content.
|
|
672
1113
|
*/
|
|
673
1114
|
#onKeydown;
|
|
1115
|
+
/**
|
|
1116
|
+
* The grid position ArrowUp/ArrowDown (one row) or PageUp/PageDown (a page of
|
|
1117
|
+
* the body) moves to from `pos`. PageDown from above the body lands a page
|
|
1118
|
+
* into it and stops at its last row; PageUp stays in the body.
|
|
1119
|
+
*/
|
|
1120
|
+
#verticalStep(key, pos, layout, scroller) {
|
|
1121
|
+
const bodyFirst = layout.position({
|
|
1122
|
+
section: "body",
|
|
1123
|
+
index: 0
|
|
1124
|
+
});
|
|
1125
|
+
const bodyLast = bodyFirst + layout.body - 1;
|
|
1126
|
+
const page = this.#pageRows(key, pos, layout, scroller);
|
|
1127
|
+
switch (key) {
|
|
1128
|
+
case "PageDown": return layout.body > 0 && pos <= bodyLast ? Math.min(Math.max(pos, bodyFirst - 1) + page, bodyLast) : pos;
|
|
1129
|
+
case "PageUp": return layout.body > 0 && pos > bodyFirst ? Math.max(Math.min(pos, bodyLast + 1) - page, bodyFirst) : pos;
|
|
1130
|
+
case "ArrowDown": return Math.min(pos + 1, layout.total - 1);
|
|
1131
|
+
case "ArrowUp": return Math.max(pos - 1, 0);
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
/** How many rows PageUp/PageDown move from grid position `pos`. */
|
|
1135
|
+
#pageRows(key, pos, layout, scroller) {
|
|
1136
|
+
if (this.rowHeight !== "auto") {
|
|
1137
|
+
const fit = Math.floor(scroller.clientHeight / this.#rowPx + .01);
|
|
1138
|
+
return Math.max(1, fit - 1 - layout.top - layout.bottom);
|
|
1139
|
+
}
|
|
1140
|
+
const view = scroller.clientHeight - this.#rowPx - this.#pinnedPx("top", layout) - this.#pinnedPx("bottom", layout);
|
|
1141
|
+
const bodyFirst = layout.position({
|
|
1142
|
+
section: "body",
|
|
1143
|
+
index: 0
|
|
1144
|
+
});
|
|
1145
|
+
const from = Math.min(Math.max(pos - bodyFirst, 0), Math.max(layout.body - 1, 0));
|
|
1146
|
+
return this.#virtual.pageRows(from, key === "PageDown", view);
|
|
1147
|
+
}
|
|
1148
|
+
/**
|
|
1149
|
+
* Selection keys, with a `selectionMode`: Space (or Shift+Space) toggles the
|
|
1150
|
+
* focused body row; in `multi` mode, Shift+ArrowUp/ArrowDown and
|
|
1151
|
+
* Shift+PageUp/PageDown move focus through the body and select from the
|
|
1152
|
+
* anchor to it, and Ctrl/Cmd+A selects every row. `true` if it took the key.
|
|
1153
|
+
*/
|
|
1154
|
+
#selectionKey(event, cell, col, inHeader, scroller) {
|
|
1155
|
+
const mode = this.#mode();
|
|
1156
|
+
if (mode === "none" || event.altKey) return false;
|
|
1157
|
+
const { key } = event;
|
|
1158
|
+
const command = event.ctrlKey || event.metaKey;
|
|
1159
|
+
const model = this.#table.model;
|
|
1160
|
+
if (command && !event.shiftKey && key.toLowerCase() === "a") {
|
|
1161
|
+
if (mode !== "multi") return false;
|
|
1162
|
+
event.preventDefault();
|
|
1163
|
+
this.#selectAll();
|
|
1164
|
+
return true;
|
|
1165
|
+
}
|
|
1166
|
+
if (command) return false;
|
|
1167
|
+
if (inHeader) {
|
|
1168
|
+
if (key !== " " || col !== "--argus-selection" || mode !== "multi") return false;
|
|
1169
|
+
event.preventDefault();
|
|
1170
|
+
this.#toggleAll();
|
|
1171
|
+
return true;
|
|
1172
|
+
}
|
|
1173
|
+
const layout = layoutOf(model);
|
|
1174
|
+
const from = gridRowOf(cell.parentElement, layout);
|
|
1175
|
+
if (from?.section !== "body") return false;
|
|
1176
|
+
if (key === " ") {
|
|
1177
|
+
event.preventDefault();
|
|
1178
|
+
const id = model.rowAt(from.index)?.id;
|
|
1179
|
+
if (id !== void 0) this.#requestSelection(toggleRow(this.#selection.value, mode, id));
|
|
1180
|
+
return true;
|
|
1181
|
+
}
|
|
1182
|
+
if (!event.shiftKey || mode !== "multi" || key !== "ArrowDown" && key !== "ArrowUp" && key !== "PageDown" && key !== "PageUp") return false;
|
|
1183
|
+
event.preventDefault();
|
|
1184
|
+
const bodyFirst = layout.position({
|
|
1185
|
+
section: "body",
|
|
1186
|
+
index: 0
|
|
1187
|
+
});
|
|
1188
|
+
const pos = this.#verticalStep(key, layout.position(from), layout, scroller);
|
|
1189
|
+
const to = layout.at(Math.min(Math.max(pos, bodyFirst), bodyFirst + layout.body - 1));
|
|
1190
|
+
if (to?.section !== "body" || to.index === from.index) return true;
|
|
1191
|
+
if (key.startsWith("Page")) this.#virtual.scrollBy(this.#virtual.startOf(to.index) - this.#virtual.startOf(from.index));
|
|
1192
|
+
const fromId = model.rowAt(from.index)?.id;
|
|
1193
|
+
const anchor = this.#anchorAt(model) ?? (fromId === void 0 ? void 0 : {
|
|
1194
|
+
id: fromId,
|
|
1195
|
+
index: from.index
|
|
1196
|
+
});
|
|
1197
|
+
if (anchor) {
|
|
1198
|
+
const idAt = (index) => model.rowAt(index)?.id;
|
|
1199
|
+
const selection = selectRange([], anchor.index, to.index, idAt, false);
|
|
1200
|
+
this.#requestSelection({
|
|
1201
|
+
selection,
|
|
1202
|
+
anchor: anchor.id
|
|
1203
|
+
});
|
|
1204
|
+
}
|
|
1205
|
+
this.#moveTo(to, col, model);
|
|
1206
|
+
return true;
|
|
1207
|
+
}
|
|
674
1208
|
/** Focus that lands in a cell (a click, or content inside it) makes that cell active. */
|
|
675
1209
|
#onFocusin;
|
|
676
|
-
/**
|
|
1210
|
+
/**
|
|
1211
|
+
* `argus-cell-click`, then `argus-row-click`, for clicks in the body; then,
|
|
1212
|
+
* with a `selectionMode`, the click selects its row.
|
|
1213
|
+
*/
|
|
677
1214
|
#onClick;
|
|
1215
|
+
/** Selects for a click on a row, through `clickSelection`. */
|
|
1216
|
+
#clickRow(target, event, toggle) {
|
|
1217
|
+
const model = this.#table.model;
|
|
1218
|
+
this.#requestSelection(clickSelection(this.#selection.value, this.#mode(), target, this.#anchorAt(model), {
|
|
1219
|
+
toggle,
|
|
1220
|
+
range: event.shiftKey
|
|
1221
|
+
}, (index) => model.rowAt(index)?.id));
|
|
1222
|
+
}
|
|
1223
|
+
/**
|
|
1224
|
+
* The checkboxes' handlers. The table owns their state: a click asks for a new
|
|
1225
|
+
* selection, and the render that always follows sets every box back to the
|
|
1226
|
+
* selection (`live()`), so a change a controlled host turns down unticks it
|
|
1227
|
+
* again. Not `preventDefault()`: for a real click the browser runs that render
|
|
1228
|
+
* inside the dispatch, then undoes the toggle after it, over the render. A
|
|
1229
|
+
* press leaves focus on the cell, so the grid's keys keep working.
|
|
1230
|
+
*/
|
|
1231
|
+
#checkboxHandlers;
|
|
1232
|
+
/** Selects every selectable row, keeping the others (with a `dataSource`: the loaded ones). */
|
|
1233
|
+
#selectAll() {
|
|
1234
|
+
const ids = this.#table.model.selectableIds();
|
|
1235
|
+
this.#requestSelection({
|
|
1236
|
+
selection: selectAll(this.#selection.value, ids),
|
|
1237
|
+
anchor: this.#anchor
|
|
1238
|
+
});
|
|
1239
|
+
}
|
|
1240
|
+
/** The select-all checkbox: clears the selection when every row is selected, else selects all. */
|
|
1241
|
+
#toggleAll() {
|
|
1242
|
+
if (this.#checkAllState(this.#table.model) === "all") this.#requestSelection({
|
|
1243
|
+
selection: [],
|
|
1244
|
+
anchor: this.#anchor
|
|
1245
|
+
});
|
|
1246
|
+
else this.#selectAll();
|
|
1247
|
+
}
|
|
1248
|
+
/** The select-all checkbox, in the checkbox column of a `multi` table. */
|
|
1249
|
+
#checkAllOf(model) {
|
|
1250
|
+
if (!this.#selectionColumn || this.#mode() !== "multi") return void 0;
|
|
1251
|
+
return {
|
|
1252
|
+
state: this.#checkAllState(model),
|
|
1253
|
+
handlers: this.#checkboxHandlers
|
|
1254
|
+
};
|
|
1255
|
+
}
|
|
1256
|
+
/**
|
|
1257
|
+
* Whether all, some or none of the selectable rows are selected. With `data`
|
|
1258
|
+
* it's worked out once per `selection`, `data` and `getRowId`, not on scroll;
|
|
1259
|
+
* with a `dataSource` every time, over the loaded rows, which eviction keeps few.
|
|
1260
|
+
*/
|
|
1261
|
+
#checkAllState(model) {
|
|
1262
|
+
const count = () => {
|
|
1263
|
+
const ids = model.selectableIds();
|
|
1264
|
+
const selected = this.#selectedSet();
|
|
1265
|
+
const n = ids.reduce((sum, id) => selected.has(id) ? sum + 1 : sum, 0);
|
|
1266
|
+
if (n === 0) return "none";
|
|
1267
|
+
return n === ids.length ? "all" : "some";
|
|
1268
|
+
};
|
|
1269
|
+
if (this.#data.rows) return count();
|
|
1270
|
+
const key = [
|
|
1271
|
+
this.#selection.value,
|
|
1272
|
+
this.data,
|
|
1273
|
+
this.getRowId,
|
|
1274
|
+
this.#columns,
|
|
1275
|
+
this.#filters.value,
|
|
1276
|
+
this.#quickFilter,
|
|
1277
|
+
this.#visibility.value
|
|
1278
|
+
];
|
|
1279
|
+
const cached = this.#checkAll;
|
|
1280
|
+
if (cached?.key.length !== key.length || key.some((k, i) => k !== cached.key[i])) this.#checkAll = {
|
|
1281
|
+
key,
|
|
1282
|
+
state: count()
|
|
1283
|
+
};
|
|
1284
|
+
return this.#checkAll?.state ?? "none";
|
|
1285
|
+
}
|
|
1286
|
+
/**
|
|
1287
|
+
* A Shift+press on a row in `multi` mode selects a range of rows, not the
|
|
1288
|
+
* page's text from the last click: no text selection, and the cell still
|
|
1289
|
+
* takes focus.
|
|
1290
|
+
*/
|
|
1291
|
+
#onMousedown;
|
|
1292
|
+
/** `selectionMode`, with anything unknown read as `none`. */
|
|
1293
|
+
#mode() {
|
|
1294
|
+
const mode = this.selectionMode;
|
|
1295
|
+
return mode === "single" || mode === "multi" ? mode : "none";
|
|
1296
|
+
}
|
|
1297
|
+
/** Whether each rendered body row is selected; `undefined` without a `selectionMode`. */
|
|
1298
|
+
#selectedAt(model) {
|
|
1299
|
+
if (this.#mode() === "none") return void 0;
|
|
1300
|
+
const ids = this.#selectedSet();
|
|
1301
|
+
return (index) => {
|
|
1302
|
+
const id = model.rowAt(index)?.id;
|
|
1303
|
+
return id === void 0 ? void 0 : ids.has(id);
|
|
1304
|
+
};
|
|
1305
|
+
}
|
|
1306
|
+
/** `selection` as a set, rebuilt only when `selection` changes. */
|
|
1307
|
+
#selectedSet() {
|
|
1308
|
+
const selection = this.#selection.value;
|
|
1309
|
+
if (this.#selectedIds?.of !== selection) this.#selectedIds = {
|
|
1310
|
+
of: selection,
|
|
1311
|
+
ids: new Set(selection)
|
|
1312
|
+
};
|
|
1313
|
+
return this.#selectedIds.ids;
|
|
1314
|
+
}
|
|
1315
|
+
/**
|
|
1316
|
+
* The loaded body row a pointer event is on, as a selection target. None for
|
|
1317
|
+
* the header, pinned rows, rows still loading, and presses on interactive
|
|
1318
|
+
* content in a cell (a button, a link, an input).
|
|
1319
|
+
*/
|
|
1320
|
+
#selectableRowOf(path) {
|
|
1321
|
+
const row = path.find((t) => t instanceof HTMLElement && t.getAttribute("role") === "row");
|
|
1322
|
+
if (!row || fromInteractive(path, row)) return void 0;
|
|
1323
|
+
const model = this.#table.model;
|
|
1324
|
+
const at = gridRowOf(row, layoutOf(model));
|
|
1325
|
+
if (at?.section !== "body") return void 0;
|
|
1326
|
+
const id = model.rowAt(at.index)?.id;
|
|
1327
|
+
return id === void 0 ? void 0 : {
|
|
1328
|
+
id,
|
|
1329
|
+
index: at.index
|
|
1330
|
+
};
|
|
1331
|
+
}
|
|
1332
|
+
/** The anchor row and where it is now; `undefined` if it isn't in the rows (or not loaded). */
|
|
1333
|
+
#anchorAt(model) {
|
|
1334
|
+
const id = this.#anchor;
|
|
1335
|
+
const index = id === void 0 ? void 0 : model.indexOf(id);
|
|
1336
|
+
return id === void 0 || index === void 0 ? void 0 : {
|
|
1337
|
+
id,
|
|
1338
|
+
index
|
|
1339
|
+
};
|
|
1340
|
+
}
|
|
1341
|
+
/**
|
|
1342
|
+
* Changes the selection through the controlled/uncontrolled rules, and moves
|
|
1343
|
+
* the anchor. A step that leaves the same rows selected fires nothing.
|
|
1344
|
+
*/
|
|
1345
|
+
#requestSelection(step) {
|
|
1346
|
+
if (!step) return;
|
|
1347
|
+
this.#anchor = step.anchor;
|
|
1348
|
+
const old = this.#selection.value;
|
|
1349
|
+
if (sameSelection(step.selection, old)) return;
|
|
1350
|
+
const { value, applied } = this.#selection.request(step.selection);
|
|
1351
|
+
if (applied) {
|
|
1352
|
+
this.#appliedSelection = value;
|
|
1353
|
+
this.requestUpdate("selection", old);
|
|
1354
|
+
} else fire(this, ROW_SELECT, { selection: value });
|
|
1355
|
+
}
|
|
678
1356
|
/**
|
|
679
1357
|
* Fires `argus-cell-click` (when a cell is on the path), then `argus-row-click`,
|
|
680
1358
|
* for the body or pinned row on an event path. Clicks and Enter on a cell use
|
|
681
|
-
* it. Rows that haven't loaded and the
|
|
1359
|
+
* it. Rows that haven't loaded, the header and the checkbox column fire nothing.
|
|
682
1360
|
*/
|
|
683
1361
|
#activate(path) {
|
|
684
1362
|
let columnId;
|
|
@@ -686,6 +1364,7 @@ var ArgusTable = class ArgusTable extends LitElement {
|
|
|
686
1364
|
if (!(target instanceof HTMLElement)) continue;
|
|
687
1365
|
const role = target.getAttribute("role");
|
|
688
1366
|
if (role === "gridcell") columnId = target.dataset.col;
|
|
1367
|
+
if (columnId === "--argus-selection") return;
|
|
689
1368
|
if (role !== "row") continue;
|
|
690
1369
|
const model = this.#table.model;
|
|
691
1370
|
const at = gridRowOf(target, layoutOf(model));
|
|
@@ -750,10 +1429,10 @@ var ArgusTable = class ArgusTable extends LitElement {
|
|
|
750
1429
|
const cell = this.renderRoot.querySelector(`[role='row'][aria-rowindex="${String(index)}"] > [data-col="${col}"]`);
|
|
751
1430
|
if (!cell) return;
|
|
752
1431
|
cell.focus({ preventScroll: true });
|
|
753
|
-
if (active.section === "body" && this.#virtual.scaled) {
|
|
1432
|
+
if (active.section === "body" && (this.#virtual.scaled || this.rowHeight === "auto")) {
|
|
754
1433
|
const layout = layoutOf(this.#table.model);
|
|
755
|
-
const before =
|
|
756
|
-
if (this.#virtual.reveal(active.index, before,
|
|
1434
|
+
const before = this.#rowPx + this.#pinnedPx("top", layout);
|
|
1435
|
+
if (this.#virtual.reveal(active.index, before, this.#pinnedPx("bottom", layout))) {
|
|
757
1436
|
this.#focusPending = true;
|
|
758
1437
|
return;
|
|
759
1438
|
}
|
|
@@ -783,9 +1462,9 @@ var ArgusTable = class ArgusTable extends LitElement {
|
|
|
783
1462
|
rowId: void 0,
|
|
784
1463
|
col: first.id
|
|
785
1464
|
};
|
|
786
|
-
const col = columns.some((c) => c.id === prev.col) ? prev.col : first.id;
|
|
1465
|
+
const col = columns.some((c) => c.id === prev.col) ? prev.col : this.#columnInPlaceOf(model, prev.col, first.id);
|
|
787
1466
|
let { section, index, rowId } = prev;
|
|
788
|
-
let moved =
|
|
1467
|
+
let moved = col !== prev.col && this.#active !== void 0;
|
|
789
1468
|
if (section === "body" && rowId !== void 0) {
|
|
790
1469
|
const found = model.indexOf(rowId);
|
|
791
1470
|
const unloaded = index < model.rowCount && model.rowAt(index) === void 0;
|
|
@@ -816,6 +1495,18 @@ var ArgusTable = class ArgusTable extends LitElement {
|
|
|
816
1495
|
};
|
|
817
1496
|
}
|
|
818
1497
|
/**
|
|
1498
|
+
* The shown column that takes a hidden column's place: the next one in the
|
|
1499
|
+
* column order, else the one before it. `fallback` for a column that is gone
|
|
1500
|
+
* altogether.
|
|
1501
|
+
*/
|
|
1502
|
+
#columnInPlaceOf(model, id, fallback) {
|
|
1503
|
+
const all = model.allColumns;
|
|
1504
|
+
const at = all.findIndex((c) => c.id === id);
|
|
1505
|
+
if (at < 0) return fallback;
|
|
1506
|
+
const shown = (c) => !c.hidden;
|
|
1507
|
+
return (all.slice(at + 1).find(shown) ?? all.slice(0, at).findLast(shown))?.id ?? fallback;
|
|
1508
|
+
}
|
|
1509
|
+
/**
|
|
819
1510
|
* The width a resize starts from: the latest requested width (key presses can
|
|
820
1511
|
* come faster than renders), else the model's fixed width, else the rendered
|
|
821
1512
|
* width of a flex column.
|
|
@@ -863,7 +1554,79 @@ var ArgusTable = class ArgusTable extends LitElement {
|
|
|
863
1554
|
} else for (const detail of details) fire(this, COLUMN_RESIZE, detail);
|
|
864
1555
|
}
|
|
865
1556
|
/**
|
|
866
|
-
*
|
|
1557
|
+
* Pins or unpins a column through the controlled/uncontrolled rules. Starts
|
|
1558
|
+
* from the latest requested pins, so requests can come faster than renders.
|
|
1559
|
+
*/
|
|
1560
|
+
#requestPin(columnId, pin) {
|
|
1561
|
+
const old = this.#pinning.value;
|
|
1562
|
+
const next = this.#table.model.nextPinning(columnId, pin, old);
|
|
1563
|
+
if (!next) return;
|
|
1564
|
+
const { value, applied } = this.#pinning.request(next);
|
|
1565
|
+
if (applied) {
|
|
1566
|
+
for (const detail of this.#appliedPins.values()) detail.pinning = value;
|
|
1567
|
+
this.#appliedPins.set(columnId, {
|
|
1568
|
+
columnId,
|
|
1569
|
+
pin,
|
|
1570
|
+
pinning: value
|
|
1571
|
+
});
|
|
1572
|
+
this.requestUpdate("columnPinning", old);
|
|
1573
|
+
} else fire(this, COLUMN_PIN, {
|
|
1574
|
+
columnId,
|
|
1575
|
+
pin,
|
|
1576
|
+
pinning: value
|
|
1577
|
+
});
|
|
1578
|
+
}
|
|
1579
|
+
/** Hides or shows a column through the controlled/uncontrolled rules. */
|
|
1580
|
+
#requestHide(columnId, hidden) {
|
|
1581
|
+
const old = this.#visibility.value;
|
|
1582
|
+
const next = this.#table.model.nextVisibility(columnId, hidden, old);
|
|
1583
|
+
if (!next) return;
|
|
1584
|
+
const { value, applied } = this.#visibility.request(next);
|
|
1585
|
+
if (applied) {
|
|
1586
|
+
for (const detail of this.#appliedHides.values()) detail.visibility = value;
|
|
1587
|
+
this.#appliedHides.set(columnId, {
|
|
1588
|
+
columnId,
|
|
1589
|
+
hidden,
|
|
1590
|
+
visibility: value
|
|
1591
|
+
});
|
|
1592
|
+
this.requestUpdate("columnVisibility", old);
|
|
1593
|
+
} else fire(this, COLUMN_HIDE, {
|
|
1594
|
+
columnId,
|
|
1595
|
+
hidden,
|
|
1596
|
+
visibility: value
|
|
1597
|
+
});
|
|
1598
|
+
}
|
|
1599
|
+
/** Sets or clears a column's filter through the controlled/uncontrolled rules. */
|
|
1600
|
+
#requestFilter(columnId, filter) {
|
|
1601
|
+
const old = this.#filters.value;
|
|
1602
|
+
const next = this.#table.model.nextFilters(columnId, filter, old);
|
|
1603
|
+
if (!next) return;
|
|
1604
|
+
const { value, applied } = this.#filters.request(next);
|
|
1605
|
+
if (applied) {
|
|
1606
|
+
for (const detail of this.#appliedFilters.values()) detail.filters = value;
|
|
1607
|
+
this.#appliedFilters.set(columnId, {
|
|
1608
|
+
columnId,
|
|
1609
|
+
filter,
|
|
1610
|
+
filters: value
|
|
1611
|
+
});
|
|
1612
|
+
this.requestUpdate("columnFilters", old);
|
|
1613
|
+
} else fire(this, COLUMN_FILTER, {
|
|
1614
|
+
columnId,
|
|
1615
|
+
filter,
|
|
1616
|
+
filters: value
|
|
1617
|
+
});
|
|
1618
|
+
}
|
|
1619
|
+
/**
|
|
1620
|
+
* Says how many rows the filters leave ("42 rows"). With a `dataSource` the count
|
|
1621
|
+
* isn't known until the first page answers, so nothing is said.
|
|
1622
|
+
*/
|
|
1623
|
+
#announceRowCount(model) {
|
|
1624
|
+
if (this.#data.rows) return;
|
|
1625
|
+
const n = model.rowCount;
|
|
1626
|
+
announce(this.ownerDocument, n === 1 ? "1 row" : `${String(n)} rows`);
|
|
1627
|
+
}
|
|
1628
|
+
/**
|
|
1629
|
+
* Moves a column to position `to` among the shown data columns (within its pin group),
|
|
867
1630
|
* through the controlled/uncontrolled rules. Starts from the latest requested
|
|
868
1631
|
* order, so moves can come faster than renders. `false` if it can't move there.
|
|
869
1632
|
*/
|
|
@@ -888,7 +1651,8 @@ var ArgusTable = class ArgusTable extends LitElement {
|
|
|
888
1651
|
/** Alt+Shift+Arrow on a header: one step toward the start (-1) or the end (1). */
|
|
889
1652
|
#moveByKey(columnId, step) {
|
|
890
1653
|
const model = this.#table.model;
|
|
891
|
-
const
|
|
1654
|
+
const shown = new Set(model.columns.map((c) => c.id));
|
|
1655
|
+
const from = model.orderOf(this.#order.value).filter((id) => shown.has(id)).indexOf(columnId);
|
|
892
1656
|
const view = model.columns.find((c) => c.id === columnId);
|
|
893
1657
|
if (from < 0 || !view) return;
|
|
894
1658
|
if (!this.#requestReorder(columnId, from + step)) return;
|
|
@@ -946,10 +1710,13 @@ var ArgusTable = class ArgusTable extends LitElement {
|
|
|
946
1710
|
}
|
|
947
1711
|
});
|
|
948
1712
|
}
|
|
949
|
-
/**
|
|
1713
|
+
/**
|
|
1714
|
+
* The rendered headers of one pin group, with their positions in the whole
|
|
1715
|
+
* order (which leaves out the checkbox column).
|
|
1716
|
+
*/
|
|
950
1717
|
#dropSlots(pin) {
|
|
951
1718
|
const slots = [];
|
|
952
|
-
this.#table.model.columns.forEach((c, index) => {
|
|
1719
|
+
this.#table.model.columns.filter((c) => c.kind === "data").forEach((c, index) => {
|
|
953
1720
|
if (c.pin !== pin) return;
|
|
954
1721
|
const cell = this.renderRoot.querySelector(`[role='columnheader'][data-col="${CSS.escape(c.id)}"]`);
|
|
955
1722
|
if (cell) slots.push({
|
|
@@ -974,7 +1741,24 @@ var ArgusTable = class ArgusTable extends LitElement {
|
|
|
974
1741
|
/** asc → desc → unsorted, through the controlled/uncontrolled rules. */
|
|
975
1742
|
#requestSort(columnId) {
|
|
976
1743
|
const next = this.#table.model.nextSorting(columnId);
|
|
977
|
-
if (
|
|
1744
|
+
if (next) this.#applySorting(next);
|
|
1745
|
+
}
|
|
1746
|
+
/**
|
|
1747
|
+
* A plugin's sort: by a column in a direction, or (`null`) cleared if it is by
|
|
1748
|
+
* that column. Only sortable columns, and only a change.
|
|
1749
|
+
*/
|
|
1750
|
+
#setSort(columnId, direction) {
|
|
1751
|
+
const view = this.#table.model.columns.find((c) => c.id === columnId);
|
|
1752
|
+
if (view?.kind !== "data" || !view.sortable) return;
|
|
1753
|
+
if (direction === (view.sorted === false ? null : view.sorted)) return;
|
|
1754
|
+
if (direction === null) this.#applySorting([]);
|
|
1755
|
+
else this.#applySorting([{
|
|
1756
|
+
id: columnId,
|
|
1757
|
+
desc: direction === "desc"
|
|
1758
|
+
}]);
|
|
1759
|
+
}
|
|
1760
|
+
/** A new sort order, through the controlled/uncontrolled rules. */
|
|
1761
|
+
#applySorting(next) {
|
|
978
1762
|
const old = this.#sorting.value;
|
|
979
1763
|
const { value, applied } = this.#sorting.request(next);
|
|
980
1764
|
if (applied) {
|
|
@@ -982,6 +1766,49 @@ var ArgusTable = class ArgusTable extends LitElement {
|
|
|
982
1766
|
this.requestUpdate("sorting", old);
|
|
983
1767
|
} else fire(this, SORT_CHANGE, { sorting: value });
|
|
984
1768
|
}
|
|
1769
|
+
/**
|
|
1770
|
+
* A data column as plugins see it. Header functions call this on every
|
|
1771
|
+
* render, so the views are indexed by id once per new column list.
|
|
1772
|
+
*/
|
|
1773
|
+
#columnInfo(id) {
|
|
1774
|
+
const columns = this.#table.model.allColumns;
|
|
1775
|
+
if (this.#columnsById?.of !== columns) this.#columnsById = {
|
|
1776
|
+
of: columns,
|
|
1777
|
+
byId: new Map(columns.map((c) => [c.id, c]))
|
|
1778
|
+
};
|
|
1779
|
+
const view = this.#columnsById.byId.get(id);
|
|
1780
|
+
if (view?.kind !== "data") return void 0;
|
|
1781
|
+
return {
|
|
1782
|
+
id,
|
|
1783
|
+
sortable: view.sortable,
|
|
1784
|
+
resizable: view.resizable,
|
|
1785
|
+
pinnable: view.pinnable,
|
|
1786
|
+
hideable: view.hideable,
|
|
1787
|
+
pin: view.pin,
|
|
1788
|
+
hidden: view.hidden,
|
|
1789
|
+
width: view.width,
|
|
1790
|
+
sorted: view.sorted === false ? void 0 : view.sorted,
|
|
1791
|
+
filter: view.filter,
|
|
1792
|
+
filterValue: this.#filters.value[id]
|
|
1793
|
+
};
|
|
1794
|
+
}
|
|
1795
|
+
/** The header or cell an event happened in, for plugins. */
|
|
1796
|
+
#cellTarget(event) {
|
|
1797
|
+
for (const target of event.composedPath()) {
|
|
1798
|
+
const cell = gridCell(target);
|
|
1799
|
+
const columnId = cell?.dataset.col;
|
|
1800
|
+
if (!cell || columnId === void 0) continue;
|
|
1801
|
+
const model = this.#table.model;
|
|
1802
|
+
const row = cell.getAttribute("role") === "columnheader" ? HEADER : gridRowOf(cell.parentElement, layoutOf(model));
|
|
1803
|
+
if (!row) return void 0;
|
|
1804
|
+
return {
|
|
1805
|
+
section: row.section,
|
|
1806
|
+
columnId,
|
|
1807
|
+
rowIndex: row.index,
|
|
1808
|
+
rowId: this.#gridRowId(model, row)
|
|
1809
|
+
};
|
|
1810
|
+
}
|
|
1811
|
+
}
|
|
985
1812
|
#observeProbe() {
|
|
986
1813
|
const probe = this.renderRoot.querySelector(".probe");
|
|
987
1814
|
if (!probe) return;
|
|
@@ -990,6 +1817,22 @@ var ArgusTable = class ArgusTable extends LitElement {
|
|
|
990
1817
|
});
|
|
991
1818
|
this.#probeObserver.observe(probe);
|
|
992
1819
|
}
|
|
1820
|
+
/** The height of the rows pinned to one side: one row each, or measured with `rowHeight: 'auto'`. */
|
|
1821
|
+
#pinnedPx(side, layout) {
|
|
1822
|
+
const count = side === "top" ? layout.top : layout.bottom;
|
|
1823
|
+
if (this.rowHeight !== "auto" || count === 0) return count * this.#rowPx;
|
|
1824
|
+
return Math.max(this.#rowMeasure.pinned[side], count * this.#rowPx);
|
|
1825
|
+
}
|
|
1826
|
+
/**
|
|
1827
|
+
* `rowHeight: 'auto'`: reads the heights of the rendered rows, after their
|
|
1828
|
+
* content has rendered. Returns whether any changed (an update is requested).
|
|
1829
|
+
*/
|
|
1830
|
+
#measureRows() {
|
|
1831
|
+
const { rows, pinnedChanged } = this.#rowMeasure.read(this.renderRoot);
|
|
1832
|
+
const resized = this.#virtual.measureRows(rows);
|
|
1833
|
+
if (pinnedChanged) this.requestUpdate();
|
|
1834
|
+
return resized || pinnedChanged;
|
|
1835
|
+
}
|
|
993
1836
|
/** Reads the resolved row height, so themes and density change the virtualizer too. */
|
|
994
1837
|
#measureRowHeight() {
|
|
995
1838
|
const px = this.renderRoot.querySelector(".probe")?.getBoundingClientRect().height ?? 0;
|
|
@@ -1006,13 +1849,29 @@ __decorate([property({ attribute: false })], ArgusTable.prototype, "pinnedRows",
|
|
|
1006
1849
|
__decorate([property({ attribute: false })], ArgusTable.prototype, "getRowId", void 0);
|
|
1007
1850
|
__decorate([property({
|
|
1008
1851
|
attribute: "row-height",
|
|
1009
|
-
|
|
1852
|
+
converter: rowHeightConverter
|
|
1010
1853
|
})], ArgusTable.prototype, "rowHeight", void 0);
|
|
1011
1854
|
__decorate([property({ reflect: true })], ArgusTable.prototype, "density", void 0);
|
|
1012
1855
|
__decorate([property({ type: Number })], ArgusTable.prototype, "overscan", null);
|
|
1856
|
+
__decorate([property({
|
|
1857
|
+
attribute: "virtualize-columns",
|
|
1858
|
+
type: Boolean
|
|
1859
|
+
})], ArgusTable.prototype, "virtualizeColumns", void 0);
|
|
1013
1860
|
__decorate([property({ attribute: false })], ArgusTable.prototype, "sorting", null);
|
|
1014
1861
|
__decorate([property({ attribute: false })], ArgusTable.prototype, "columnSizing", null);
|
|
1015
1862
|
__decorate([property({ attribute: false })], ArgusTable.prototype, "columnOrder", null);
|
|
1863
|
+
__decorate([property({ attribute: false })], ArgusTable.prototype, "columnPinning", null);
|
|
1864
|
+
__decorate([property({ attribute: false })], ArgusTable.prototype, "columnVisibility", null);
|
|
1865
|
+
__decorate([property({ attribute: false })], ArgusTable.prototype, "columnFilters", null);
|
|
1866
|
+
__decorate([property({ attribute: "quick-filter" })], ArgusTable.prototype, "quickFilter", null);
|
|
1867
|
+
__decorate([property({ attribute: "selection-mode" })], ArgusTable.prototype, "selectionMode", void 0);
|
|
1868
|
+
__decorate([property({
|
|
1869
|
+
attribute: "selection-checkboxes",
|
|
1870
|
+
type: Boolean
|
|
1871
|
+
})], ArgusTable.prototype, "selectionCheckboxes", void 0);
|
|
1872
|
+
__decorate([property({ attribute: false })], ArgusTable.prototype, "selection", null);
|
|
1873
|
+
__decorate([property({ attribute: false })], ArgusTable.prototype, "contentRenderer", void 0);
|
|
1874
|
+
__decorate([property({ attribute: false })], ArgusTable.prototype, "plugins", null);
|
|
1016
1875
|
ArgusTable = __decorate([customElement("argus-table")], ArgusTable);
|
|
1017
1876
|
export { ArgusTable };
|
|
1018
1877
|
|