@rozie-ui/data-table-lit 0.7.4 → 0.8.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/dist/index.mjs CHANGED
@@ -96,6 +96,74 @@ const columnSpecsEquivalent = (a, b, depth) => {
96
96
  return true;
97
97
  };
98
98
  //#endregion
99
+ //#region src/helpers/columnRuleUtils.ts
100
+ const FILTER_FN_NAMES = [
101
+ "includesString",
102
+ "includesStringSensitive",
103
+ "equalsString",
104
+ "arrIncludes",
105
+ "arrIncludesAll",
106
+ "arrIncludesSome",
107
+ "equals",
108
+ "weakEquals",
109
+ "inNumberRange"
110
+ ];
111
+ const SORTING_FN_NAMES = [
112
+ "alphanumeric",
113
+ "alphanumericCaseSensitive",
114
+ "text",
115
+ "textCaseSensitive",
116
+ "datetime",
117
+ "basic"
118
+ ];
119
+ const warned = /* @__PURE__ */ new Set();
120
+ const resolveName = (value, names, kind, columnId, warn) => {
121
+ if (value === "auto" || names.includes(value)) return value;
122
+ const key = kind + "\0" + columnId + "\0" + value;
123
+ if (!warned.has(key)) {
124
+ warned.add(key);
125
+ warn(`[data-table] column "${columnId}": unknown ${kind} "${value}"; falling back to "auto".`);
126
+ }
127
+ return "auto";
128
+ };
129
+ const resolveFilterFn = (value, columnId, warn = console.warn) => {
130
+ if (value == null) return void 0;
131
+ if (typeof value === "string") return resolveName(value, FILTER_FN_NAMES, "filterFn", columnId, warn);
132
+ if (typeof value !== "function") return void 0;
133
+ const fn = value;
134
+ const adapter = (row, colId, filterValue) => {
135
+ try {
136
+ return fn(row.getValue(colId), filterValue, row.original);
137
+ } catch (err) {
138
+ return true;
139
+ }
140
+ };
141
+ if (fn.autoRemove !== void 0) adapter.autoRemove = fn.autoRemove;
142
+ if (fn.resolveFilterValue !== void 0) adapter.resolveFilterValue = fn.resolveFilterValue;
143
+ return adapter;
144
+ };
145
+ const resolveSortingFn = (value, columnId, warn = console.warn) => {
146
+ if (value == null) return void 0;
147
+ if (typeof value === "string") return resolveName(value, SORTING_FN_NAMES, "sortingFn", columnId, warn);
148
+ if (typeof value !== "function") return void 0;
149
+ const fn = value;
150
+ return (rowA, rowB, colId) => {
151
+ try {
152
+ return fn(rowA.getValue(colId), rowB.getValue(colId), rowA.original, rowB.original);
153
+ } catch (err) {
154
+ return 0;
155
+ }
156
+ };
157
+ };
158
+ const columnRuleKeys = (spec, columnId, warn) => {
159
+ const out = {};
160
+ const f = resolveFilterFn(spec?.filterFn, columnId, warn);
161
+ if (f !== void 0) out.filterFn = f;
162
+ const s = resolveSortingFn(spec?.sortingFn, columnId, warn);
163
+ if (s !== void 0) out.sortingFn = s;
164
+ return out;
165
+ };
166
+ //#endregion
99
167
  //#region src/helpers/indexMath.ts
100
168
  const clamp = (v, lo, hi) => v < lo ? lo : v > hi ? hi : v;
