@ixfx/components 0.15.5 → 0.16.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.
- package/bundle/index.d.ts +986 -49
- package/bundle/index.js +765 -729
- package/dist/ac-token.js +1 -1
- package/dist/crumb-navigation-BbqfmkYs.d.ts +147 -0
- package/dist/crumb-navigation-YnFVFqrx.js +1290 -0
- package/dist/crumbs.d.ts +2 -115
- package/dist/crumbs.js +1 -1179
- package/dist/{data-grid-C1sZX6n9.js → data-grid-Cqxuwgbb.js} +615 -210
- package/dist/{data-grid-D7qzmMMp.d.ts → data-grid-Csh5EJW9.d.ts} +55 -4
- package/dist/data-grid.d.ts +3 -3
- package/dist/data-grid.js +1 -1
- package/dist/{detail-list-q8bs67BM.js → detail-list-Bo5ze3SN.js} +234 -9
- package/dist/{detail-list-CKIKlszv.d.ts → detail-list-DSZ1DDor.d.ts} +48 -4
- package/dist/detail-list.d.ts +4 -4
- package/dist/detail-list.js +1 -1
- package/dist/{flat-list-DXMyDiSH.d.ts → flat-list-CT9kX3TQ.d.ts} +27 -4
- package/dist/flat-list.d.ts +2 -2
- package/dist/flat-list.js +123 -4
- package/dist/{grid-list-y6dWJYu3.js → grid-list-D0dJNVEF.js} +278 -76
- package/dist/{grid-list-0e7snW6t.d.ts → grid-list-DDLrWRxm.d.ts} +128 -22
- package/dist/grid-list.d.ts +3 -3
- package/dist/grid-list.js +1 -1
- package/dist/{incr-search-JIhwB49y.js → incr-search-CgbL-OlS.js} +140 -10
- package/dist/incr-search.d.ts +1 -1
- package/dist/incr-search.js +1 -1
- package/dist/index-9ttcdwlL.d.ts +71 -0
- package/dist/index-Bnm-kKGC.d.ts +475 -0
- package/dist/{index-KN2MW_xD.d.ts → index-CUErGhCg.d.ts} +31 -4
- package/dist/{index-DEVyVIn8.d.ts → index-D-lZNlHa.d.ts} +52 -6
- package/dist/index.d.ts +341 -21
- package/dist/index.js +2286 -884
- package/dist/{list-native-drag-controller-O2Nruqpi.d.ts → list-native-drag-controller-D_UrXipS.d.ts} +1 -1
- package/dist/{list-selection-controller-BeE70lbB.js → list-selection-controller-5L0Nj9tR.js} +87 -2
- package/dist/miller-DUqiUAWQ.js +2230 -0
- package/dist/miller.d.ts +4 -437
- package/dist/miller.js +1 -1815
- package/dist/narrowed-text.js +1 -1
- package/dist/pending-BGidHppL.js +91 -0
- package/dist/scroll-hover-guard-Cjw6179Q.js +35 -0
- package/dist/{split-layout-JK3xWVX2.d.ts → split-layout-CophkNeU.d.ts} +41 -3
- package/dist/{split-layout-Deoxy47d.js → split-layout-DTY4PJRp.js} +251 -19
- package/dist/split-layout.d.ts +1 -1
- package/dist/split-layout.js +1 -1
- package/dist/sunburst-tree.d.ts +2 -2
- package/dist/sunburst-tree.js +1 -1
- package/dist/{sunbursttree-list-yXwS-uEI.js → sunbursttree-list-B_M0YTEA.js} +299 -80
- package/dist/{sunbursttree-list--Z0uyh8O.d.ts → sunbursttree-list-DePuFYlU.d.ts} +40 -3
- package/dist/tree-CftHwtEb.js +146 -0
- package/dist/{tree-D8VxvR9u.js → tree-DAlgvaHB.js} +276 -48
- package/dist/{tree-component-C6UHO-w9.d.ts → tree-component-DgfkxmVY.d.ts} +19 -8
- package/dist/tree.d.ts +3 -3
- package/dist/tree.js +1 -1
- package/dist/types-LU1u0CB8.d.ts +87 -0
- package/dist/vertical-list-DyKPKGxK.js +1993 -0
- package/dist/{vertical-list-D8MksoTp.d.ts → vertical-list-SAx56U-l.d.ts} +31 -3
- package/dist/vertical-list.d.ts +3 -3
- package/dist/vertical-list.js +1 -1789
- package/dist/view-state-controller-BdyXWUUM.js +305 -0
- package/docs-user/README.md +2 -0
- package/docs-user/crumbs.md +27 -0
- package/docs-user/data-grid.md +79 -5
- package/docs-user/detail-list.md +69 -0
- package/docs-user/dock.md +63 -12
- package/docs-user/flat-list.md +27 -0
- package/docs-user/grid-list.md +69 -13
- package/docs-user/hyperbolic-tree.md +31 -4
- package/docs-user/icicle-tree.md +36 -0
- package/docs-user/index.json +12 -2
- package/docs-user/llms.txt +2 -0
- package/docs-user/miller.md +52 -21
- package/docs-user/multicol-list.md +50 -8
- package/docs-user/split-layout.md +45 -17
- package/docs-user/sunburst-tree.md +32 -2
- package/docs-user/tree.md +45 -6
- package/docs-user/treemap.md +32 -0
- package/docs-user/twosplit-layout.md +22 -0
- package/docs-user/user-catalog.md +1 -1
- package/docs-user/vertical-list.md +55 -7
- package/llms.txt +2 -0
- package/package.json +2 -2
|
@@ -3,10 +3,12 @@ import { n as tickledStyles, t as tickledItemStyles } from "./tickled-styles-BrA
|
|
|
3
3
|
import { t as themeFallbacks } from "./fallbacks-DQ_5OsmR.js";
|
|
4
4
|
import { n as safeCustomElement, t as __decorate } from "./decorate-VRK8rslU.js";
|
|
5
5
|
import "./icon-1gpwtPx8.js";
|
|
6
|
-
import { i as ListSearchController, n as listSearchStyles, o as registerListSearchCommands, r as LIST_SEARCH_MODES } from "./incr-search-
|
|
6
|
+
import { i as ListSearchController, n as listSearchStyles, o as registerListSearchCommands, r as LIST_SEARCH_MODES } from "./incr-search-CgbL-OlS.js";
|
|
7
7
|
import { t as handleStyles } from "./handle-DAXs7kbJ.js";
|
|
8
8
|
import { a as listItemFadeKeyframes, t as ListNativeDragController } from "./list-native-drag-controller-CcXoS0YX.js";
|
|
9
|
+
import { a as capKeys, l as keysToArray, n as EMPTY_KEYS, o as flagsFromRecord, r as MAX_PENDING_KEYS } from "./pending-BGidHppL.js";
|
|
9
10
|
import "./interaction-E-uMGHkJ.js";
|
|
11
|
+
import { n as anchorFromRow, o as scrollToOffset, t as ViewStateController } from "./view-state-controller-BdyXWUUM.js";
|
|
10
12
|
import { t as scrollFadeStyles } from "./scroll-fade-Xx28D7mH.js";
|
|
11
13
|
import { n as appearanceColor, t as appearanceAbsent } from "./item-appearance-BjcMyauw.js";
|
|
12
14
|
import { t as createDragGhost } from "./drag-ghost-DtSDvjri.js";
|
|
@@ -16,6 +18,191 @@ import { LitElement, css, html, nothing } from "lit";
|
|
|
16
18
|
import { property, query, state } from "lit/decorators.js";
|
|
17
19
|
import { createCommandRegistry } from "@clinth/ui-commands";
|
|
18
20
|
import { repeat } from "lit/directives/repeat.js";
|
|
21
|
+
//#region src/data-grid/data-grid-view-state.ts
|
|
22
|
+
const EMPTY_COLUMNS = /* @__PURE__ */ new Map();
|
|
23
|
+
const EMPTY_ORDER = /* @__PURE__ */ new Map();
|
|
24
|
+
/**
|
|
25
|
+
* Selected keys whose row is gone go pending (deselected silently); pending
|
|
26
|
+
* keys whose row now exists are selected unless `canRestore` is false (e.g.
|
|
27
|
+
* `selectionMode="none"`), in which case they stay pending. With `single`, at
|
|
28
|
+
* most one key stays selected (a restored key wins) and nothing more stays
|
|
29
|
+
* pending.
|
|
30
|
+
*/
|
|
31
|
+
function reconcileSelectedKeys(selected, pending, has, single, canRestore = true) {
|
|
32
|
+
if (selected.size === 0 && pending.size === 0) return {
|
|
33
|
+
selected,
|
|
34
|
+
pending,
|
|
35
|
+
restored: false,
|
|
36
|
+
changed: false
|
|
37
|
+
};
|
|
38
|
+
let next;
|
|
39
|
+
let nextPending;
|
|
40
|
+
for (const key of selected) {
|
|
41
|
+
if (has(key)) continue;
|
|
42
|
+
next ??= new Set(selected);
|
|
43
|
+
next.delete(key);
|
|
44
|
+
nextPending ??= new Set(pending);
|
|
45
|
+
nextPending.add(key);
|
|
46
|
+
}
|
|
47
|
+
const present = canRestore ? [...pending].filter(has) : [];
|
|
48
|
+
const resolved = resolvePendingKeys(next ?? selected, nextPending ?? pending, present, single);
|
|
49
|
+
return {
|
|
50
|
+
...resolved,
|
|
51
|
+
changed: resolved.changed || next !== void 0
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Select the `present` pending keys (already known to exist). Used on its own
|
|
56
|
+
* for streamed appends, where only the new slice can match — so the cost is
|
|
57
|
+
* proportional to the slice, not the row count.
|
|
58
|
+
*/
|
|
59
|
+
function resolvePendingKeys(selected, pending, present, single) {
|
|
60
|
+
if (present.length === 0) return {
|
|
61
|
+
selected,
|
|
62
|
+
pending: capKeys(pending),
|
|
63
|
+
restored: false,
|
|
64
|
+
changed: false
|
|
65
|
+
};
|
|
66
|
+
const next = new Set(selected);
|
|
67
|
+
const nextPending = new Set(pending);
|
|
68
|
+
for (const key of present) {
|
|
69
|
+
next.add(key);
|
|
70
|
+
nextPending.delete(key);
|
|
71
|
+
}
|
|
72
|
+
if (single) return {
|
|
73
|
+
selected: /* @__PURE__ */ new Set([present.at(-1)]),
|
|
74
|
+
pending: EMPTY_KEYS,
|
|
75
|
+
restored: true,
|
|
76
|
+
changed: true
|
|
77
|
+
};
|
|
78
|
+
return {
|
|
79
|
+
selected: next,
|
|
80
|
+
pending: capKeys(nextPending),
|
|
81
|
+
restored: true,
|
|
82
|
+
changed: true
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/** Pending keys that appear in `sliceKeys` — iterates the slice, not the pending set. */
|
|
86
|
+
function matchSlice(pending, sliceKeys) {
|
|
87
|
+
if (pending.size === 0) return [];
|
|
88
|
+
return sliceKeys.filter((k) => pending.has(k));
|
|
89
|
+
}
|
|
90
|
+
/** Collapsed group keys as an `expanded` record (`false` = collapsed). */
|
|
91
|
+
function collapsedToRecord(collapsed, include = () => true) {
|
|
92
|
+
const out = {};
|
|
93
|
+
for (const key of collapsed) if (include(key)) out[key] = false;
|
|
94
|
+
return out;
|
|
95
|
+
}
|
|
96
|
+
/** `expanded` record → collapsed group keys. Groups default open, so only `false` matters. */
|
|
97
|
+
function collapsedFromRecord(record) {
|
|
98
|
+
const keys = [...flagsFromRecord(record)].filter(([, open]) => !open).map(([k]) => k);
|
|
99
|
+
return new Set(capKeys(new Set(keys)));
|
|
100
|
+
}
|
|
101
|
+
/** Parse a saved `columns` record into pending layout and a saved order. Invalid values are dropped. */
|
|
102
|
+
function columnStateFromRecord(record) {
|
|
103
|
+
const pending = /* @__PURE__ */ new Map();
|
|
104
|
+
const order = /* @__PURE__ */ new Map();
|
|
105
|
+
for (const [key, s] of Object.entries(record)) {
|
|
106
|
+
if (s === null || typeof s !== `object`) continue;
|
|
107
|
+
const width = typeof s.width === `number` && Number.isFinite(s.width) && s.width > 0 ? s.width : void 0;
|
|
108
|
+
const visible = typeof s.visible === `boolean` ? s.visible : void 0;
|
|
109
|
+
if (width !== void 0 || visible !== void 0) pending.set(key, {
|
|
110
|
+
...width === void 0 ? {} : { width },
|
|
111
|
+
...visible === void 0 ? {} : { visible }
|
|
112
|
+
});
|
|
113
|
+
if (typeof s.order === `number` && Number.isFinite(s.order)) order.set(key, s.order);
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
pending,
|
|
117
|
+
order
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* - columns in `prev` but not in `columns` → their width / visibility go pending
|
|
122
|
+
* - pending layout for columns now defined → applied (on a copy of the column)
|
|
123
|
+
* - a saved `order` reorders the columns it names within the slots they
|
|
124
|
+
* occupy, leaving other columns where they are; it is dropped once every
|
|
125
|
+
* column it names is defined (so the host can reorder freely afterwards)
|
|
126
|
+
*
|
|
127
|
+
* Returns the input array when nothing changed, so `columns` identity (a sort
|
|
128
|
+
* input) only changes when it must.
|
|
129
|
+
*/
|
|
130
|
+
function reconcileColumns(prev, columns, pending, order) {
|
|
131
|
+
const keys = new Set(columns.map((c) => c.key));
|
|
132
|
+
let nextPending;
|
|
133
|
+
for (const col of prev) {
|
|
134
|
+
if (keys.has(col.key) || pending.has(col.key)) continue;
|
|
135
|
+
nextPending ??= new Map(pending);
|
|
136
|
+
nextPending.set(col.key, layoutOf(col));
|
|
137
|
+
}
|
|
138
|
+
let next;
|
|
139
|
+
columns.forEach((col, i) => {
|
|
140
|
+
const saved = (nextPending ?? pending).get(col.key);
|
|
141
|
+
if (!saved) return;
|
|
142
|
+
nextPending ??= new Map(pending);
|
|
143
|
+
nextPending.delete(col.key);
|
|
144
|
+
if ((saved.width === void 0 || saved.width === col.width) && (saved.visible === void 0 || saved.visible === (col.visible !== false))) return;
|
|
145
|
+
next ??= [...columns];
|
|
146
|
+
next[i] = {
|
|
147
|
+
...col,
|
|
148
|
+
...saved
|
|
149
|
+
};
|
|
150
|
+
});
|
|
151
|
+
let nextOrder = order;
|
|
152
|
+
if (order.size > 0) {
|
|
153
|
+
const working = next ?? columns;
|
|
154
|
+
const slots = working.flatMap((c, i) => order.has(c.key) ? [i] : []);
|
|
155
|
+
const ranked = slots.map((i) => working[i]).sort((a, b) => order.get(a.key) - order.get(b.key));
|
|
156
|
+
if (ranked.some((c, j) => c !== working[slots[j]])) {
|
|
157
|
+
next ??= [...columns];
|
|
158
|
+
slots.forEach((slot, j) => {
|
|
159
|
+
next[slot] = ranked[j];
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
if ([...order.keys()].every((k) => keys.has(k))) nextOrder = EMPTY_ORDER;
|
|
163
|
+
}
|
|
164
|
+
return {
|
|
165
|
+
columns: next ?? columns,
|
|
166
|
+
pending: nextPending ? capMap(nextPending) : pending,
|
|
167
|
+
order: nextOrder,
|
|
168
|
+
changed: next !== void 0 || nextPending !== void 0 || nextOrder !== order
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
/** Defined columns (and, with `pending`, saved layout for undefined ones) as a JSON record. */
|
|
172
|
+
function captureColumns(columns, pending, order, includePending) {
|
|
173
|
+
const out = {};
|
|
174
|
+
if (includePending) {
|
|
175
|
+
for (const [key, layout] of pending) out[key] = withOrder(layout, order.get(key));
|
|
176
|
+
for (const [key, rank] of order) if (!(key in out) && !columns.some((c) => c.key === key)) out[key] = { order: rank };
|
|
177
|
+
}
|
|
178
|
+
columns.forEach((col, i) => {
|
|
179
|
+
out[col.key] = withOrder(layoutOf(col), order.size > 0 ? order.get(col.key) : i);
|
|
180
|
+
});
|
|
181
|
+
return out;
|
|
182
|
+
}
|
|
183
|
+
/** Whether `value` is a usable sort state (`null` = unsorted). */
|
|
184
|
+
function isSortState(value) {
|
|
185
|
+
if (value === null) return true;
|
|
186
|
+
if (typeof value !== `object`) return false;
|
|
187
|
+
const s = value;
|
|
188
|
+
return typeof s.key === `string` && (s.direction === `asc` || s.direction === `desc`);
|
|
189
|
+
}
|
|
190
|
+
function layoutOf(col) {
|
|
191
|
+
return {
|
|
192
|
+
...col.width === void 0 ? {} : { width: col.width },
|
|
193
|
+
visible: col.visible !== false
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
function withOrder(layout, order) {
|
|
197
|
+
return order === void 0 ? { ...layout } : {
|
|
198
|
+
...layout,
|
|
199
|
+
order
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
function capMap(map, max = MAX_PENDING_KEYS) {
|
|
203
|
+
return map.size <= max ? map : new Map([...map].slice(map.size - max));
|
|
204
|
+
}
|
|
205
|
+
//#endregion
|
|
19
206
|
//#region src/data-grid/shift-wheel.ts
|
|
20
207
|
/**
|
|
21
208
|
* Shift + vertical mouse wheel → horizontal scroll.
|
|
@@ -66,18 +253,28 @@ function attachShiftWheel(getScroller) {
|
|
|
66
253
|
}
|
|
67
254
|
//#endregion
|
|
68
255
|
//#region src/data-grid/data-grid.ts
|
|
256
|
+
/** `CSS.highlights` key for incremental-search matches, styled in `listSearchStyles`. */
|
|
257
|
+
const SEARCH_HIGHLIGHT_KEY = `data-grid-search`;
|
|
69
258
|
const EMPTY_CELL_LOOK = {
|
|
70
259
|
textColor: void 0,
|
|
71
260
|
icon: void 0,
|
|
72
261
|
iconColor: void 0,
|
|
73
262
|
badge: void 0
|
|
74
263
|
};
|
|
264
|
+
/**
|
|
265
|
+
* Default `getRowKey`: `row.key`, then `row.id`, else the row's sorted index.
|
|
266
|
+
* Index keys aren't stable, so view state never records them.
|
|
267
|
+
*/
|
|
268
|
+
const defaultGetRowKey = (r, i) => r.key ?? r.id ?? String(i);
|
|
75
269
|
/** Shallow key-array equality, treating `undefined` (never dispatched) as different. */
|
|
76
270
|
function sameKeys(a, b) {
|
|
77
271
|
if (a === void 0 || a.length !== b.length) return false;
|
|
78
272
|
for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
|
|
79
273
|
return true;
|
|
80
274
|
}
|
|
275
|
+
function sameSet(a, b) {
|
|
276
|
+
return a.size === b.size && [...a].every((k) => b.has(k));
|
|
277
|
+
}
|
|
81
278
|
/** Element-wise reference equality, treating `undefined` (never computed) as different. */
|
|
82
279
|
function sameRefs(a, b) {
|
|
83
280
|
return a !== void 0 && a.length === b.length && a.every((v, i) => v === b[i]);
|
|
@@ -91,7 +288,7 @@ let DataGridElement = class DataGridElement extends LitElement {
|
|
|
91
288
|
this.interactionMode = `standard`;
|
|
92
289
|
this.sortState = null;
|
|
93
290
|
this.rowHeight = 28;
|
|
94
|
-
this.getRowKey =
|
|
291
|
+
this.getRowKey = defaultGetRowKey;
|
|
95
292
|
this.editable = false;
|
|
96
293
|
this.editability = `user`;
|
|
97
294
|
this.cellOverflow = `ellipsis`;
|
|
@@ -103,6 +300,7 @@ let DataGridElement = class DataGridElement extends LitElement {
|
|
|
103
300
|
this.textColorColumn = ``;
|
|
104
301
|
this.iconColumn = ``;
|
|
105
302
|
this.badgeColumn = ``;
|
|
303
|
+
this.searchColumn = ``;
|
|
106
304
|
this.itemsDraggable = false;
|
|
107
305
|
this.columnButtonIcon = `ellipsis`;
|
|
108
306
|
this._columnFormatters = /* @__PURE__ */ new Map();
|
|
@@ -185,6 +383,15 @@ let DataGridElement = class DataGridElement extends LitElement {
|
|
|
185
383
|
this.#viewportChangeRaf = 0;
|
|
186
384
|
this.#resortRaf = 0;
|
|
187
385
|
this._drag = null;
|
|
386
|
+
this.#viewState = new ViewStateController(this, {
|
|
387
|
+
kind: `data-grid`,
|
|
388
|
+
capture: () => this.getViewState()
|
|
389
|
+
});
|
|
390
|
+
this.#pendingSelected = EMPTY_KEYS;
|
|
391
|
+
this.#pendingColumns = EMPTY_COLUMNS;
|
|
392
|
+
this.#columnOrder = EMPTY_ORDER;
|
|
393
|
+
this.#reconciledColumns = [];
|
|
394
|
+
this.#groupKeys = /* @__PURE__ */ new Set();
|
|
188
395
|
this._onFocusOut = (e) => {
|
|
189
396
|
if (this._suppressBlurCommit) return;
|
|
190
397
|
if (this.editability === `immediate`) return;
|
|
@@ -204,180 +411,18 @@ let DataGridElement = class DataGridElement extends LitElement {
|
|
|
204
411
|
this._scrollLeft = el.scrollLeft;
|
|
205
412
|
const after = this.#windowRows(2);
|
|
206
413
|
if (before.start !== after.start || before.end !== after.end) this.requestUpdate();
|
|
414
|
+
if (!this.#viewState.pendingScroll) this.#viewState.notify();
|
|
207
415
|
cancelAnimationFrame(this.#viewportChangeRaf);
|
|
208
416
|
this.#viewportChangeRaf = 0;
|
|
209
417
|
this.#emitViewportChangeIfChanged();
|
|
210
418
|
});
|
|
211
419
|
};
|
|
212
420
|
this._onKeyDown = (e) => {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
const g = this._getGroupKey(this._getRowForKey(this._anchorKey) ?? { key: this._anchorKey });
|
|
219
|
-
this._selectedKeys = new Set(this._flat.filter((f) => f.type === `row` && this._getGroupKey(f.row) === g).map((f) => f.key));
|
|
220
|
-
} else this._selectedKeys = new Set(this._flat.filter((f) => f.type === `row`).map((f) => f.key));
|
|
221
|
-
this.dispatchEvent(new CustomEvent(`datagrid-select`, {
|
|
222
|
-
detail: {
|
|
223
|
-
selectedKeys: new Set(this._selectedKeys),
|
|
224
|
-
previous: prev
|
|
225
|
-
},
|
|
226
|
-
bubbles: true,
|
|
227
|
-
composed: true
|
|
228
|
-
}));
|
|
229
|
-
}
|
|
230
|
-
return;
|
|
231
|
-
}
|
|
232
|
-
if (this._editing && (e.key === `ArrowDown` || e.key === `ArrowUp`)) {
|
|
233
|
-
if ((this.shadowRoot?.activeElement)?.tagName === `SELECT`) return;
|
|
234
|
-
e.preventDefault();
|
|
235
|
-
const cur = this._editing;
|
|
236
|
-
const dir = e.key === `ArrowDown` ? 1 : -1;
|
|
237
|
-
const colKey = cur.colKey;
|
|
238
|
-
this._suppressBlurCommit = true;
|
|
239
|
-
this._commitEdit();
|
|
240
|
-
const rowKeys = this._flat.filter((f) => f.type === `row`).map((f) => f.key);
|
|
241
|
-
const idx = rowKeys.indexOf(cur.rowKey);
|
|
242
|
-
let nextIdx = idx + dir;
|
|
243
|
-
while (nextIdx >= 0 && nextIdx < rowKeys.length) {
|
|
244
|
-
const nk = rowKeys[nextIdx];
|
|
245
|
-
const row = this.#rowEntry(nk)?.row;
|
|
246
|
-
const col = this._visibleColumns.find((c) => c.key === colKey);
|
|
247
|
-
if (row && col && this._isEditable(row, col)) {
|
|
248
|
-
this._startEdit(nk, colKey);
|
|
249
|
-
this._tickled.handleKeyboardNavigation(nextIdx > idx ? `next` : `previous`);
|
|
250
|
-
this._scrollRowIntoView(nk);
|
|
251
|
-
break;
|
|
252
|
-
}
|
|
253
|
-
nextIdx += dir;
|
|
254
|
-
}
|
|
255
|
-
if (nextIdx < 0 || nextIdx >= rowKeys.length) {
|
|
256
|
-
this._tickled.handleKeyboardNavigation(dir > 0 ? `next` : `previous`);
|
|
257
|
-
if (this._tickled.tickledId) this._scrollRowIntoView(this._tickled.tickledId);
|
|
258
|
-
}
|
|
259
|
-
requestAnimationFrame(() => {
|
|
260
|
-
this._suppressBlurCommit = false;
|
|
261
|
-
});
|
|
262
|
-
return;
|
|
263
|
-
}
|
|
264
|
-
if (e.key === `ArrowDown` || e.key === `ArrowUp` || e.key === `Home` || e.key === `End`) {
|
|
265
|
-
e.preventDefault();
|
|
266
|
-
this._tickled.setInputMode(`keyboard`);
|
|
267
|
-
}
|
|
268
|
-
switch (e.key) {
|
|
269
|
-
case `ArrowDown`:
|
|
270
|
-
this._tickled.handleKeyboardNavigation(`next`);
|
|
271
|
-
if (this._tickled.tickledId) this._scrollRowIntoView(this._tickled.tickledId);
|
|
272
|
-
if (this.interactionMode === `implicit` && this._tickled.tickledId) this._replaceSelection(this._tickled.tickledId);
|
|
273
|
-
if (e.shiftKey && (this.interactionMode === `standard` || this.interactionMode === `vscode`) && this._tickled.tickledId) this._rangeSelectTo(this._tickled.tickledId);
|
|
274
|
-
break;
|
|
275
|
-
case `ArrowUp`:
|
|
276
|
-
this._tickled.handleKeyboardNavigation(`previous`);
|
|
277
|
-
if (this._tickled.tickledId) this._scrollRowIntoView(this._tickled.tickledId);
|
|
278
|
-
if (this.interactionMode === `implicit` && this._tickled.tickledId) this._replaceSelection(this._tickled.tickledId);
|
|
279
|
-
if (e.shiftKey && (this.interactionMode === `standard` || this.interactionMode === `vscode`) && this._tickled.tickledId) this._rangeSelectTo(this._tickled.tickledId);
|
|
280
|
-
break;
|
|
281
|
-
case `Home`:
|
|
282
|
-
this._tickled.handleKeyboardNavigation(`first`);
|
|
283
|
-
if (this._tickled.tickledId) this._scrollRowIntoView(this._tickled.tickledId);
|
|
284
|
-
break;
|
|
285
|
-
case `End`:
|
|
286
|
-
this._tickled.handleKeyboardNavigation(`last`);
|
|
287
|
-
if (this._tickled.tickledId) this._scrollRowIntoView(this._tickled.tickledId);
|
|
288
|
-
break;
|
|
289
|
-
case `Enter`: {
|
|
290
|
-
const key = this._tickled.tickledId;
|
|
291
|
-
if (!key) break;
|
|
292
|
-
e.preventDefault();
|
|
293
|
-
if (!this._editing && this.editability === `user`) {
|
|
294
|
-
const entry = this.#rowEntry(key);
|
|
295
|
-
if (entry) {
|
|
296
|
-
const row = entry.row;
|
|
297
|
-
if (this._lastClickedCell?.rowKey === key) {
|
|
298
|
-
const col = this._visibleColumns.find((c) => c.key === this._lastClickedCell.colKey);
|
|
299
|
-
if (col && this._isEditable(row, col)) {
|
|
300
|
-
this._startEdit(key, col.key);
|
|
301
|
-
break;
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
const firstEditable = this._visibleColumns.find((c) => this._isEditable(row, c));
|
|
305
|
-
if (firstEditable) {
|
|
306
|
-
this._startEdit(key, firstEditable.key);
|
|
307
|
-
break;
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
if (!this._editing) {
|
|
312
|
-
const entry = this.#rowEntry(key);
|
|
313
|
-
const idx = entry?.idx ?? -1;
|
|
314
|
-
if (this._selectedKeys.has(key)) {
|
|
315
|
-
this._activateRow(key, entry?.row, idx);
|
|
316
|
-
break;
|
|
317
|
-
}
|
|
318
|
-
if (this.interactionMode !== `manual` && this.interactionMode !== `checked`) this._replaceSelection(key);
|
|
319
|
-
this.dispatchEvent(new CustomEvent(`datagrid-cell-click`, {
|
|
320
|
-
detail: {
|
|
321
|
-
rowKey: key,
|
|
322
|
-
colKey: ``,
|
|
323
|
-
rowIndex: idx,
|
|
324
|
-
colIndex: -1
|
|
325
|
-
},
|
|
326
|
-
bubbles: true,
|
|
327
|
-
composed: true
|
|
328
|
-
}));
|
|
329
|
-
}
|
|
330
|
-
break;
|
|
331
|
-
}
|
|
332
|
-
case ` `: {
|
|
333
|
-
if (this._editing) {
|
|
334
|
-
if ((this.columns.find((c) => c.key === this._editing.colKey)?.dataType ?? `text`) === `boolean`) {
|
|
335
|
-
e.preventDefault();
|
|
336
|
-
const isImmediate = this.editability === `immediate`;
|
|
337
|
-
const rowKey = this._editing.rowKey;
|
|
338
|
-
const colKey = this._editing.colKey;
|
|
339
|
-
const active = this.shadowRoot?.activeElement;
|
|
340
|
-
let next;
|
|
341
|
-
if (active?.type === `checkbox`) {
|
|
342
|
-
next = !active.checked;
|
|
343
|
-
active.checked = next;
|
|
344
|
-
} else {
|
|
345
|
-
const row = this.#rowEntry(rowKey)?.row;
|
|
346
|
-
next = !(this._editValue !== `` ? Boolean(this._editValue) : Boolean(row?.[colKey]));
|
|
347
|
-
}
|
|
348
|
-
if (isImmediate) this._commitImmediate(rowKey, colKey, next);
|
|
349
|
-
else {
|
|
350
|
-
this._editValue = next;
|
|
351
|
-
this._commitEdit();
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
break;
|
|
355
|
-
}
|
|
356
|
-
const key = this._tickled.tickledId;
|
|
357
|
-
if (!key) break;
|
|
358
|
-
e.preventDefault();
|
|
359
|
-
if (this.interactionMode === `checked` || this.interactionMode === `sticky` || this.interactionMode === `standard` || this.interactionMode === `vscode` || this.interactionMode === `manual`) this._toggleSelection(key);
|
|
360
|
-
else this._replaceSelection(key);
|
|
361
|
-
break;
|
|
362
|
-
}
|
|
363
|
-
case `Escape`: if (this._editing || this._editingRow) {
|
|
364
|
-
e.preventDefault();
|
|
365
|
-
this._editing = null;
|
|
366
|
-
this._editingRow = null;
|
|
367
|
-
this.requestUpdate();
|
|
368
|
-
} else if (this._selectedKeys.size) {
|
|
369
|
-
e.preventDefault();
|
|
370
|
-
const prev = new Set(this._selectedKeys);
|
|
371
|
-
this._selectedKeys = /* @__PURE__ */ new Set();
|
|
372
|
-
this.dispatchEvent(new CustomEvent(`datagrid-select`, {
|
|
373
|
-
detail: {
|
|
374
|
-
selectedKeys: /* @__PURE__ */ new Set(),
|
|
375
|
-
previous: prev
|
|
376
|
-
},
|
|
377
|
-
bubbles: true,
|
|
378
|
-
composed: true
|
|
379
|
-
}));
|
|
380
|
-
}
|
|
421
|
+
const before = this._tickled.tickledId;
|
|
422
|
+
this.#handleKeyDown(e);
|
|
423
|
+
if (this._tickled.tickledId !== before) {
|
|
424
|
+
this.#pendingFocus = void 0;
|
|
425
|
+
this.#viewState.notify();
|
|
381
426
|
}
|
|
382
427
|
};
|
|
383
428
|
this._suppressBlurCommit = false;
|
|
@@ -660,8 +705,13 @@ let DataGridElement = class DataGridElement extends LitElement {
|
|
|
660
705
|
}
|
|
661
706
|
#rowSearchText(row) {
|
|
662
707
|
if (this.rowSearchText) return this.rowSearchText(row);
|
|
663
|
-
const
|
|
664
|
-
|
|
708
|
+
const col = this.#searchColumn();
|
|
709
|
+
if (col === void 0) return ``;
|
|
710
|
+
const value = this._formatValue(row[col.key], col, row);
|
|
711
|
+
return typeof value === `string` || typeof value === `number` ? String(value) : ``;
|
|
712
|
+
}
|
|
713
|
+
#searchColumn() {
|
|
714
|
+
return (this.searchColumn === `` ? void 0 : this._visibleColumns.find((c) => c.key === this.searchColumn)) ?? this._visibleColumns[0];
|
|
665
715
|
}
|
|
666
716
|
#createSearchAdapter() {
|
|
667
717
|
return {
|
|
@@ -685,7 +735,12 @@ let DataGridElement = class DataGridElement extends LitElement {
|
|
|
685
735
|
const k = this.#searchKeyByRow.get(row);
|
|
686
736
|
return k === void 0 || matched.has(k);
|
|
687
737
|
};
|
|
688
|
-
}
|
|
738
|
+
},
|
|
739
|
+
highlightRoot: () => this.rowSearchText ? void 0 : this.renderRoot,
|
|
740
|
+
highlightSelector: `.row .cell`,
|
|
741
|
+
highlightKeyOf: (el) => el.getAttribute(`data-col`) === this.#searchColumn()?.key ? el.closest(`.row`)?.getAttribute(`data-key`) ?? void 0 : void 0,
|
|
742
|
+
highlightText: (el) => Array.from(el.childNodes, (n) => n.nodeType === Node.TEXT_NODE ? n.textContent : ``).join(``),
|
|
743
|
+
highlightKey: SEARCH_HIGHLIGHT_KEY
|
|
689
744
|
};
|
|
690
745
|
}
|
|
691
746
|
#effectiveSearchModes() {
|
|
@@ -711,6 +766,21 @@ let DataGridElement = class DataGridElement extends LitElement {
|
|
|
711
766
|
#lastViewportVisibleKeys;
|
|
712
767
|
#resortRaf;
|
|
713
768
|
#detachShiftWheel;
|
|
769
|
+
#viewState;
|
|
770
|
+
/** Saved selection for row keys not (yet) in `rows`. */
|
|
771
|
+
#pendingSelected;
|
|
772
|
+
#pendingFocus;
|
|
773
|
+
/** Saved width / visibility for column keys not (yet) in `columns`. */
|
|
774
|
+
#pendingColumns;
|
|
775
|
+
/** Saved column order, kept until every column it names is defined. */
|
|
776
|
+
#columnOrder;
|
|
777
|
+
/** `columns` as of the last column reconcile, to spot removed columns. */
|
|
778
|
+
#reconciledColumns;
|
|
779
|
+
/** Every row by key, built lazily (only when a key isn't in `_flat`); reset on resort. */
|
|
780
|
+
#rowIndex;
|
|
781
|
+
#rowIndexKeyFn;
|
|
782
|
+
/** Group keys in the current flat model. */
|
|
783
|
+
#groupKeys;
|
|
714
784
|
connectedCallback() {
|
|
715
785
|
super.connectedCallback();
|
|
716
786
|
if (!this.hasAttribute(`tabindex`)) this.tabIndex = 0;
|
|
@@ -754,6 +824,7 @@ let DataGridElement = class DataGridElement extends LitElement {
|
|
|
754
824
|
this.#viewportResizeObserver = new ResizeObserver(() => this.#measureViewport());
|
|
755
825
|
this.#viewportResizeObserver.observe(this._viewportEl);
|
|
756
826
|
this.#measureViewport();
|
|
827
|
+
this.#viewState.observeScrollTarget(this._viewportEl);
|
|
757
828
|
}
|
|
758
829
|
/**
|
|
759
830
|
* Re-read the viewport's size and update `_viewportH`/`_viewportW` if it
|
|
@@ -788,6 +859,8 @@ let DataGridElement = class DataGridElement extends LitElement {
|
|
|
788
859
|
}
|
|
789
860
|
updated(changed) {
|
|
790
861
|
super.updated(changed);
|
|
862
|
+
this.#viewState.applyPendingScroll((s) => this.#applyScroll(s));
|
|
863
|
+
if (this.search.isOpen) this.search.repaintHighlights();
|
|
791
864
|
if (changed.has(`rows`) || changed.has(`sortState`) || changed.has(`columns`) || changed.has(`groupBy`) || changed.has(`groupLabel`) || changed.has(`filterPredicate`) || changed.has(`rowHeight`)) this.#scheduleViewportChangeEmit();
|
|
792
865
|
if (changed.has(`_editing`) || changed.has(`_editingRow`)) this.updateComplete.then(() => {
|
|
793
866
|
let sel = null;
|
|
@@ -803,7 +876,8 @@ let DataGridElement = class DataGridElement extends LitElement {
|
|
|
803
876
|
super.willUpdate(changed);
|
|
804
877
|
this.setAttribute(`data-input-mode`, this._tickled.inputMode);
|
|
805
878
|
if (changed.has(`searchModes`)) this.#syncSearchCommands();
|
|
806
|
-
this.#
|
|
879
|
+
if (changed.has(`columns`)) this.#reconcileColumns();
|
|
880
|
+
if (this.#syncDerivedRows()) this.#reconcileRows();
|
|
807
881
|
this._visibleColumns = this.columns.filter((c) => c.visible !== false);
|
|
808
882
|
let off = 0;
|
|
809
883
|
this._colOffsets = this._visibleColumns.map((c) => {
|
|
@@ -840,11 +914,14 @@ let DataGridElement = class DataGridElement extends LitElement {
|
|
|
840
914
|
if (resort) {
|
|
841
915
|
this.#sortInputs = sortInputs;
|
|
842
916
|
this._sortedRows = this._applySort([...this.rows]);
|
|
917
|
+
this.#rowIndex = void 0;
|
|
843
918
|
}
|
|
844
919
|
if (resort || !sameRefs(this.#flatInputs, flatInputs)) {
|
|
845
920
|
this.#flatInputs = flatInputs;
|
|
846
921
|
this._rebuildFlat();
|
|
922
|
+
return true;
|
|
847
923
|
}
|
|
924
|
+
return false;
|
|
848
925
|
}
|
|
849
926
|
/** Mark the derived rows as matching the current inputs (after an incremental update). */
|
|
850
927
|
#markDerivedRowsSynced() {
|
|
@@ -904,6 +981,7 @@ let DataGridElement = class DataGridElement extends LitElement {
|
|
|
904
981
|
for (let i = 0; i < this._sortedRows.length; i++) if (!pred || pred(this._sortedRows[i])) sourceIdx.push(i);
|
|
905
982
|
const flat = [];
|
|
906
983
|
const keys = [];
|
|
984
|
+
this.#groupKeys = /* @__PURE__ */ new Set();
|
|
907
985
|
const pushRow = (idx) => {
|
|
908
986
|
const row = this._sortedRows[idx];
|
|
909
987
|
const key = this.getRowKey(row, idx);
|
|
@@ -924,6 +1002,7 @@ let DataGridElement = class DataGridElement extends LitElement {
|
|
|
924
1002
|
if (arr) arr.push(idx);
|
|
925
1003
|
else groups.set(k, [idx]);
|
|
926
1004
|
}
|
|
1005
|
+
this.#groupKeys = new Set(groups.keys());
|
|
927
1006
|
for (const [gKey, idxs] of groups) {
|
|
928
1007
|
const label = this.groupLabel ? this.groupLabel(gKey, idxs.map((i) => this._sortedRows[i])) : gKey;
|
|
929
1008
|
const gId = `__group__${gKey}`;
|
|
@@ -963,6 +1042,7 @@ let DataGridElement = class DataGridElement extends LitElement {
|
|
|
963
1042
|
if (next.has(gKey)) next.delete(gKey);
|
|
964
1043
|
else next.add(gKey);
|
|
965
1044
|
this._collapsedGroups = next;
|
|
1045
|
+
this.#viewState.notify();
|
|
966
1046
|
}
|
|
967
1047
|
_applySort(rows) {
|
|
968
1048
|
if (!this.sortState) return rows;
|
|
@@ -1001,6 +1081,7 @@ let DataGridElement = class DataGridElement extends LitElement {
|
|
|
1001
1081
|
};
|
|
1002
1082
|
else next = null;
|
|
1003
1083
|
this.sortState = next;
|
|
1084
|
+
this.#viewState.notify();
|
|
1004
1085
|
this.dispatchEvent(new CustomEvent(`datagrid-sort`, {
|
|
1005
1086
|
detail: {
|
|
1006
1087
|
sort: next,
|
|
@@ -1071,6 +1152,7 @@ let DataGridElement = class DataGridElement extends LitElement {
|
|
|
1071
1152
|
_onVisibilityToggle(col, checked) {
|
|
1072
1153
|
col.visible = checked;
|
|
1073
1154
|
this.columns = [...this.columns];
|
|
1155
|
+
this.#viewState.notify();
|
|
1074
1156
|
this.dispatchEvent(new CustomEvent(`datagrid-column-visibility`, {
|
|
1075
1157
|
detail: {
|
|
1076
1158
|
key: col.key,
|
|
@@ -1175,6 +1257,8 @@ let DataGridElement = class DataGridElement extends LitElement {
|
|
|
1175
1257
|
const [moved] = vis.splice(fromIdx, 1);
|
|
1176
1258
|
vis.splice(toIdx, 0, moved);
|
|
1177
1259
|
this.columns = [...vis, ...hidden];
|
|
1260
|
+
this.#columnOrder = EMPTY_ORDER;
|
|
1261
|
+
this.#viewState.notify();
|
|
1178
1262
|
this.dispatchEvent(new CustomEvent(`datagrid-column-reorder`, {
|
|
1179
1263
|
detail: {
|
|
1180
1264
|
from: fromIdx,
|
|
@@ -1222,10 +1306,10 @@ let DataGridElement = class DataGridElement extends LitElement {
|
|
|
1222
1306
|
* set when the selection actually changes.
|
|
1223
1307
|
*/
|
|
1224
1308
|
selectKeys(keys) {
|
|
1309
|
+
this.#pendingSelected = EMPTY_KEYS;
|
|
1225
1310
|
let next = new Set([...keys].filter((k) => this._getRowForKey(k) !== void 0));
|
|
1226
|
-
const multi = this.selectionMode === `multiple` || this.interactionMode === `checked` || this.interactionMode === `sticky` || this.interactionMode === `vscode`;
|
|
1227
1311
|
if (this.selectionMode === `none`) next = /* @__PURE__ */ new Set();
|
|
1228
|
-
else if (!
|
|
1312
|
+
else if (!this.#isMultiSelect() && next.size > 1) {
|
|
1229
1313
|
const last = [...next].at(-1);
|
|
1230
1314
|
next = last === void 0 ? /* @__PURE__ */ new Set() : /* @__PURE__ */ new Set([last]);
|
|
1231
1315
|
}
|
|
@@ -1233,10 +1317,18 @@ let DataGridElement = class DataGridElement extends LitElement {
|
|
|
1233
1317
|
if (prev.size === next.size && [...next].every((k) => prev.has(k))) return;
|
|
1234
1318
|
this._selectedKeys = next;
|
|
1235
1319
|
this._anchorKey = [...next].at(-1);
|
|
1320
|
+
this.#dispatchSelect(prev);
|
|
1321
|
+
}
|
|
1322
|
+
#isMultiSelect() {
|
|
1323
|
+
return this.selectionMode === `multiple` || this.interactionMode === `checked` || this.interactionMode === `sticky` || this.interactionMode === `vscode`;
|
|
1324
|
+
}
|
|
1325
|
+
#dispatchSelect(previous) {
|
|
1326
|
+
if (!this.#viewState.isRestoring) this.#pendingSelected = EMPTY_KEYS;
|
|
1327
|
+
this.#viewState.notify();
|
|
1236
1328
|
this.dispatchEvent(new CustomEvent(`datagrid-select`, {
|
|
1237
1329
|
detail: {
|
|
1238
|
-
selectedKeys: new Set(
|
|
1239
|
-
previous
|
|
1330
|
+
selectedKeys: new Set(this._selectedKeys),
|
|
1331
|
+
previous
|
|
1240
1332
|
},
|
|
1241
1333
|
bubbles: true,
|
|
1242
1334
|
composed: true
|
|
@@ -1251,6 +1343,8 @@ let DataGridElement = class DataGridElement extends LitElement {
|
|
|
1251
1343
|
if (!this.#flatIndexByKey.has(key) || key.startsWith(`__group__`)) return;
|
|
1252
1344
|
this._tickled.focus(key);
|
|
1253
1345
|
this._scrollRowIntoView(key);
|
|
1346
|
+
this.#pendingFocus = void 0;
|
|
1347
|
+
this.#viewState.notify();
|
|
1254
1348
|
}
|
|
1255
1349
|
/**
|
|
1256
1350
|
* Append rows without the full O(n log n) rebuild that reassigning `rows`
|
|
@@ -1273,6 +1367,7 @@ let DataGridElement = class DataGridElement extends LitElement {
|
|
|
1273
1367
|
this._sortedRows = [...this._sortedRows, ...appended];
|
|
1274
1368
|
this.#appendToFlat(appended, startIdx);
|
|
1275
1369
|
this.#markDerivedRowsSynced();
|
|
1370
|
+
this.#resolveAppended(appended, startIdx);
|
|
1276
1371
|
if (this.sortState != null) {
|
|
1277
1372
|
cancelAnimationFrame(this.#resortRaf);
|
|
1278
1373
|
this.#resortRaf = requestAnimationFrame(() => {
|
|
@@ -1311,14 +1406,7 @@ let DataGridElement = class DataGridElement extends LitElement {
|
|
|
1311
1406
|
this._selectedKeys = /* @__PURE__ */ new Set([key]);
|
|
1312
1407
|
} else this._selectedKeys = /* @__PURE__ */ new Set([key]);
|
|
1313
1408
|
this._anchorKey = key;
|
|
1314
|
-
this
|
|
1315
|
-
detail: {
|
|
1316
|
-
selectedKeys: new Set(this._selectedKeys),
|
|
1317
|
-
previous: prev
|
|
1318
|
-
},
|
|
1319
|
-
bubbles: true,
|
|
1320
|
-
composed: true
|
|
1321
|
-
}));
|
|
1409
|
+
this.#dispatchSelect(prev);
|
|
1322
1410
|
}
|
|
1323
1411
|
_toggleSelection(key) {
|
|
1324
1412
|
const prev = new Set(this._selectedKeys);
|
|
@@ -1334,14 +1422,7 @@ let DataGridElement = class DataGridElement extends LitElement {
|
|
|
1334
1422
|
}
|
|
1335
1423
|
this._selectedKeys = next;
|
|
1336
1424
|
this._anchorKey = key;
|
|
1337
|
-
this
|
|
1338
|
-
detail: {
|
|
1339
|
-
selectedKeys: new Set(this._selectedKeys),
|
|
1340
|
-
previous: prev
|
|
1341
|
-
},
|
|
1342
|
-
bubbles: true,
|
|
1343
|
-
composed: true
|
|
1344
|
-
}));
|
|
1425
|
+
this.#dispatchSelect(prev);
|
|
1345
1426
|
}
|
|
1346
1427
|
_rangeSelectTo(key) {
|
|
1347
1428
|
if (!this._anchorKey) {
|
|
@@ -1372,28 +1453,171 @@ let DataGridElement = class DataGridElement extends LitElement {
|
|
|
1372
1453
|
const hi = Math.max(ga, gb);
|
|
1373
1454
|
const prev = new Set(this._selectedKeys);
|
|
1374
1455
|
this._selectedKeys = new Set(groupKeys.slice(lo, hi + 1));
|
|
1375
|
-
this
|
|
1376
|
-
detail: {
|
|
1377
|
-
selectedKeys: new Set(this._selectedKeys),
|
|
1378
|
-
previous: prev
|
|
1379
|
-
},
|
|
1380
|
-
bubbles: true,
|
|
1381
|
-
composed: true
|
|
1382
|
-
}));
|
|
1456
|
+
this.#dispatchSelect(prev);
|
|
1383
1457
|
return;
|
|
1384
1458
|
}
|
|
1385
1459
|
const lo = Math.min(a, b);
|
|
1386
1460
|
const hi = Math.max(a, b);
|
|
1387
1461
|
const prev = new Set(this._selectedKeys);
|
|
1388
1462
|
this._selectedKeys = new Set(keys.slice(lo, hi + 1));
|
|
1389
|
-
this
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1463
|
+
this.#dispatchSelect(prev);
|
|
1464
|
+
}
|
|
1465
|
+
#handleKeyDown(e) {
|
|
1466
|
+
if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === `a`) {
|
|
1467
|
+
if (this.selectionMode === `multiple` || this.interactionMode === `checked` || this.interactionMode === `sticky` || this.interactionMode === `vscode` || this.interactionMode === `standard`) {
|
|
1468
|
+
e.preventDefault();
|
|
1469
|
+
const prev = new Set(this._selectedKeys);
|
|
1470
|
+
if (this.groupBy && !this.crossGroupSelection && this._anchorKey) {
|
|
1471
|
+
const g = this._getGroupKey(this._getRowForKey(this._anchorKey) ?? { key: this._anchorKey });
|
|
1472
|
+
this._selectedKeys = new Set(this._flat.filter((f) => f.type === `row` && this._getGroupKey(f.row) === g).map((f) => f.key));
|
|
1473
|
+
} else this._selectedKeys = new Set(this._flat.filter((f) => f.type === `row`).map((f) => f.key));
|
|
1474
|
+
this.#dispatchSelect(prev);
|
|
1475
|
+
}
|
|
1476
|
+
return;
|
|
1477
|
+
}
|
|
1478
|
+
if (this._editing && (e.key === `ArrowDown` || e.key === `ArrowUp`)) {
|
|
1479
|
+
if ((this.shadowRoot?.activeElement)?.tagName === `SELECT`) return;
|
|
1480
|
+
e.preventDefault();
|
|
1481
|
+
const cur = this._editing;
|
|
1482
|
+
const dir = e.key === `ArrowDown` ? 1 : -1;
|
|
1483
|
+
const colKey = cur.colKey;
|
|
1484
|
+
this._suppressBlurCommit = true;
|
|
1485
|
+
this._commitEdit();
|
|
1486
|
+
const rowKeys = this._flat.filter((f) => f.type === `row`).map((f) => f.key);
|
|
1487
|
+
const idx = rowKeys.indexOf(cur.rowKey);
|
|
1488
|
+
let nextIdx = idx + dir;
|
|
1489
|
+
while (nextIdx >= 0 && nextIdx < rowKeys.length) {
|
|
1490
|
+
const nk = rowKeys[nextIdx];
|
|
1491
|
+
const row = this.#rowEntry(nk)?.row;
|
|
1492
|
+
const col = this._visibleColumns.find((c) => c.key === colKey);
|
|
1493
|
+
if (row && col && this._isEditable(row, col)) {
|
|
1494
|
+
this._startEdit(nk, colKey);
|
|
1495
|
+
this._tickled.handleKeyboardNavigation(nextIdx > idx ? `next` : `previous`);
|
|
1496
|
+
this._scrollRowIntoView(nk);
|
|
1497
|
+
break;
|
|
1498
|
+
}
|
|
1499
|
+
nextIdx += dir;
|
|
1500
|
+
}
|
|
1501
|
+
if (nextIdx < 0 || nextIdx >= rowKeys.length) {
|
|
1502
|
+
this._tickled.handleKeyboardNavigation(dir > 0 ? `next` : `previous`);
|
|
1503
|
+
if (this._tickled.tickledId) this._scrollRowIntoView(this._tickled.tickledId);
|
|
1504
|
+
}
|
|
1505
|
+
requestAnimationFrame(() => {
|
|
1506
|
+
this._suppressBlurCommit = false;
|
|
1507
|
+
});
|
|
1508
|
+
return;
|
|
1509
|
+
}
|
|
1510
|
+
if (e.key === `ArrowDown` || e.key === `ArrowUp` || e.key === `Home` || e.key === `End`) {
|
|
1511
|
+
e.preventDefault();
|
|
1512
|
+
this._tickled.setInputMode(`keyboard`);
|
|
1513
|
+
}
|
|
1514
|
+
switch (e.key) {
|
|
1515
|
+
case `ArrowDown`:
|
|
1516
|
+
this._tickled.handleKeyboardNavigation(`next`);
|
|
1517
|
+
if (this._tickled.tickledId) this._scrollRowIntoView(this._tickled.tickledId);
|
|
1518
|
+
if (this.interactionMode === `implicit` && this._tickled.tickledId) this._replaceSelection(this._tickled.tickledId);
|
|
1519
|
+
if (e.shiftKey && (this.interactionMode === `standard` || this.interactionMode === `vscode`) && this._tickled.tickledId) this._rangeSelectTo(this._tickled.tickledId);
|
|
1520
|
+
break;
|
|
1521
|
+
case `ArrowUp`:
|
|
1522
|
+
this._tickled.handleKeyboardNavigation(`previous`);
|
|
1523
|
+
if (this._tickled.tickledId) this._scrollRowIntoView(this._tickled.tickledId);
|
|
1524
|
+
if (this.interactionMode === `implicit` && this._tickled.tickledId) this._replaceSelection(this._tickled.tickledId);
|
|
1525
|
+
if (e.shiftKey && (this.interactionMode === `standard` || this.interactionMode === `vscode`) && this._tickled.tickledId) this._rangeSelectTo(this._tickled.tickledId);
|
|
1526
|
+
break;
|
|
1527
|
+
case `Home`:
|
|
1528
|
+
this._tickled.handleKeyboardNavigation(`first`);
|
|
1529
|
+
if (this._tickled.tickledId) this._scrollRowIntoView(this._tickled.tickledId);
|
|
1530
|
+
break;
|
|
1531
|
+
case `End`:
|
|
1532
|
+
this._tickled.handleKeyboardNavigation(`last`);
|
|
1533
|
+
if (this._tickled.tickledId) this._scrollRowIntoView(this._tickled.tickledId);
|
|
1534
|
+
break;
|
|
1535
|
+
case `Enter`: {
|
|
1536
|
+
const key = this._tickled.tickledId;
|
|
1537
|
+
if (!key) break;
|
|
1538
|
+
e.preventDefault();
|
|
1539
|
+
if (!this._editing && this.editability === `user`) {
|
|
1540
|
+
const entry = this.#rowEntry(key);
|
|
1541
|
+
if (entry) {
|
|
1542
|
+
const row = entry.row;
|
|
1543
|
+
if (this._lastClickedCell?.rowKey === key) {
|
|
1544
|
+
const col = this._visibleColumns.find((c) => c.key === this._lastClickedCell.colKey);
|
|
1545
|
+
if (col && this._isEditable(row, col)) {
|
|
1546
|
+
this._startEdit(key, col.key);
|
|
1547
|
+
break;
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
const firstEditable = this._visibleColumns.find((c) => this._isEditable(row, c));
|
|
1551
|
+
if (firstEditable) {
|
|
1552
|
+
this._startEdit(key, firstEditable.key);
|
|
1553
|
+
break;
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
if (!this._editing) {
|
|
1558
|
+
const entry = this.#rowEntry(key);
|
|
1559
|
+
const idx = entry?.idx ?? -1;
|
|
1560
|
+
if (this._selectedKeys.has(key)) {
|
|
1561
|
+
this._activateRow(key, entry?.row, idx);
|
|
1562
|
+
break;
|
|
1563
|
+
}
|
|
1564
|
+
if (this.interactionMode !== `manual` && this.interactionMode !== `checked`) this._replaceSelection(key);
|
|
1565
|
+
this.dispatchEvent(new CustomEvent(`datagrid-cell-click`, {
|
|
1566
|
+
detail: {
|
|
1567
|
+
rowKey: key,
|
|
1568
|
+
colKey: ``,
|
|
1569
|
+
rowIndex: idx,
|
|
1570
|
+
colIndex: -1
|
|
1571
|
+
},
|
|
1572
|
+
bubbles: true,
|
|
1573
|
+
composed: true
|
|
1574
|
+
}));
|
|
1575
|
+
}
|
|
1576
|
+
break;
|
|
1577
|
+
}
|
|
1578
|
+
case ` `: {
|
|
1579
|
+
if (this._editing) {
|
|
1580
|
+
if ((this.columns.find((c) => c.key === this._editing.colKey)?.dataType ?? `text`) === `boolean`) {
|
|
1581
|
+
e.preventDefault();
|
|
1582
|
+
const isImmediate = this.editability === `immediate`;
|
|
1583
|
+
const rowKey = this._editing.rowKey;
|
|
1584
|
+
const colKey = this._editing.colKey;
|
|
1585
|
+
const active = this.shadowRoot?.activeElement;
|
|
1586
|
+
let next;
|
|
1587
|
+
if (active?.type === `checkbox`) {
|
|
1588
|
+
next = !active.checked;
|
|
1589
|
+
active.checked = next;
|
|
1590
|
+
} else {
|
|
1591
|
+
const row = this.#rowEntry(rowKey)?.row;
|
|
1592
|
+
next = !(this._editValue !== `` ? Boolean(this._editValue) : Boolean(row?.[colKey]));
|
|
1593
|
+
}
|
|
1594
|
+
if (isImmediate) this._commitImmediate(rowKey, colKey, next);
|
|
1595
|
+
else {
|
|
1596
|
+
this._editValue = next;
|
|
1597
|
+
this._commitEdit();
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
break;
|
|
1601
|
+
}
|
|
1602
|
+
const key = this._tickled.tickledId;
|
|
1603
|
+
if (!key) break;
|
|
1604
|
+
e.preventDefault();
|
|
1605
|
+
if (this.interactionMode === `checked` || this.interactionMode === `sticky` || this.interactionMode === `standard` || this.interactionMode === `vscode` || this.interactionMode === `manual`) this._toggleSelection(key);
|
|
1606
|
+
else this._replaceSelection(key);
|
|
1607
|
+
break;
|
|
1608
|
+
}
|
|
1609
|
+
case `Escape`: if (this._editing || this._editingRow) {
|
|
1610
|
+
e.preventDefault();
|
|
1611
|
+
this._editing = null;
|
|
1612
|
+
this._editingRow = null;
|
|
1613
|
+
this.requestUpdate();
|
|
1614
|
+
} else if (this._selectedKeys.size) {
|
|
1615
|
+
e.preventDefault();
|
|
1616
|
+
const prev = new Set(this._selectedKeys);
|
|
1617
|
+
this._selectedKeys = /* @__PURE__ */ new Set();
|
|
1618
|
+
this.#dispatchSelect(prev);
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1397
1621
|
}
|
|
1398
1622
|
_isEditable(row, col) {
|
|
1399
1623
|
if (this.editability === `disabled`) return false;
|
|
@@ -1609,6 +1833,7 @@ let DataGridElement = class DataGridElement extends LitElement {
|
|
|
1609
1833
|
const w = this._measureAutoWidth(col);
|
|
1610
1834
|
col.width = w;
|
|
1611
1835
|
this.requestUpdate();
|
|
1836
|
+
this.#viewState.notify();
|
|
1612
1837
|
this.dispatchEvent(new CustomEvent(`datagrid-column-resize`, {
|
|
1613
1838
|
detail: {
|
|
1614
1839
|
key: col.key,
|
|
@@ -1636,6 +1861,7 @@ let DataGridElement = class DataGridElement extends LitElement {
|
|
|
1636
1861
|
requestAnimationFrame(() => {
|
|
1637
1862
|
col.width = target;
|
|
1638
1863
|
this.requestUpdate();
|
|
1864
|
+
this.#viewState.notify();
|
|
1639
1865
|
this.dispatchEvent(new CustomEvent(`datagrid-column-resize`, {
|
|
1640
1866
|
detail: {
|
|
1641
1867
|
key,
|
|
@@ -1672,6 +1898,7 @@ let DataGridElement = class DataGridElement extends LitElement {
|
|
|
1672
1898
|
window.removeEventListener(`pointerup`, onUp);
|
|
1673
1899
|
this._resizingColKey = void 0;
|
|
1674
1900
|
const w = Math.max(col.minWidth ?? 40, Math.min(col.maxWidth ?? 600, startW + ev.clientX - startX));
|
|
1901
|
+
this.#viewState.notify();
|
|
1675
1902
|
this.dispatchEvent(new CustomEvent(`datagrid-column-resize`, {
|
|
1676
1903
|
detail: {
|
|
1677
1904
|
key: col.key,
|
|
@@ -1684,6 +1911,179 @@ let DataGridElement = class DataGridElement extends LitElement {
|
|
|
1684
1911
|
window.addEventListener(`pointermove`, onMove);
|
|
1685
1912
|
window.addEventListener(`pointerup`, onUp);
|
|
1686
1913
|
}
|
|
1914
|
+
/**
|
|
1915
|
+
* Snapshot selection, cursor, scroll, collapsed groups, column layout, sort
|
|
1916
|
+
* and (with `allowGrouping`) grouping as plain JSON, keyed by row / group /
|
|
1917
|
+
* column key. Includes pending state for keys not currently present. Rows
|
|
1918
|
+
* keyed by index (no `key` / `id` under the default `getRowKey`) are never
|
|
1919
|
+
* recorded.
|
|
1920
|
+
*/
|
|
1921
|
+
getViewState(options) {
|
|
1922
|
+
const withPending = options?.includePending ?? true;
|
|
1923
|
+
const cursor = this._tickled.inputMode === `keyboard` ? this._tickled.tickledId : void 0;
|
|
1924
|
+
const focus = cursor !== void 0 && this.#isStableKey(cursor) ? cursor : void 0;
|
|
1925
|
+
const groupBy = this.groupBy === null || typeof this.groupBy === `string` ? this.groupBy : void 0;
|
|
1926
|
+
return this.#viewState.shape({
|
|
1927
|
+
selected: keysToArray([...this._selectedKeys].filter((k) => this.#isStableKey(k)), withPending ? this.#pendingSelected : []),
|
|
1928
|
+
focus: focus ?? (withPending ? this.#pendingFocus : void 0),
|
|
1929
|
+
scroll: (withPending ? this.#viewState.pendingScroll : void 0) ?? this.#captureScroll(),
|
|
1930
|
+
expanded: collapsedToRecord(this._collapsedGroups, (k) => withPending || this.#groupKeys.has(k)),
|
|
1931
|
+
columns: captureColumns(this.columns, this.#pendingColumns, this.#columnOrder, withPending),
|
|
1932
|
+
sort: this.sortState,
|
|
1933
|
+
groupBy: this.allowGrouping ? groupBy : void 0
|
|
1934
|
+
}, options);
|
|
1935
|
+
}
|
|
1936
|
+
/**
|
|
1937
|
+
* Apply saved view state. Fields present replace the current ones; row and
|
|
1938
|
+
* column keys that aren't defined yet are applied as they appear (including
|
|
1939
|
+
* rows streamed in with `appendRows()`).
|
|
1940
|
+
*/
|
|
1941
|
+
setViewState(state) {
|
|
1942
|
+
if (!this.#viewState.accepts(state)) return;
|
|
1943
|
+
this.#viewState.restoring(() => {
|
|
1944
|
+
if (state.columns && typeof state.columns === `object`) {
|
|
1945
|
+
const { pending, order } = columnStateFromRecord(state.columns);
|
|
1946
|
+
this.#pendingColumns = pending;
|
|
1947
|
+
this.#columnOrder = order;
|
|
1948
|
+
this.#reconcileColumns();
|
|
1949
|
+
}
|
|
1950
|
+
if (state.sort !== void 0 && isSortState(state.sort)) this.sortState = state.sort;
|
|
1951
|
+
if (state.groupBy === null || typeof state.groupBy === `string`) this.groupBy = state.groupBy;
|
|
1952
|
+
if (state.expanded && typeof state.expanded === `object`) this._collapsedGroups = collapsedFromRecord(state.expanded);
|
|
1953
|
+
if (state.focus !== void 0) this.#pendingFocus = state.focus;
|
|
1954
|
+
if (state.scroll) this.#viewState.pendingScroll = state.scroll;
|
|
1955
|
+
if (Array.isArray(state.selected)) {
|
|
1956
|
+
const prev = this._selectedKeys;
|
|
1957
|
+
this.#pendingSelected = capKeys(new Set(state.selected.filter((k) => typeof k === `string`)));
|
|
1958
|
+
this._selectedKeys = /* @__PURE__ */ new Set();
|
|
1959
|
+
this.#reconcileRows(prev);
|
|
1960
|
+
} else this.#reconcileRows();
|
|
1961
|
+
this.#viewState.notify();
|
|
1962
|
+
});
|
|
1963
|
+
this.requestUpdate();
|
|
1964
|
+
}
|
|
1965
|
+
clearPendingViewState() {
|
|
1966
|
+
this.#pendingSelected = EMPTY_KEYS;
|
|
1967
|
+
this.#pendingFocus = void 0;
|
|
1968
|
+
this.#pendingColumns = EMPTY_COLUMNS;
|
|
1969
|
+
this.#columnOrder = EMPTY_ORDER;
|
|
1970
|
+
this.#viewState.pendingScroll = void 0;
|
|
1971
|
+
if ([...this._collapsedGroups].some((k) => !this.#groupKeys.has(k))) this._collapsedGroups = new Set([...this._collapsedGroups].filter((k) => this.#groupKeys.has(k)));
|
|
1972
|
+
}
|
|
1973
|
+
/** Apply pending column layout to defined columns; demote layout of removed ones. */
|
|
1974
|
+
#reconcileColumns() {
|
|
1975
|
+
const r = reconcileColumns(this.#reconciledColumns, this.columns, this.#pendingColumns, this.#columnOrder);
|
|
1976
|
+
this.#pendingColumns = r.pending;
|
|
1977
|
+
this.#columnOrder = r.order;
|
|
1978
|
+
this.#reconciledColumns = r.columns;
|
|
1979
|
+
if (r.columns !== this.columns) {
|
|
1980
|
+
this.columns = r.columns;
|
|
1981
|
+
this.#viewState.restoring(() => this.#viewState.notify());
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1984
|
+
/**
|
|
1985
|
+
* Re-key selection and cursor onto the current rows: state for rows that
|
|
1986
|
+
* left goes pending (silently), pending state for rows now present is
|
|
1987
|
+
* applied. `baseline` is the selection to report as `previous` when the
|
|
1988
|
+
* caller already replaced it (`setViewState`).
|
|
1989
|
+
*/
|
|
1990
|
+
#reconcileRows(baseline) {
|
|
1991
|
+
this.#viewState.restoring(() => {
|
|
1992
|
+
const sel = reconcileSelectedKeys(this._selectedKeys, this.#pendingSelected, (k) => this.#hasRow(k), !this.#isMultiSelect(), this.selectionMode !== `none`);
|
|
1993
|
+
this.#pendingSelected = sel.pending;
|
|
1994
|
+
if (sel.changed) this.#setRestoredSelection(sel.selected);
|
|
1995
|
+
const before = baseline ?? this._selectedKeys;
|
|
1996
|
+
if (baseline ? !sameSet(baseline, this._selectedKeys) : sel.restored) this.#dispatchSelect(before);
|
|
1997
|
+
const cursor = this._tickled.tickledId;
|
|
1998
|
+
if (cursor !== void 0 && !this.#flatIndexByKey.has(cursor)) {
|
|
1999
|
+
if (this._tickled.inputMode === `keyboard` && this.#isStableKey(cursor)) this.#pendingFocus ??= cursor;
|
|
2000
|
+
this._tickled.clearTickled();
|
|
2001
|
+
}
|
|
2002
|
+
this.#applyPendingFocus();
|
|
2003
|
+
});
|
|
2004
|
+
}
|
|
2005
|
+
/** Streamed append: match pending state against the new slice only (never the whole row set). */
|
|
2006
|
+
#resolveAppended(appended, startIdx) {
|
|
2007
|
+
const wantSelection = this.#pendingSelected.size > 0 && this.selectionMode !== `none`;
|
|
2008
|
+
if (wantSelection || this.#rowIndex !== void 0) {
|
|
2009
|
+
const keys = appended.map((r, j) => this.getRowKey(r, startIdx + j));
|
|
2010
|
+
keys.forEach((k, j) => this.#rowIndex?.set(k, appended[j]));
|
|
2011
|
+
const present = wantSelection ? matchSlice(this.#pendingSelected, keys) : [];
|
|
2012
|
+
if (present.length > 0) this.#viewState.restoring(() => {
|
|
2013
|
+
const prev = this._selectedKeys;
|
|
2014
|
+
const sel = resolvePendingKeys(prev, this.#pendingSelected, present, !this.#isMultiSelect());
|
|
2015
|
+
this.#pendingSelected = sel.pending;
|
|
2016
|
+
this.#setRestoredSelection(sel.selected);
|
|
2017
|
+
this.#dispatchSelect(prev);
|
|
2018
|
+
});
|
|
2019
|
+
}
|
|
2020
|
+
if (this.#pendingFocus !== void 0) this.#viewState.restoring(() => this.#applyPendingFocus());
|
|
2021
|
+
}
|
|
2022
|
+
#setRestoredSelection(selected) {
|
|
2023
|
+
const lastKey = this._anchorKey;
|
|
2024
|
+
this._selectedKeys = new Set(selected);
|
|
2025
|
+
this._anchorKey = lastKey !== void 0 && selected.has(lastKey) ? lastKey : [...selected].at(-1);
|
|
2026
|
+
}
|
|
2027
|
+
#applyPendingFocus() {
|
|
2028
|
+
const key = this.#pendingFocus;
|
|
2029
|
+
if (key === void 0 || this._getRowForKey(key) === void 0) return;
|
|
2030
|
+
this.#pendingFocus = void 0;
|
|
2031
|
+
this._tickled.setInputMode(`keyboard`);
|
|
2032
|
+
this._tickled.setTickled(key);
|
|
2033
|
+
this.#viewState.notify();
|
|
2034
|
+
}
|
|
2035
|
+
/** Whether a row with this key is in `rows` (visible, filtered out or in a collapsed group). */
|
|
2036
|
+
#hasRow(key) {
|
|
2037
|
+
const i = this.#flatIndexByKey.get(key);
|
|
2038
|
+
if (i !== void 0) return this._flat[i].type === `row`;
|
|
2039
|
+
return this.#rowsByKey().has(key);
|
|
2040
|
+
}
|
|
2041
|
+
/** Every row by key — built only when a key isn't in `_flat`, then reused until the next resort. */
|
|
2042
|
+
#rowsByKey() {
|
|
2043
|
+
if (this.#rowIndex === void 0 || this.#rowIndexKeyFn !== this.getRowKey) {
|
|
2044
|
+
this.#rowIndexKeyFn = this.getRowKey;
|
|
2045
|
+
this.#rowIndex = new Map(this._sortedRows.map((r, j) => [this.getRowKey(r, j), r]));
|
|
2046
|
+
}
|
|
2047
|
+
return this.#rowIndex;
|
|
2048
|
+
}
|
|
2049
|
+
/** Index-derived keys (default `getRowKey`, row without `key` / `id`) can't be restored, so aren't recorded. */
|
|
2050
|
+
#isStableKey(key) {
|
|
2051
|
+
if (this.getRowKey !== defaultGetRowKey) return true;
|
|
2052
|
+
const row = this._getRowForKey(key) ?? this.#rowsByKey().get(key);
|
|
2053
|
+
return row !== void 0 && (row.key ?? row.id) !== void 0;
|
|
2054
|
+
}
|
|
2055
|
+
#captureScroll() {
|
|
2056
|
+
const vp = this._viewportEl;
|
|
2057
|
+
if (!vp) return void 0;
|
|
2058
|
+
const top = vp.scrollTop;
|
|
2059
|
+
const rh = this.rowHeight;
|
|
2060
|
+
const first = Math.max(0, Math.floor(top / rh));
|
|
2061
|
+
const end = Math.min(this._flat.length, first + 64);
|
|
2062
|
+
for (let i = first; i < end; i++) {
|
|
2063
|
+
const entry = this._flat[i];
|
|
2064
|
+
if (entry.type === `row`) return anchorFromRow(top, this.#isStableKey(entry.key) ? entry.key : void 0, i * rh, vp.scrollLeft);
|
|
2065
|
+
}
|
|
2066
|
+
return anchorFromRow(top, void 0, 0, vp.scrollLeft);
|
|
2067
|
+
}
|
|
2068
|
+
/** Returns `true` once the saved position is fully reached. */
|
|
2069
|
+
#applyScroll(scroll) {
|
|
2070
|
+
const vp = this._viewportEl;
|
|
2071
|
+
if (!vp) return false;
|
|
2072
|
+
let top = scroll.top;
|
|
2073
|
+
if (scroll.anchor) {
|
|
2074
|
+
const i = this.#flatIndexByKey.get(scroll.anchor.key);
|
|
2075
|
+
if (i === void 0) return false;
|
|
2076
|
+
top = i * this.rowHeight + scroll.anchor.offset;
|
|
2077
|
+
}
|
|
2078
|
+
const reachedY = scrollToOffset(vp, top);
|
|
2079
|
+
const reachedX = scroll.left === void 0 || scrollToOffset(vp, scroll.left, `horizontal`);
|
|
2080
|
+
const before = this.#windowRows(2);
|
|
2081
|
+
this._scrollTop = vp.scrollTop;
|
|
2082
|
+
this._scrollLeft = vp.scrollLeft;
|
|
2083
|
+
const after = this.#windowRows(2);
|
|
2084
|
+
if (before.start !== after.start || before.end !== after.end) this.requestUpdate();
|
|
2085
|
+
return reachedY && reachedX;
|
|
2086
|
+
}
|
|
1687
2087
|
/** Height of the row area: the viewport minus the sticky header row inside it. */
|
|
1688
2088
|
#bodyHeight() {
|
|
1689
2089
|
return this._viewportEl.clientHeight - (this._headerRowEl?.offsetHeight ?? 0);
|
|
@@ -2013,6 +2413,7 @@ let DataGridElement = class DataGridElement extends LitElement {
|
|
|
2013
2413
|
const v = value ?? ``;
|
|
2014
2414
|
this.groupBy = v || null;
|
|
2015
2415
|
this._collapsedGroups = /* @__PURE__ */ new Set();
|
|
2416
|
+
this.#viewState.notify();
|
|
2016
2417
|
this.dispatchEvent(new CustomEvent(`datagrid-group-change`, {
|
|
2017
2418
|
detail: { groupBy: this.groupBy },
|
|
2018
2419
|
bubbles: true,
|
|
@@ -2101,6 +2502,10 @@ __decorate([property({
|
|
|
2101
2502
|
})], DataGridElement.prototype, "badgeColumn", void 0);
|
|
2102
2503
|
__decorate([property({ attribute: false })], DataGridElement.prototype, "filterPredicate", void 0);
|
|
2103
2504
|
__decorate([property({ attribute: false })], DataGridElement.prototype, "rowSearchText", void 0);
|
|
2505
|
+
__decorate([property({
|
|
2506
|
+
type: String,
|
|
2507
|
+
attribute: `search-column`
|
|
2508
|
+
})], DataGridElement.prototype, "searchColumn", void 0);
|
|
2104
2509
|
__decorate([property({
|
|
2105
2510
|
type: String,
|
|
2106
2511
|
attribute: `search-modes`,
|