101
169
  const focusables = (cellEl) => {
@@ -104,6 +172,24 @@ const focusables = (cellEl) => {
104
172
  };
105
173
  const applyUpdater = (updater, current) => typeof updater === "function" ? updater(current) : updater;
106
174
  //#endregion
175
+ //#region src/helpers/activeFollow.ts
176
+ function resolveFollow(prevIds, activeRow, nextIds, viewChangeOrWrite) {
177
+ if (!prevIds || !Number.isInteger(activeRow) || activeRow < 0 || activeRow >= prevIds.length) return { kind: "clamp" };
178
+ const id = prevIds[activeRow];
179
+ if (nextIds[activeRow] === id) return { kind: "stay" };
180
+ const index = nextIds.indexOf(id);
181
+ if (index >= 0) return {
182
+ kind: "move",
183
+ index
184
+ };
185
+ return viewChangeOrWrite ? { kind: "park" } : { kind: "clamp" };
186
+ }
187
+ function sameIdSequence(a, b) {
188
+ if (!a || a.length !== b.length) return false;
189
+ for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
190
+ return true;
191
+ }
192
+ //#endregion
107
193
  //#region src/helpers/tsvGrid.ts
108
194
  const escapeTsvField = (s) => {
109
195
  if (s.indexOf(" ") >= 0 || s.indexOf("\n") >= 0 || s.indexOf("\r") >= 0 || s.indexOf("\"") >= 0) return "\"" + s.replace(/"/g, "\"\"") + "\"";
@@ -239,6 +325,150 @@ const indexOfRowIn = (rows, rowOriginal, rowId) => {
239
325
  return -1;
240
326
  };
241
327
  //#endregion
328
+ //#region src/helpers/heldView.ts
329
+ const CLEAN_VIEW_STATE = Object.freeze({
330
+ held: false,
331
+ filterMismatches: 0,
332
+ sortMismatches: 0
333
+ });
334
+ const viewStatesEqual = (a, b) => a.held === b.held && a.filterMismatches === b.filterMismatches && a.sortMismatches === b.sortMismatches;
335
+ const changedIndices = (prev, next) => {
336
+ const out = [];
337
+ if (prev.length !== next.length) {
338
+ const n = Math.max(prev.length, next.length);
339
+ for (let i = 0; i < n; i++) out.push(i);
340
+ return out;
341
+ }
342
+ for (let i = 0; i < next.length; i++) if (prev[i] !== next[i]) out.push(i);
343
+ return out;
344
+ };
345
+ const rowIds = (model) => (model?.rows ?? []).map((r) => r.id);
346
+ const createHoldController = () => {
347
+ let active = false;
348
+ let heldMembership = null;
349
+ let heldOrder = null;
350
+ const written = /* @__PURE__ */ new Set();
351
+ let version = 0;
352
+ let liveFilteredIds = /* @__PURE__ */ new Set();
353
+ let liveSortedIds = [];
354
+ const release = () => {
355
+ heldMembership = null;
356
+ heldOrder = null;
357
+ written.clear();
358
+ version++;
359
+ };
360
+ const wrapFiltered = (base) => (table) => {
361
+ const getBase = base(table);
362
+ let lastBase = void 0;
363
+ let lastVersion = -1;
364
+ let lastOut = void 0;
365
+ return () => {
366
+ const baseModel = getBase();
367
+ if (!heldMembership) return baseModel;
368
+ if (baseModel === lastBase && version === lastVersion) return lastOut;
369
+ liveFilteredIds = new Set(rowIds(baseModel));
370
+ const held = heldMembership;
371
+ const rows = table.getCoreRowModel().rows.filter((r) => held.has(r.id));
372
+ const rowsById = {};
373
+ for (const r of rows) rowsById[r.id] = r;
374
+ lastBase = baseModel;
375
+ lastVersion = version;
376
+ lastOut = {
377
+ rows,
378
+ flatRows: rows,
379
+ rowsById
380
+ };
381
+ return lastOut;
382
+ };
383
+ };
384
+ const wrapSorted = (base) => (table) => {
385
+ const getBase = base(table);
386
+ let lastBase = void 0;
387
+ let lastVersion = -1;
388
+ let lastOut = void 0;
389
+ return () => {
390
+ const baseModel = getBase();
391
+ if (!heldOrder) return baseModel;
392
+ if (baseModel === lastBase && version === lastVersion) return lastOut;
393
+ const baseRows = baseModel?.rows ?? [];
394
+ liveSortedIds = baseRows.map((r) => r.id);
395
+ const byId = /* @__PURE__ */ new Map();
396
+ for (const r of baseRows) byId.set(r.id, r);
397
+ const rows = [];
398
+ for (const id of heldOrder) {
399
+ const r = byId.get(id);
400
+ if (r) {
401
+ rows.push(r);
402
+ byId.delete(id);
403
+ }
404
+ }
405
+ for (const r of baseRows) if (byId.has(r.id)) rows.push(r);
406
+ lastBase = baseModel;
407
+ lastVersion = version;
408
+ lastOut = {
409
+ rows,
410
+ flatRows: rows,
411
+ rowsById: baseModel?.rowsById ?? {}
412
+ };
413
+ return lastOut;
414
+ };
415
+ };
416
+ return {
417
+ setActive(next) {
418
+ const was = active;
419
+ active = !!next;
420
+ if (!active && was) release();
421
+ },
422
+ isActive: () => active,
423
+ isHolding: () => heldMembership !== null,
424
+ capture(table) {
425
+ if (!active || heldMembership) return;
426
+ const filtered = table.getFilteredRowModel();
427
+ const sorted = typeof table.getSortedRowModel === "function" ? table.getSortedRowModel() : filtered;
428
+ heldMembership = new Set(rowIds(filtered));
429
+ heldOrder = rowIds(sorted);
430
+ written.clear();
431
+ version++;
432
+ },
433
+ release,
434
+ noteWrite(prev, next) {
435
+ if (!heldMembership) return;
436
+ for (const i of changedIndices(prev, next)) written.add(i);
437
+ },
438
+ wrapFiltered,
439
+ wrapSorted,
440
+ viewState(table) {
441
+ if (!active) return CLEAN_VIEW_STATE;
442
+ if (!heldMembership || !heldOrder) return {
443
+ held: true,
444
+ filterMismatches: 0,
445
+ sortMismatches: 0
446
+ };
447
+ table.getSortedRowModel();
448
+ const held = heldMembership;
449
+ let filterMismatches = 0;
450
+ for (const id of held) if (!liveFilteredIds.has(id)) filterMismatches++;
451
+ const coreRows = table.getCoreRowModel().rows;
452
+ for (const r of coreRows) if (!held.has(r.id) && liveFilteredIds.has(r.id)) filterMismatches++;
453
+ const heldPos = /* @__PURE__ */ new Map();
454
+ heldOrder.forEach((id, i) => heldPos.set(id, i));
455
+ const livePos = /* @__PURE__ */ new Map();
456
+ liveSortedIds.forEach((id, i) => livePos.set(id, i));
457
+ let sortMismatches = 0;
458
+ for (const i of written) {
459
+ const id = coreRows[i]?.id;
460
+ if (id === void 0 || !heldPos.has(id)) continue;
461
+ if (heldPos.get(id) !== livePos.get(id)) sortMismatches++;
462
+ }
463
+ return {
464
+ held: true,
465
+ filterMismatches,
466
+ sortMismatches
467
+ };
468
+ }
469
+ };
470
+ };
471
+ //#endregion
242
472
  //#region \0@oxc-project+runtime@0.127.0/helpers/decorate.js
243
473
  function __decorate(decorators, target, key, desc) {
244
474
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
@@ -361,6 +591,7 @@ let DataTable = class DataTable extends SignalWatcher(LitElement) {
361
591
  this.singleClickEdit = false;
362
592
  this.undoable = false;
363
593
  this.undoLimit = 100;
594
+ this.holdView = false;
364
595
  this.virtual = false;
365
596
  this.estimateRowHeight = 40;
366
597
  this.autoMeasure = false;
@@ -479,6 +710,48 @@ let DataTable = class DataTable extends SignalWatcher(LitElement) {
479
710
  this.undoStack = [];
480
711
  this.redoStack = [];
481
712
  this.restoringHistory = false;
713
+ this.holdCtrl = createHoldController();
714
+ this.heldFilteredRowModel = this.holdCtrl.wrapFiltered(getFilteredRowModel());
715
+ this.heldSortedRowModel = this.holdCtrl.wrapSorted(getSortedRowModel());
716
+ this.pendingViewChange = false;
717
+ this.pendingWrite = false;
718
+ this.prevRowIds = null;
719
+ this.lastFedData = null;
720
+ this.lastViewState = CLEAN_VIEW_STATE;
721
+ this.lastHoldSorting = null;
722
+ this.lastHoldColumnFilters = null;
723
+ this.lastHoldGlobalFilter = null;
724
+ this.holdFallbackWarned = false;
725
+ this.lastPageGrouping = null;
726
+ this.lastColumnSig = "";
727
+ this.columnSetSignature = (defs) => {
728
+ let sig = "";
729
+ const stack = (defs || []).slice().reverse();
730
+ while (stack.length) {
731
+ const d = stack.pop();
732
+ if (!d) continue;
733
+ sig += String(d.id) + (d.enableSorting ? "+" : "") + (d.enableColumnFilter ? "*" : "") + "|";
734
+ if (Array.isArray(d.columns)) for (let i = d.columns.length - 1; i >= 0; i--) stack.push(d.columns[i]);
735
+ }
736
+ return sig;
737
+ };
738
+ this.lastPropsData = null;
739
+ this.isExternalSwap = () => {
740
+ const pd = this.data;
741
+ if (pd === this.lastPropsData) return false;
742
+ return !(pd != null && pd[this.DATA_WRITE_TOKEN_KEY] != null);
743
+ };
744
+ this.syncHoldActive = (st) => {
745
+ const wanted = this.holdView === true;
746
+ const active = wanted && this.manual !== true && this.getSubRows == null && !(st.grouping || []).length;
747
+ const wasHolding = this.holdCtrl.isHolding();
748
+ this.holdCtrl.setActive(active);
749
+ if (wasHolding && !this.holdCtrl.isHolding()) this.pendingViewChange = true;
750
+ if (wanted && !active && !this.holdFallbackWarned) {
751
+ this.holdFallbackWarned = true;
752
+ console.warn("[rozie-data-table] holdView falls back to live behaviour while grouping is active, in manual mode, or with tree data (getSubRows); the view is not held.");
753
+ }
754
+ };
482
755
  this.expandedTouched = false;
483
756
  this.groupingActiveDefault = () => ((this.grouping != null ? this.grouping : this._groupingDefault.value) || []).length > 0;
484
757
  this.effectiveColumnPinning = () => {
@@ -590,6 +863,7 @@ let DataTable = class DataTable extends SignalWatcher(LitElement) {
590
863
  expandable: c.expandable === true,
591
864
  groupable: c.groupable !== false,
592
865
  aggregationFn: wrapAggregationFn(c.aggregationFn),
866
+ ...columnRuleKeys(c, id),
593
867
  pinned: c.pinned != null ? c.pinned : "",
594
868
  width: c.width != null ? c.width : "",
595
869
  ...this.parseWidthToSize(c.width) != null ? { size: this.parseWidthToSize(c.width) } : {},
@@ -633,6 +907,7 @@ let DataTable = class DataTable extends SignalWatcher(LitElement) {
633
907
  expandable: spec.expandable === true,
634
908
  groupable: spec.groupable !== false,
635
909
  aggregationFn: wrapAggregationFn(spec.aggregationFn),
910
+ ...columnRuleKeys(spec, id),
636
911
  pinned: spec.pinned != null ? spec.pinned : "",
637
912
  width: spec.width != null ? spec.width : "",
638
913
  ...this.parseWidthToSize(spec.width) != null ? { size: this.parseWidthToSize(spec.width) } : {},
@@ -831,6 +1106,9 @@ let DataTable = class DataTable extends SignalWatcher(LitElement) {
831
1106
  this.recordSnapshot(this.currentData());
832
1107
  this.emitHistoryChangeIfEdged(prevU, prevR);
833
1108
  }
1109
+ if (this.table) this.holdCtrl.capture(this.table);
1110
+ this.holdCtrl.noteWrite(this.currentData() || [], Array.isArray(next) ? next : []);
1111
+ this.pendingWrite = true;
834
1112
  const fresh = Array.isArray(next) ? next.slice() : next;
835
1113
  try {
836
1114
  Object.defineProperty(fresh, this.DATA_WRITE_TOKEN_KEY, {
@@ -1434,13 +1712,60 @@ let DataTable = class DataTable extends SignalWatcher(LitElement) {
1434
1712
  if (this.selectionMode === "none") return false;
1435
1713
  return this.lazyRowsActive() ? (row) => !this.rowIsLazyPlaceholder(row) : true;
1436
1714
  };
1715
+ this.writePaginationSoon = (next) => {
1716
+ if (!this.programmatic) {
1717
+ this.writePagination(next);
1718
+ return;
1719
+ }
1720
+ const run = () => this.writePagination(next);
1721
+ if (typeof queueMicrotask !== "undefined") queueMicrotask(run);
1722
+ else Promise.resolve().then(run);
1723
+ };
1437
1724
  this.reFeed = () => {
1438
1725
  if (!this.table) return;
1726
+ const st0 = this.currentState();
1727
+ this.syncHoldActive(st0);
1728
+ let st = st0;
1729
+ let pageReset = null;
1730
+ const groupingChanged = st0.grouping !== this.lastPageGrouping;
1731
+ this.lastPageGrouping = st0.grouping;
1732
+ const viewChanged = st0.sorting !== this.lastHoldSorting || st0.columnFilters !== this.lastHoldColumnFilters || st0.globalFilter !== this.lastHoldGlobalFilter;
1733
+ if (viewChanged) {
1734
+ this.lastHoldSorting = st0.sorting;
1735
+ this.lastHoldColumnFilters = st0.columnFilters;
1736
+ this.lastHoldGlobalFilter = st0.globalFilter;
1737
+ this.holdCtrl.release();
1738
+ this.pendingViewChange = true;
1739
+ }
1740
+ if ((viewChanged || groupingChanged) && this.manual !== true && st0.pagination && st0.pagination.pageIndex !== 0) {
1741
+ pageReset = {
1742
+ pageIndex: 0,
1743
+ pageSize: st0.pagination.pageSize
1744
+ };
1745
+ st = {
1746
+ ...st0,
1747
+ pagination: pageReset
1748
+ };
1749
+ }
1750
+ const cols = this.tableColumns();
1751
+ const colSig = this.columnSetSignature(cols);
1752
+ if (colSig !== this.lastColumnSig) {
1753
+ this.lastColumnSig = colSig;
1754
+ st = {
1755
+ ...st,
1756
+ sorting: [...st.sorting || []],
1757
+ columnFilters: [...st.columnFilters || []]
1758
+ };
1759
+ }
1760
+ if (this.holdCtrl.isHolding() && this.isExternalSwap()) this.holdCtrl.release();
1761
+ if (this.pendingWrite && this.isExternalSwap()) this.pendingWrite = false;
1439
1762
  this.table.setOptions((prev) => ({
1440
1763
  ...prev,
1441
1764
  data: this.feedData(),
1442
- columns: this.tableColumns(),
1443
- state: this.currentState(),
1765
+ columns: cols,
1766
+ state: st,
1767
+ getFilteredRowModel: this.heldFilteredRowModel,
1768
+ getSortedRowModel: this.heldSortedRowModel,
1444
1769
  enableRowSelection: this.rowSelectionOption(),
1445
1770
  enableMultiRowSelection: this.selectionMode === "multiple",
1446
1771
  rowCount: this.rowCount ?? void 0,
@@ -1468,15 +1793,19 @@ let DataTable = class DataTable extends SignalWatcher(LitElement) {
1468
1793
  onColumnSizingInfoChange: this.onColumnSizingInfoChangeCb
1469
1794
  }));
1470
1795
  if (this.refreshRowModel) this.refreshRowModel();
1796
+ if (pageReset) this.writePaginationSoon(pageReset);
1471
1797
  };
1472
- this.lastPropsData = null;
1473
1798
  this.maybeClearHistoryOnExternalSwap = () => {
1474
1799
  const pd = this.data;
1475
1800
  if (pd === this.lastPropsData) return;
1476
1801
  this.lastPropsData = pd;
1477
- if (!this.undoable) return;
1478
1802
  if (pd != null && pd[this.DATA_WRITE_TOKEN_KEY] != null) return;
1479
- this.clearHistory();
1803
+ this.pendingWrite = false;
1804
+ if (this.holdCtrl.isHolding()) {
1805
+ this.holdCtrl.release();
1806
+ if (this.refreshRowModel) this.refreshRowModel();
1807
+ }
1808
+ if (this.undoable) this.clearHistory();
1480
1809
  };
1481
1810
  this.lastData = null;
1482
1811
  this.lastDataLen = -1;
@@ -1815,6 +2144,21 @@ let DataTable = class DataTable extends SignalWatcher(LitElement) {
1815
2144
  return abs - this.pageRowOffset();
1816
2145
  };
1817
2146
  this.cut = () => this.cutRange();
2147
+ this.refreshView = () => {
2148
+ this.holdCtrl.release();
2149
+ this.pendingViewChange = true;
2150
+ if (!this.table) return;
2151
+ this.table.setOptions((p) => ({
2152
+ ...p,
2153
+ state: {
2154
+ ...p.state,
2155
+ columnFilters: [...p.state.columnFilters || []],
2156
+ sorting: [...p.state.sorting || []]
2157
+ }
2158
+ }));
2159
+ if (this.refreshRowModel) this.refreshRowModel();
2160
+ };
2161
+ this.getViewState = () => this.holdCtrl.viewState(this.table);
1818
2162
  this.isGrid = () => this.interactionMode === "grid";
1819
2163
  this.tableRole = () => this.isGrid() ? "grid" : "table";
1820
2164
  this.cellRole = () => this.isGrid() ? "gridcell" : "cell";
@@ -2325,6 +2669,7 @@ let DataTable = class DataTable extends SignalWatcher(LitElement) {
2325
2669
  if (!tgt || !tgt.closest) return;
2326
2670
  const cellEl = tgt.closest("[data-grid-cell]");
2327
2671
  if (!cellEl) return;
2672
+ this.activeCellEngaged = true;
2328
2673
  const rowAttr = cellEl.getAttribute("data-row");
2329
2674
  const colAttr = cellEl.getAttribute("data-col-index");
2330
2675
  if (rowAttr == null || colAttr == null) return;
@@ -2502,13 +2847,63 @@ let DataTable = class DataTable extends SignalWatcher(LitElement) {
2502
2847
  if (typeof requestAnimationFrame === "function") requestAnimationFrame(tryFocus);
2503
2848
  else setTimeout(tryFocus, 0);
2504
2849
  };
2505
- this.clampActiveCell = (rowCount, colCount) => {
2850
+ this.followOpenEditors = (prevIds, nextIds) => {
2851
+ if (!prevIds || sameIdSequence(prevIds, nextIds)) return false;
2852
+ let owns = false;
2853
+ const er = this._editingRow.value;
2854
+ if (er >= 0) {
2855
+ const ef = resolveFollow(prevIds, er, nextIds, true);
2856
+ if (ef.kind === "move") {
2857
+ this._editingRow.value = ef.index;
2858
+ this.focusEditorWhenReady(ef.index, this._editingCol.value, false);
2859
+ owns = true;
2860
+ } else if (ef.kind !== "stay") {
2861
+ this.committedThisSession = true;
2862
+ this.editTransition = true;
2863
+ this.endEdit();
2864
+ this.editTransition = false;
2865
+ }
2866
+ }
2867
+ const rr = this._editingRowIndex.value;
2868
+ if (rr != null && rr >= 0) {
2869
+ const rf = resolveFollow(prevIds, rr, nextIds, true);
2870
+ if (rf.kind === "move") {
2871
+ this._editingRowIndex.value = rf.index;
2872
+ owns = true;
2873
+ } else if (rf.kind !== "stay") {
2874
+ this.editTransition = true;
2875
+ this.endRowEdit();
2876
+ this.editTransition = false;
2877
+ }
2878
+ }
2879
+ return owns;
2880
+ };
2881
+ this.rowIdInModel = (index) => {
2882
+ const row = this.windowSource()[index];
2883
+ return row ? row.id : null;
2884
+ };
2885
+ this.modelIndexOfRowId = (id) => {
2886
+ const rows = this.windowSource();
2887
+ for (let i = 0; i < rows.length; i++) if (rows[i].id === id) return i;
2888
+ return -1;
2889
+ };
2890
+ this.followActiveCell = (prevIds, nextIds, viewChangeOrWrite, rowCount, colCount, editFollowPending = false) => {
2891
+ const editorOwnsFocus = this.followOpenEditors(prevIds, nextIds);
2892
+ if (!this.isGrid()) return;
2893
+ let follow = null;
2894
+ if (this.activeCellEngaged && rowCount > 0 && !this._activeIsHeader.value && !this.gridEmptyFallback) follow = resolveFollow(prevIds, this._activeRow.value, nextIds, viewChangeOrWrite);
2895
+ const focusEmptyPark = rowCount <= 0 && viewChangeOrWrite;
2896
+ this.clampActiveCell(rowCount, colCount, follow, editFollowPending || editorOwnsFocus, focusEmptyPark);
2897
+ if (viewChangeOrWrite && !sameIdSequence(prevIds, nextIds)) this.clearRange();
2898
+ };
2899
+ this.clampActiveCell = (rowCount, colCount, follow = null, editFollowPending = false, focusEmptyPark = false) => {
2506
2900
  if (!this.isGrid()) return;
2507
2901
  const colN = colCount != null ? colCount : this.visibleColCount();
2508
2902
  const rowN = rowCount != null ? rowCount : this.bodyRowCount();
2509
2903
  let recoverFocus = false;
2510
2904
  let doomedRow = -1;
2511
2905
  let doomedCol = 0;
2906
+ let focusOnBody = false;
2512
2907
  if (this.gridRoot) {
2513
2908
  const rootNode = this.gridRoot.getRootNode ? this.gridRoot.getRootNode() : null;
2514
2909
  const focusedEl = rootNode ? rootNode.activeElement : null;
@@ -2517,6 +2912,7 @@ let DataTable = class DataTable extends SignalWatcher(LitElement) {
2517
2912
  const fRowAttr = focusedCell.getAttribute("data-row");
2518
2913
  const fColAttr = focusedCell.getAttribute("data-col-index");
2519
2914
  if (fRowAttr != null && fRowAttr !== "__header") {
2915
+ focusOnBody = true;
2520
2916
  const fr = parseInt(fRowAttr, 10);
2521
2917
  const fc = parseInt(fColAttr, 10);
2522
2918
  if (Number.isFinite(fr) && fr > rowN - 1) {
@@ -2536,6 +2932,7 @@ let DataTable = class DataTable extends SignalWatcher(LitElement) {
2536
2932
  this._activeColIndex.value = 0;
2537
2933
  this.gridEmptyFallback = true;
2538
2934
  this.clampRange(rowN - 1, colN - 1);
2935
+ if (focusEmptyPark && (focusOnBody || editFollowPending)) this.recoverGridFocus("__header", 0, this.headerLeafLevel());
2539
2936
  return;
2540
2937
  }
2541
2938
  if (this.gridEmptyFallback) {
@@ -2543,6 +2940,39 @@ let DataTable = class DataTable extends SignalWatcher(LitElement) {
2543
2940
  this._activeIsHeader.value = false;
2544
2941
  this._activeRow.value = 0;
2545
2942
  }
2943
+ if (follow && follow.kind === "move") {
2944
+ const nextRow = follow.index;
2945
+ this._activeRow.value = nextRow;
2946
+ this.clampRange(rowN - 1, colN - 1);
2947
+ this.dispatchEvent(new CustomEvent("activecell-change", {
2948
+ detail: {
2949
+ rowIndex: this.toAbsRow(nextRow),
2950
+ colIndex: col,
2951
+ isHeader: false
2952
+ },
2953
+ bubbles: true,
2954
+ composed: true
2955
+ }));
2956
+ if (focusOnBody && !editFollowPending) this.focusCellWhenReady(nextRow, col);
2957
+ return;
2958
+ }
2959
+ if (follow && follow.kind === "park") {
2960
+ const level = this.headerLeafLevel();
2961
+ this._activeIsHeader.value = true;
2962
+ this._activeHeaderLevel.value = level;
2963
+ this.clampRange(rowN - 1, colN - 1);
2964
+ this.dispatchEvent(new CustomEvent("activecell-change", {
2965
+ detail: {
2966
+ rowIndex: null,
2967
+ colIndex: col,
2968
+ isHeader: true
2969
+ },
2970
+ bubbles: true,
2971
+ composed: true
2972
+ }));
2973
+ if (focusOnBody || editFollowPending) this.recoverGridFocus("__header", col, level);
2974
+ return;
2975
+ }
2546
2976
  if (!this._activeIsHeader.value) {
2547
2977
  const lastRow = rowN - 1;
2548
2978
  const maxRow = lastRow < 0 ? 0 : lastRow;
@@ -2606,6 +3036,7 @@ let DataTable = class DataTable extends SignalWatcher(LitElement) {
2606
3036
  this.colVirtualizer._willUpdate();
2607
3037
  };
2608
3038
  this.gridEmptyFallback = false;
3039
+ this.activeCellEngaged = false;
2609
3040
  this.rangeTransition = false;
2610
3041
  this.rangeClickPending = null;
2611
3042
  this.rangeActive = false;
@@ -3329,6 +3760,7 @@ let DataTable = class DataTable extends SignalWatcher(LitElement) {
3329
3760
  const colId = this.columnIdAt(rowIndex, colIndex);
3330
3761
  if (colId == null || !this.columnEditable(colId)) return;
3331
3762
  this.committedThisSession = false;
3763
+ this.outsidePointerDown = false;
3332
3764
  this.setInvalid("");
3333
3765
  this._editingRowIndex.value = null;
3334
3766
  this._rowDraft.value = {};
@@ -3784,12 +4216,14 @@ let DataTable = class DataTable extends SignalWatcher(LitElement) {
3784
4216
  const fromRow = this._editingRow.value;
3785
4217
  const fromCol = this._editingCol.value;
3786
4218
  const target = e.shiftKey ? this.prevEditableCell(fromRow, fromCol) : this.nextEditableCell(fromRow, fromCol);
4219
+ const targetRowId = target ? this.rowIdInModel(target.row) : null;
3787
4220
  const committed = this.commitEdit(void 0, true);
3788
- if (committed && target) {
3789
- this._activeRow.value = target.row;
4221
+ const advanceRow = targetRowId != null ? this.modelIndexOfRowId(targetRowId) : -1;
4222
+ if (committed && target && advanceRow >= 0) {
4223
+ this._activeRow.value = advanceRow;
3790
4224
  this._activeColIndex.value = target.col;
3791
- this.beginEdit(target.row, target.col, null);
3792
- } else if (committed) this.focusCellWhenReady(fromRow, fromCol);
4225
+ this.beginEdit(advanceRow, target.col, null);
4226
+ } else if (committed && target) {} else if (committed) this.focusCellWhenReady(fromRow, fromCol);
3793
4227
  } else if (key === "Escape") {
3794
4228
  e.preventDefault();
3795
4229
  this.cancelEdit();
@@ -4678,6 +5112,7 @@ let DataTable = class DataTable extends SignalWatcher(LitElement) {
4678
5112
  this.columnOrder,
4679
5113
  this.columnPinning,
4680
5114
  this.selectionMode,
5115
+ this.holdView,
4681
5116
  (this.data || []).length,
4682
5117
  this.data,
4683
5118
  this._dataDefault.value,
@@ -4734,13 +5169,15 @@ let DataTable = class DataTable extends SignalWatcher(LitElement) {
4734
5169
  }));
4735
5170
  this._dataDefault.value = this.data || [];
4736
5171
  this.seedColumnPinning();
5172
+ const cols0 = this.tableColumns();
5173
+ this.lastColumnSig = this.columnSetSignature(cols0);
4737
5174
  this.table = createTable({
4738
5175
  data: this.feedData(),
4739
- columns: this.tableColumns(),
5176
+ columns: cols0,
4740
5177
  state: this.currentState(),
4741
5178
  getCoreRowModel: getCoreRowModel(),
4742
- getSortedRowModel: getSortedRowModel(),
4743
- getFilteredRowModel: getFilteredRowModel(),
5179
+ getSortedRowModel: this.heldSortedRowModel,
5180
+ getFilteredRowModel: this.heldFilteredRowModel,
4744
5181
  getPaginationRowModel: getPaginationRowModel(),
4745
5182
  getExpandedRowModel: getExpandedRowModel(),
4746
5183
  getSubRows: this.getSubRows || void 0,
@@ -4775,6 +5212,21 @@ let DataTable = class DataTable extends SignalWatcher(LitElement) {
4775
5212
  renderFallbackValue: null,
4776
5213
  onStateChange: () => {}
4777
5214
  });
5215
+ this.table.resetPageIndex = () => {
5216
+ if (!this.table) return;
5217
+ const pg = this.table.getState().pagination;
5218
+ this.writePagination({
5219
+ pageIndex: pg.pageIndex,
5220
+ pageSize: pg.pageSize
5221
+ });
5222
+ };
5223
+ const st0 = this.currentState();
5224
+ this.lastHoldSorting = st0.sorting;
5225
+ this.lastHoldColumnFilters = st0.columnFilters;
5226
+ this.lastHoldGlobalFilter = st0.globalFilter;
5227
+ this.lastPageGrouping = st0.grouping;
5228
+ this.syncHoldActive(st0);
5229
+ this.lastPropsData = this.data;
4778
5230
  this.refreshRowModel = () => {
4779
5231
  if (!this.table) return;
4780
5232
  const nextRows = this.windowSource().slice();
@@ -4790,7 +5242,16 @@ let DataTable = class DataTable extends SignalWatcher(LitElement) {
4790
5242
  this.remeasureColumnWindow();
4791
5243
  const nextRowCount = nextRows.length;
4792
5244
  const nextColCount = nextRows.length ? nextRows[0].getVisibleCells().length : nextGroups.length ? (nextGroups[nextGroups.length - 1].headers || []).length : 0;
4793
- this.clampActiveCell(nextRowCount, nextColCount);
5245
+ const nextIds = nextRows.map((r) => r.id);
5246
+ const fedData = this.table.options.data;
5247
+ const dataChanged = fedData !== this.lastFedData;
5248
+ this.lastFedData = fedData;
5249
+ const viewChangeOrWrite = this.pendingViewChange || this.pendingWrite && dataChanged;
5250
+ const swapWithoutIds = dataChanged && !this.pendingWrite && !this.pendingViewChange && this.table.options.getRowId == null;
5251
+ this.pendingViewChange = false;
5252
+ if (dataChanged) this.pendingWrite = false;
5253
+ this.followActiveCell(swapWithoutIds ? null : this.prevRowIds, nextIds, viewChangeOrWrite, nextRowCount, nextColCount, this.pendingEditFollow != null);
5254
+ this.prevRowIds = nextIds;
4794
5255
  const pgState = this.table.getState().pagination;
4795
5256
  const pc = this.table.getPageCount();
4796
5257
  if (pc > 0 && pgState.pageIndex > pc - 1) this.writePagination({
@@ -4806,6 +5267,15 @@ let DataTable = class DataTable extends SignalWatcher(LitElement) {
4806
5267
  this.syncIndeterminate();
4807
5268
  if (typeof queueMicrotask !== "undefined") queueMicrotask(this.syncIndeterminate);
4808
5269
  else Promise.resolve().then(this.syncIndeterminate);
5270
+ const vs = this.holdCtrl.viewState(this.table);
5271
+ if (!viewStatesEqual(vs, this.lastViewState)) {
5272
+ this.lastViewState = vs;
5273
+ this.dispatchEvent(new CustomEvent("view-state-change", {
5274
+ detail: vs,
5275
+ bubbles: true,
5276
+ composed: true
5277
+ }));
5278
+ }
4809
5279
  };
4810
5280
  this.refreshRowModel();
4811
5281
  this.gridRoot = this._ref__rozieRoot ? this._ref__rozieRoot.querySelector(".rozie-data-table") : null;
@@ -4853,7 +5323,7 @@ let DataTable = class DataTable extends SignalWatcher(LitElement) {
4853
5323
  this.announceState.globalFilter = this.effectiveGlobalFilter();
4854
5324
  }
4855
5325
  updated(changedProperties) {
4856
- if (this.__rozieFirstUpdateDone && (changedProperties.has("rowCount") || changedProperties.has("pageCount") || changedProperties.has("expandable") || changedProperties.has("groupable") || changedProperties.has("selectionMode") || changedProperties.has("columns"))) {
5326
+ if (this.__rozieFirstUpdateDone && (changedProperties.has("rowCount") || changedProperties.has("pageCount") || changedProperties.has("expandable") || changedProperties.has("groupable") || changedProperties.has("selectionMode") || changedProperties.has("holdView") || changedProperties.has("columns"))) {
4857
5327
  const __watchVal = untracked(() => [
4858
5328
  this.sorting,
4859
5329
  this.globalFilter,
@@ -4871,6 +5341,7 @@ let DataTable = class DataTable extends SignalWatcher(LitElement) {
4871
5341
  this.columnOrder,
4872
5342
  this.columnPinning,
4873
5343
  this.selectionMode,
5344
+ this.holdView,
4874
5345
  (this.data || []).length,
4875
5346
  this.data,
4876
5347
  this._dataDefault.value,
@@ -6065,6 +6536,11 @@ __decorate([property({
6065
6536
  reflect: true,
6066
6537
  attribute: "undo-limit"
6067
6538
  })], DataTable.prototype, "undoLimit", void 0);
6539
+ __decorate([property({
6540
+ type: Boolean,
6541
+ reflect: true,
6542
+ attribute: "hold-view"
6543
+ })], DataTable.prototype, "holdView", void 0);
6068
6544
  __decorate([property({ converter: { fromAttribute: (v) => v === null ? false : v === "true" ? true : v === "false" ? false : v === "" ? true : v } })], DataTable.prototype, "virtual", void 0);
6069
6545
  __decorate([property({
6070
6546
  type: Number,
@@ -6165,6 +6641,8 @@ let Column$1 = class Column extends SignalWatcher(LitElement) {
6165
6641
  this.expandable = false;
6166
6642
  this.groupable = true;
6167
6643
  this.aggregationFn = null;
6644
+ this.filterFn = null;
6645
+ this.sortingFn = null;
6168
6646
  this.editable = false;
6169
6647
  this.editor = "text";
6170
6648
  this.editorOptions = [];
@@ -6178,23 +6656,36 @@ let Column$1 = class Column extends SignalWatcher(LitElement) {
6178
6656
  this._rozieTornDown = false;
6179
6657
  this.reg = null;
6180
6658
  this.registered = false;
6181
- this.colId = () => this.id !== "" ? this.id : this.field;
6182
- this.buildSpec = () => ({
6183
- id: this.colId(),
6184
- field: this.field !== "" ? this.field : this.colId(),
6185
- header: this.header,
6186
- sortable: this.sortable,
6187
- filterable: this.filterable,
6188
- pinned: this.pinned,
6189
- width: this.width,
6190
- expandable: this.expandable,
6191
- groupable: this.groupable,
6192
- aggregationFn: this.aggregationFn,
6193
- editable: this.editable,
6194
- editor: this.editor,
6195
- editorOptions: this.editorOptions,
6196
- validate: this.validate
6659
+ this.latestFilterFn = null;
6660
+ this.latestSortingFn = null;
6661
+ this.filterTrampoline = Object.assign((...args) => typeof this.latestFilterFn === "function" ? this.latestFilterFn(...args) : true, {
6662
+ autoRemove: (value, column) => typeof this.latestFilterFn === "function" && typeof this.latestFilterFn.autoRemove === "function" ? this.latestFilterFn.autoRemove(value, column) : false,
6663
+ resolveFilterValue: (value) => typeof this.latestFilterFn === "function" && typeof this.latestFilterFn.resolveFilterValue === "function" ? this.latestFilterFn.resolveFilterValue(value) : value
6197
6664
  });
6665
+ this.sortingTrampoline = (...args) => typeof this.latestSortingFn === "function" ? this.latestSortingFn(...args) : 0;
6666
+ this.colId = () => this.id !== "" ? this.id : this.field;
6667
+ this.buildSpec = () => {
6668
+ this.latestFilterFn = this.filterFn;
6669
+ this.latestSortingFn = this.sortingFn;
6670
+ return {
6671
+ id: this.colId(),
6672
+ field: this.field !== "" ? this.field : this.colId(),
6673
+ header: this.header,
6674
+ sortable: this.sortable,
6675
+ filterable: this.filterable,
6676
+ pinned: this.pinned,
6677
+ width: this.width,
6678
+ expandable: this.expandable,
6679
+ groupable: this.groupable,
6680
+ aggregationFn: this.aggregationFn,
6681
+ filterFn: typeof this.filterFn === "function" ? this.filterTrampoline : this.filterFn,
6682
+ sortingFn: typeof this.sortingFn === "function" ? this.sortingTrampoline : this.sortingFn,
6683
+ editable: this.editable,
6684
+ editor: this.editor,
6685
+ editorOptions: this.editorOptions,
6686
+ validate: this.validate
6687
+ };
6688
+ };
6198
6689
  }
6199
6690
  static {
6200
6691
  this.styles = css`
@@ -6215,9 +6706,11 @@ let Column$1 = class Column extends SignalWatcher(LitElement) {
6215
6706
  }
6216
6707
  }
6217
6708
  updated(changedProperties) {
6218
- if (this.__rozieFirstUpdateDone && (changedProperties.has("id") || changedProperties.has("field") || changedProperties.has("header") || changedProperties.has("sortable") || changedProperties.has("filterable") || changedProperties.has("pinned") || changedProperties.has("width") || changedProperties.has("expandable") || changedProperties.has("groupable") || changedProperties.has("aggregationFn") || changedProperties.has("editable") || changedProperties.has("editor") || changedProperties.has("editorOptions") || changedProperties.has("validate"))) {
6219
- this.id, this.field, this.header, this.sortable, this.filterable, this.pinned, this.width, this.expandable, this.groupable, this.aggregationFn, this.editable, this.editor, this.editorOptions, this.validate;
6709
+ if (this.__rozieFirstUpdateDone && (changedProperties.has("id") || changedProperties.has("field") || changedProperties.has("header") || changedProperties.has("sortable") || changedProperties.has("filterable") || changedProperties.has("pinned") || changedProperties.has("width") || changedProperties.has("expandable") || changedProperties.has("groupable") || changedProperties.has("aggregationFn") || changedProperties.has("filterFn") || changedProperties.has("sortingFn") || changedProperties.has("editable") || changedProperties.has("editor") || changedProperties.has("editorOptions") || changedProperties.has("validate"))) {
6710
+ this.id, this.field, this.header, this.sortable, this.filterable, this.pinned, this.width, this.expandable, this.groupable, this.aggregationFn, this.filterFn, this.sortingFn, this.editable, this.editor, this.editorOptions, this.validate;
6220
6711
  (() => {
6712
+ this.latestFilterFn = this.filterFn;
6713
+ this.latestSortingFn = this.sortingFn;
6221
6714
  if (this.reg) this.reg.registerColumn(this.colId(), this.buildSpec());
6222
6715
  })();
6223
6716
  }
@@ -6284,6 +6777,14 @@ __decorate([property({
6284
6777
  type: String,
6285
6778
  attribute: "aggregation-fn"
6286
6779
  })], Column$1.prototype, "aggregationFn", void 0);
6780
+ __decorate([property({
6781
+ type: String,
6782
+ attribute: "filter-fn"
6783
+ })], Column$1.prototype, "filterFn", void 0);
6784
+ __decorate([property({
6785
+ type: String,
6786
+ attribute: "sorting-fn"
6787
+ })], Column$1.prototype, "sortingFn", void 0);
6287
6788
  __decorate([property({
6288
6789
  type: Boolean,
6289
6790
  reflect: true