@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.cjs CHANGED
@@ -100,6 +100,74 @@ const columnSpecsEquivalent = (a, b, depth) => {
100
100
  return true;
101
101
  };
102
102
  //#endregion
103
+ //#region src/helpers/columnRuleUtils.ts
104
+ const FILTER_FN_NAMES = [
105
+ "includesString",
106
+ "includesStringSensitive",
107
+ "equalsString",
108
+ "arrIncludes",
109
+ "arrIncludesAll",
110
+ "arrIncludesSome",
111
+ "equals",
112
+ "weakEquals",
113
+ "inNumberRange"
114
+ ];
115
+ const SORTING_FN_NAMES = [
116
+ "alphanumeric",
117
+ "alphanumericCaseSensitive",
118
+ "text",
119
+ "textCaseSensitive",
120
+ "datetime",
121
+ "basic"
122
+ ];
123
+ const warned = /* @__PURE__ */ new Set();
124
+ const resolveName = (value, names, kind, columnId, warn) => {
125
+ if (value === "auto" || names.includes(value)) return value;
126
+ const key = kind + "\0" + columnId + "\0" + value;
127
+ if (!warned.has(key)) {
128
+ warned.add(key);
129
+ warn(`[data-table] column "${columnId}": unknown ${kind} "${value}"; falling back to "auto".`);
130
+ }
131
+ return "auto";
132
+ };
133
+ const resolveFilterFn = (value, columnId, warn = console.warn) => {
134
+ if (value == null) return void 0;
135
+ if (typeof value === "string") return resolveName(value, FILTER_FN_NAMES, "filterFn", columnId, warn);
136
+ if (typeof value !== "function") return void 0;
137
+ const fn = value;
138
+ const adapter = (row, colId, filterValue) => {
139
+ try {
140
+ return fn(row.getValue(colId), filterValue, row.original);
141
+ } catch (err) {
142
+ return true;
143
+ }
144
+ };
145
+ if (fn.autoRemove !== void 0) adapter.autoRemove = fn.autoRemove;
146
+ if (fn.resolveFilterValue !== void 0) adapter.resolveFilterValue = fn.resolveFilterValue;
147
+ return adapter;
148
+ };
149
+ const resolveSortingFn = (value, columnId, warn = console.warn) => {
150
+ if (value == null) return void 0;
151
+ if (typeof value === "string") return resolveName(value, SORTING_FN_NAMES, "sortingFn", columnId, warn);
152
+ if (typeof value !== "function") return void 0;
153
+ const fn = value;
154
+ return (rowA, rowB, colId) => {
155
+ try {
156
+ return fn(rowA.getValue(colId), rowB.getValue(colId), rowA.original, rowB.original);
157
+ } catch (err) {
158
+ return 0;
159
+ }
160
+ };
161
+ };
162
+ const columnRuleKeys = (spec, columnId, warn) => {
163
+ const out = {};
164
+ const f = resolveFilterFn(spec?.filterFn, columnId, warn);
165
+ if (f !== void 0) out.filterFn = f;
166
+ const s = resolveSortingFn(spec?.sortingFn, columnId, warn);
167
+ if (s !== void 0) out.sortingFn = s;
168
+ return out;
169
+ };
170
+ //#endregion
103
171
  //#region src/helpers/indexMath.ts
104
172
  const clamp = (v, lo, hi) => v < lo ? lo : v > hi ? hi : v;
105
173
  const focusables = (cellEl) => {
@@ -108,6 +176,24 @@ const focusables = (cellEl) => {
108
176
  };
109
177
  const applyUpdater = (updater, current) => typeof updater === "function" ? updater(current) : updater;
110
178
  //#endregion
179
+ //#region src/helpers/activeFollow.ts
180
+ function resolveFollow(prevIds, activeRow, nextIds, viewChangeOrWrite) {
181
+ if (!prevIds || !Number.isInteger(activeRow) || activeRow < 0 || activeRow >= prevIds.length) return { kind: "clamp" };
182
+ const id = prevIds[activeRow];
183
+ if (nextIds[activeRow] === id) return { kind: "stay" };
184
+ const index = nextIds.indexOf(id);
185
+ if (index >= 0) return {
186
+ kind: "move",
187
+ index
188
+ };
189
+ return viewChangeOrWrite ? { kind: "park" } : { kind: "clamp" };
190
+ }
191
+ function sameIdSequence(a, b) {
192
+ if (!a || a.length !== b.length) return false;
193
+ for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
194
+ return true;
195
+ }
196
+ //#endregion
111
197
  //#region src/helpers/tsvGrid.ts
112
198
  const escapeTsvField = (s) => {
113
199
  if (s.indexOf(" ") >= 0 || s.indexOf("\n") >= 0 || s.indexOf("\r") >= 0 || s.indexOf("\"") >= 0) return "\"" + s.replace(/"/g, "\"\"") + "\"";
@@ -243,6 +329,150 @@ const indexOfRowIn = (rows, rowOriginal, rowId) => {
243
329
  return -1;
244
330
  };
245
331
  //#endregion
332
+ //#region src/helpers/heldView.ts
333
+ const CLEAN_VIEW_STATE = Object.freeze({
334
+ held: false,
335
+ filterMismatches: 0,
336
+ sortMismatches: 0
337
+ });
338
+ const viewStatesEqual = (a, b) => a.held === b.held && a.filterMismatches === b.filterMismatches && a.sortMismatches === b.sortMismatches;
339
+ const changedIndices = (prev, next) => {
340
+ const out = [];
341
+ if (prev.length !== next.length) {
342
+ const n = Math.max(prev.length, next.length);
343
+ for (let i = 0; i < n; i++) out.push(i);
344
+ return out;
345
+ }
346
+ for (let i = 0; i < next.length; i++) if (prev[i] !== next[i]) out.push(i);
347
+ return out;
348
+ };
349
+ const rowIds = (model) => (model?.rows ?? []).map((r) => r.id);
350
+ const createHoldController = () => {
351
+ let active = false;
352
+ let heldMembership = null;
353
+ let heldOrder = null;
354
+ const written = /* @__PURE__ */ new Set();
355
+ let version = 0;
356
+ let liveFilteredIds = /* @__PURE__ */ new Set();
357
+ let liveSortedIds = [];
358
+ const release = () => {
359
+ heldMembership = null;
360
+ heldOrder = null;
361
+ written.clear();
362
+ version++;
363
+ };
364
+ const wrapFiltered = (base) => (table) => {
365
+ const getBase = base(table);
366
+ let lastBase = void 0;
367
+ let lastVersion = -1;
368
+ let lastOut = void 0;
369
+ return () => {
370
+ const baseModel = getBase();
371
+ if (!heldMembership) return baseModel;
372
+ if (baseModel === lastBase && version === lastVersion) return lastOut;
373
+ liveFilteredIds = new Set(rowIds(baseModel));
374
+ const held = heldMembership;
375
+ const rows = table.getCoreRowModel().rows.filter((r) => held.has(r.id));
376
+ const rowsById = {};
377
+ for (const r of rows) rowsById[r.id] = r;
378
+ lastBase = baseModel;
379
+ lastVersion = version;
380
+ lastOut = {
381
+ rows,
382
+ flatRows: rows,
383
+ rowsById
384
+ };
385
+ return lastOut;
386
+ };
387
+ };
388
+ const wrapSorted = (base) => (table) => {
389
+ const getBase = base(table);
390
+ let lastBase = void 0;
391
+ let lastVersion = -1;
392
+ let lastOut = void 0;
393
+ return () => {
394
+ const baseModel = getBase();
395
+ if (!heldOrder) return baseModel;
396
+ if (baseModel === lastBase && version === lastVersion) return lastOut;
397
+ const baseRows = baseModel?.rows ?? [];
398
+ liveSortedIds = baseRows.map((r) => r.id);
399
+ const byId = /* @__PURE__ */ new Map();
400
+ for (const r of baseRows) byId.set(r.id, r);
401
+ const rows = [];
402
+ for (const id of heldOrder) {
403
+ const r = byId.get(id);
404
+ if (r) {
405
+ rows.push(r);
406
+ byId.delete(id);
407
+ }
408
+ }
409
+ for (const r of baseRows) if (byId.has(r.id)) rows.push(r);
410
+ lastBase = baseModel;
411
+ lastVersion = version;
412
+ lastOut = {
413
+ rows,
414
+ flatRows: rows,
415
+ rowsById: baseModel?.rowsById ?? {}
416
+ };
417
+ return lastOut;
418
+ };
419
+ };
420
+ return {
421
+ setActive(next) {
422
+ const was = active;
423
+ active = !!next;
424
+ if (!active && was) release();
425
+ },
426
+ isActive: () => active,
427
+ isHolding: () => heldMembership !== null,
428
+ capture(table) {
429
+ if (!active || heldMembership) return;
430
+ const filtered = table.getFilteredRowModel();
431
+ const sorted = typeof table.getSortedRowModel === "function" ? table.getSortedRowModel() : filtered;
432
+ heldMembership = new Set(rowIds(filtered));
433
+ heldOrder = rowIds(sorted);
434
+ written.clear();
435
+ version++;
436
+ },
437
+ release,
438
+ noteWrite(prev, next) {
439
+ if (!heldMembership) return;
440
+ for (const i of changedIndices(prev, next)) written.add(i);
441
+ },
442
+ wrapFiltered,
443
+ wrapSorted,
444
+ viewState(table) {
445
+ if (!active) return CLEAN_VIEW_STATE;
446
+ if (!heldMembership || !heldOrder) return {
447
+ held: true,
448
+ filterMismatches: 0,
449
+ sortMismatches: 0
450
+ };
451
+ table.getSortedRowModel();
452
+ const held = heldMembership;
453
+ let filterMismatches = 0;
454
+ for (const id of held) if (!liveFilteredIds.has(id)) filterMismatches++;
455
+ const coreRows = table.getCoreRowModel().rows;
456
+ for (const r of coreRows) if (!held.has(r.id) && liveFilteredIds.has(r.id)) filterMismatches++;
457
+ const heldPos = /* @__PURE__ */ new Map();
458
+ heldOrder.forEach((id, i) => heldPos.set(id, i));
459
+ const livePos = /* @__PURE__ */ new Map();
460
+ liveSortedIds.forEach((id, i) => livePos.set(id, i));
461
+ let sortMismatches = 0;
462
+ for (const i of written) {
463
+ const id = coreRows[i]?.id;
464
+ if (id === void 0 || !heldPos.has(id)) continue;
465
+ if (heldPos.get(id) !== livePos.get(id)) sortMismatches++;
466
+ }
467
+ return {
468
+ held: true,
469
+ filterMismatches,
470
+ sortMismatches
471
+ };
472
+ }
473
+ };
474
+ };
475
+ //#endregion
246
476
  //#region \0@oxc-project+runtime@0.127.0/helpers/decorate.js
247
477
  function __decorate(decorators, target, key, desc) {
248
478
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
@@ -365,6 +595,7 @@ let DataTable = class DataTable extends (0, _lit_labs_preact_signals.SignalWatch
365
595
  this.singleClickEdit = false;
366
596
  this.undoable = false;
367
597
  this.undoLimit = 100;
598
+ this.holdView = false;
368
599
  this.virtual = false;
369
600
  this.estimateRowHeight = 40;
370
601
  this.autoMeasure = false;
@@ -483,6 +714,48 @@ let DataTable = class DataTable extends (0, _lit_labs_preact_signals.SignalWatch
483
714
  this.undoStack = [];
484
715
  this.redoStack = [];
485
716
  this.restoringHistory = false;
717
+ this.holdCtrl = createHoldController();
718
+ this.heldFilteredRowModel = this.holdCtrl.wrapFiltered((0, _tanstack_table_core.getFilteredRowModel)());
719
+ this.heldSortedRowModel = this.holdCtrl.wrapSorted((0, _tanstack_table_core.getSortedRowModel)());
720
+ this.pendingViewChange = false;
721
+ this.pendingWrite = false;
722
+ this.prevRowIds = null;
723
+ this.lastFedData = null;
724
+ this.lastViewState = CLEAN_VIEW_STATE;
725
+ this.lastHoldSorting = null;
726
+ this.lastHoldColumnFilters = null;
727
+ this.lastHoldGlobalFilter = null;
728
+ this.holdFallbackWarned = false;
729
+ this.lastPageGrouping = null;
730
+ this.lastColumnSig = "";
731
+ this.columnSetSignature = (defs) => {
732
+ let sig = "";
733
+ const stack = (defs || []).slice().reverse();
734
+ while (stack.length) {
735
+ const d = stack.pop();
736
+ if (!d) continue;
737
+ sig += String(d.id) + (d.enableSorting ? "+" : "") + (d.enableColumnFilter ? "*" : "") + "|";
738
+ if (Array.isArray(d.columns)) for (let i = d.columns.length - 1; i >= 0; i--) stack.push(d.columns[i]);
739
+ }
740
+ return sig;
741
+ };
742
+ this.lastPropsData = null;
743
+ this.isExternalSwap = () => {
744
+ const pd = this.data;
745
+ if (pd === this.lastPropsData) return false;
746
+ return !(pd != null && pd[this.DATA_WRITE_TOKEN_KEY] != null);
747
+ };
748
+ this.syncHoldActive = (st) => {
749
+ const wanted = this.holdView === true;
750
+ const active = wanted && this.manual !== true && this.getSubRows == null && !(st.grouping || []).length;
751
+ const wasHolding = this.holdCtrl.isHolding();
752
+ this.holdCtrl.setActive(active);
753
+ if (wasHolding && !this.holdCtrl.isHolding()) this.pendingViewChange = true;
754
+ if (wanted && !active && !this.holdFallbackWarned) {
755
+ this.holdFallbackWarned = true;
756
+ 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.");
757
+ }
758
+ };
486
759
  this.expandedTouched = false;
487
760
  this.groupingActiveDefault = () => ((this.grouping != null ? this.grouping : this._groupingDefault.value) || []).length > 0;
488
761
  this.effectiveColumnPinning = () => {
@@ -594,6 +867,7 @@ let DataTable = class DataTable extends (0, _lit_labs_preact_signals.SignalWatch
594
867
  expandable: c.expandable === true,
595
868
  groupable: c.groupable !== false,
596
869
  aggregationFn: wrapAggregationFn(c.aggregationFn),
870
+ ...columnRuleKeys(c, id),
597
871
  pinned: c.pinned != null ? c.pinned : "",
598
872
  width: c.width != null ? c.width : "",
599
873
  ...this.parseWidthToSize(c.width) != null ? { size: this.parseWidthToSize(c.width) } : {},
@@ -637,6 +911,7 @@ let DataTable = class DataTable extends (0, _lit_labs_preact_signals.SignalWatch
637
911
  expandable: spec.expandable === true,
638
912
  groupable: spec.groupable !== false,
639
913
  aggregationFn: wrapAggregationFn(spec.aggregationFn),
914
+ ...columnRuleKeys(spec, id),
640
915
  pinned: spec.pinned != null ? spec.pinned : "",
641
916
  width: spec.width != null ? spec.width : "",
642
917
  ...this.parseWidthToSize(spec.width) != null ? { size: this.parseWidthToSize(spec.width) } : {},
@@ -835,6 +1110,9 @@ let DataTable = class DataTable extends (0, _lit_labs_preact_signals.SignalWatch
835
1110
  this.recordSnapshot(this.currentData());
836
1111
  this.emitHistoryChangeIfEdged(prevU, prevR);
837
1112
  }
1113
+ if (this.table) this.holdCtrl.capture(this.table);
1114
+ this.holdCtrl.noteWrite(this.currentData() || [], Array.isArray(next) ? next : []);
1115
+ this.pendingWrite = true;
838
1116
  const fresh = Array.isArray(next) ? next.slice() : next;
839
1117
  try {
840
1118
  Object.defineProperty(fresh, this.DATA_WRITE_TOKEN_KEY, {
@@ -1438,13 +1716,60 @@ let DataTable = class DataTable extends (0, _lit_labs_preact_signals.SignalWatch
1438
1716
  if (this.selectionMode === "none") return false;
1439
1717
  return this.lazyRowsActive() ? (row) => !this.rowIsLazyPlaceholder(row) : true;
1440
1718
  };
1719
+ this.writePaginationSoon = (next) => {
1720
+ if (!this.programmatic) {
1721
+ this.writePagination(next);
1722
+ return;
1723
+ }
1724
+ const run = () => this.writePagination(next);
1725
+ if (typeof queueMicrotask !== "undefined") queueMicrotask(run);
1726
+ else Promise.resolve().then(run);
1727
+ };
1441
1728
  this.reFeed = () => {
1442
1729
  if (!this.table) return;
1730
+ const st0 = this.currentState();
1731
+ this.syncHoldActive(st0);
1732
+ let st = st0;
1733
+ let pageReset = null;
1734
+ const groupingChanged = st0.grouping !== this.lastPageGrouping;
1735
+ this.lastPageGrouping = st0.grouping;
1736
+ const viewChanged = st0.sorting !== this.lastHoldSorting || st0.columnFilters !== this.lastHoldColumnFilters || st0.globalFilter !== this.lastHoldGlobalFilter;
1737
+ if (viewChanged) {
1738
+ this.lastHoldSorting = st0.sorting;
1739
+ this.lastHoldColumnFilters = st0.columnFilters;
1740
+ this.lastHoldGlobalFilter = st0.globalFilter;
1741
+ this.holdCtrl.release();
1742
+ this.pendingViewChange = true;
1743
+ }
1744
+ if ((viewChanged || groupingChanged) && this.manual !== true && st0.pagination && st0.pagination.pageIndex !== 0) {
1745
+ pageReset = {
1746
+ pageIndex: 0,
1747
+ pageSize: st0.pagination.pageSize
1748
+ };
1749
+ st = {
1750
+ ...st0,
1751
+ pagination: pageReset
1752
+ };
1753
+ }
1754
+ const cols = this.tableColumns();
1755
+ const colSig = this.columnSetSignature(cols);
1756
+ if (colSig !== this.lastColumnSig) {
1757
+ this.lastColumnSig = colSig;
1758
+ st = {
1759
+ ...st,
1760
+ sorting: [...st.sorting || []],
1761
+ columnFilters: [...st.columnFilters || []]
1762
+ };
1763
+ }
1764
+ if (this.holdCtrl.isHolding() && this.isExternalSwap()) this.holdCtrl.release();
1765
+ if (this.pendingWrite && this.isExternalSwap()) this.pendingWrite = false;
1443
1766
  this.table.setOptions((prev) => ({
1444
1767
  ...prev,
1445
1768
  data: this.feedData(),
1446
- columns: this.tableColumns(),
1447
- state: this.currentState(),
1769
+ columns: cols,
1770
+ state: st,
1771
+ getFilteredRowModel: this.heldFilteredRowModel,
1772
+ getSortedRowModel: this.heldSortedRowModel,
1448
1773
  enableRowSelection: this.rowSelectionOption(),
1449
1774
  enableMultiRowSelection: this.selectionMode === "multiple",
1450
1775
  rowCount: this.rowCount ?? void 0,
@@ -1472,15 +1797,19 @@ let DataTable = class DataTable extends (0, _lit_labs_preact_signals.SignalWatch
1472
1797
  onColumnSizingInfoChange: this.onColumnSizingInfoChangeCb
1473
1798
  }));
1474
1799
  if (this.refreshRowModel) this.refreshRowModel();
1800
+ if (pageReset) this.writePaginationSoon(pageReset);
1475
1801
  };
1476
- this.lastPropsData = null;
1477
1802
  this.maybeClearHistoryOnExternalSwap = () => {
1478
1803
  const pd = this.data;
1479
1804
  if (pd === this.lastPropsData) return;
1480
1805
  this.lastPropsData = pd;
1481
- if (!this.undoable) return;
1482
1806
  if (pd != null && pd[this.DATA_WRITE_TOKEN_KEY] != null) return;
1483
- this.clearHistory();
1807
+ this.pendingWrite = false;
1808
+ if (this.holdCtrl.isHolding()) {
1809
+ this.holdCtrl.release();
1810
+ if (this.refreshRowModel) this.refreshRowModel();
1811
+ }
1812
+ if (this.undoable) this.clearHistory();
1484
1813
  };
1485
1814
  this.lastData = null;
1486
1815
  this.lastDataLen = -1;
@@ -1819,6 +2148,21 @@ let DataTable = class DataTable extends (0, _lit_labs_preact_signals.SignalWatch
1819
2148
  return abs - this.pageRowOffset();
1820
2149
  };
1821
2150
  this.cut = () => this.cutRange();
2151
+ this.refreshView = () => {
2152
+ this.holdCtrl.release();
2153
+ this.pendingViewChange = true;
2154
+ if (!this.table) return;
2155
+ this.table.setOptions((p) => ({
2156
+ ...p,
2157
+ state: {
2158
+ ...p.state,
2159
+ columnFilters: [...p.state.columnFilters || []],
2160
+ sorting: [...p.state.sorting || []]
2161
+ }
2162
+ }));
2163
+ if (this.refreshRowModel) this.refreshRowModel();
2164
+ };
2165
+ this.getViewState = () => this.holdCtrl.viewState(this.table);
1822
2166
  this.isGrid = () => this.interactionMode === "grid";
1823
2167
  this.tableRole = () => this.isGrid() ? "grid" : "table";
1824
2168
  this.cellRole = () => this.isGrid() ? "gridcell" : "cell";
@@ -2329,6 +2673,7 @@ let DataTable = class DataTable extends (0, _lit_labs_preact_signals.SignalWatch
2329
2673
  if (!tgt || !tgt.closest) return;
2330
2674
  const cellEl = tgt.closest("[data-grid-cell]");
2331
2675
  if (!cellEl) return;
2676
+ this.activeCellEngaged = true;
2332
2677
  const rowAttr = cellEl.getAttribute("data-row");
2333
2678
  const colAttr = cellEl.getAttribute("data-col-index");
2334
2679
  if (rowAttr == null || colAttr == null) return;
@@ -2506,13 +2851,63 @@ let DataTable = class DataTable extends (0, _lit_labs_preact_signals.SignalWatch
2506
2851
  if (typeof requestAnimationFrame === "function") requestAnimationFrame(tryFocus);
2507
2852
  else setTimeout(tryFocus, 0);
2508
2853
  };
2509
- this.clampActiveCell = (rowCount, colCount) => {
2854
+ this.followOpenEditors = (prevIds, nextIds) => {
2855
+ if (!prevIds || sameIdSequence(prevIds, nextIds)) return false;
2856
+ let owns = false;
2857
+ const er = this._editingRow.value;
2858
+ if (er >= 0) {
2859
+ const ef = resolveFollow(prevIds, er, nextIds, true);
2860
+ if (ef.kind === "move") {
2861
+ this._editingRow.value = ef.index;
2862
+ this.focusEditorWhenReady(ef.index, this._editingCol.value, false);
2863
+ owns = true;
2864
+ } else if (ef.kind !== "stay") {
2865
+ this.committedThisSession = true;
2866
+ this.editTransition = true;
2867
+ this.endEdit();
2868
+ this.editTransition = false;
2869
+ }
2870
+ }
2871
+ const rr = this._editingRowIndex.value;
2872
+ if (rr != null && rr >= 0) {
2873
+ const rf = resolveFollow(prevIds, rr, nextIds, true);
2874
+ if (rf.kind === "move") {
2875
+ this._editingRowIndex.value = rf.index;
2876
+ owns = true;
2877
+ } else if (rf.kind !== "stay") {
2878
+ this.editTransition = true;
2879
+ this.endRowEdit();
2880
+ this.editTransition = false;
2881
+ }
2882
+ }
2883
+ return owns;
2884
+ };
2885
+ this.rowIdInModel = (index) => {
2886
+ const row = this.windowSource()[index];
2887
+ return row ? row.id : null;
2888
+ };
2889
+ this.modelIndexOfRowId = (id) => {
2890
+ const rows = this.windowSource();
2891
+ for (let i = 0; i < rows.length; i++) if (rows[i].id === id) return i;
2892
+ return -1;
2893
+ };
2894
+ this.followActiveCell = (prevIds, nextIds, viewChangeOrWrite, rowCount, colCount, editFollowPending = false) => {
2895
+ const editorOwnsFocus = this.followOpenEditors(prevIds, nextIds);
2896
+ if (!this.isGrid()) return;
2897
+ let follow = null;
2898
+ if (this.activeCellEngaged && rowCount > 0 && !this._activeIsHeader.value && !this.gridEmptyFallback) follow = resolveFollow(prevIds, this._activeRow.value, nextIds, viewChangeOrWrite);
2899
+ const focusEmptyPark = rowCount <= 0 && viewChangeOrWrite;
2900
+ this.clampActiveCell(rowCount, colCount, follow, editFollowPending || editorOwnsFocus, focusEmptyPark);
2901
+ if (viewChangeOrWrite && !sameIdSequence(prevIds, nextIds)) this.clearRange();
2902
+ };
2903
+ this.clampActiveCell = (rowCount, colCount, follow = null, editFollowPending = false, focusEmptyPark = false) => {
2510
2904
  if (!this.isGrid()) return;
2511
2905
  const colN = colCount != null ? colCount : this.visibleColCount();
2512
2906
  const rowN = rowCount != null ? rowCount : this.bodyRowCount();
2513
2907
  let recoverFocus = false;
2514
2908
  let doomedRow = -1;
2515
2909
  let doomedCol = 0;
2910
+ let focusOnBody = false;
2516
2911
  if (this.gridRoot) {
2517
2912
  const rootNode = this.gridRoot.getRootNode ? this.gridRoot.getRootNode() : null;
2518
2913
  const focusedEl = rootNode ? rootNode.activeElement : null;
@@ -2521,6 +2916,7 @@ let DataTable = class DataTable extends (0, _lit_labs_preact_signals.SignalWatch
2521
2916
  const fRowAttr = focusedCell.getAttribute("data-row");
2522
2917
  const fColAttr = focusedCell.getAttribute("data-col-index");
2523
2918
  if (fRowAttr != null && fRowAttr !== "__header") {
2919
+ focusOnBody = true;
2524
2920
  const fr = parseInt(fRowAttr, 10);
2525
2921
  const fc = parseInt(fColAttr, 10);
2526
2922
  if (Number.isFinite(fr) && fr > rowN - 1) {
@@ -2540,6 +2936,7 @@ let DataTable = class DataTable extends (0, _lit_labs_preact_signals.SignalWatch
2540
2936
  this._activeColIndex.value = 0;
2541
2937
  this.gridEmptyFallback = true;
2542
2938
  this.clampRange(rowN - 1, colN - 1);
2939
+ if (focusEmptyPark && (focusOnBody || editFollowPending)) this.recoverGridFocus("__header", 0, this.headerLeafLevel());
2543
2940
  return;
2544
2941
  }
2545
2942
  if (this.gridEmptyFallback) {
@@ -2547,6 +2944,39 @@ let DataTable = class DataTable extends (0, _lit_labs_preact_signals.SignalWatch
2547
2944
  this._activeIsHeader.value = false;
2548
2945
  this._activeRow.value = 0;
2549
2946
  }
2947
+ if (follow && follow.kind === "move") {
2948
+ const nextRow = follow.index;
2949
+ this._activeRow.value = nextRow;
2950
+ this.clampRange(rowN - 1, colN - 1);
2951
+ this.dispatchEvent(new CustomEvent("activecell-change", {
2952
+ detail: {
2953
+ rowIndex: this.toAbsRow(nextRow),
2954
+ colIndex: col,
2955
+ isHeader: false
2956
+ },
2957
+ bubbles: true,
2958
+ composed: true
2959
+ }));
2960
+ if (focusOnBody && !editFollowPending) this.focusCellWhenReady(nextRow, col);
2961
+ return;
2962
+ }
2963
+ if (follow && follow.kind === "park") {
2964
+ const level = this.headerLeafLevel();
2965
+ this._activeIsHeader.value = true;
2966
+ this._activeHeaderLevel.value = level;
2967
+ this.clampRange(rowN - 1, colN - 1);
2968
+ this.dispatchEvent(new CustomEvent("activecell-change", {
2969
+ detail: {
2970
+ rowIndex: null,
2971
+ colIndex: col,
2972
+ isHeader: true
2973
+ },
2974
+ bubbles: true,
2975
+ composed: true
2976
+ }));
2977
+ if (focusOnBody || editFollowPending) this.recoverGridFocus("__header", col, level);
2978
+ return;
2979
+ }
2550
2980
  if (!this._activeIsHeader.value) {
2551
2981
  const lastRow = rowN - 1;
2552
2982
  const maxRow = lastRow < 0 ? 0 : lastRow;
@@ -2610,6 +3040,7 @@ let DataTable = class DataTable extends (0, _lit_labs_preact_signals.SignalWatch
2610
3040
  this.colVirtualizer._willUpdate();
2611
3041
  };
2612
3042
  this.gridEmptyFallback = false;
3043
+ this.activeCellEngaged = false;
2613
3044
  this.rangeTransition = false;
2614
3045
  this.rangeClickPending = null;
2615
3046
  this.rangeActive = false;
@@ -3333,6 +3764,7 @@ let DataTable = class DataTable extends (0, _lit_labs_preact_signals.SignalWatch
3333
3764
  const colId = this.columnIdAt(rowIndex, colIndex);
3334
3765
  if (colId == null || !this.columnEditable(colId)) return;
3335
3766
  this.committedThisSession = false;
3767
+ this.outsidePointerDown = false;
3336
3768
  this.setInvalid("");
3337
3769
  this._editingRowIndex.value = null;
3338
3770
  this._rowDraft.value = {};
@@ -3788,12 +4220,14 @@ let DataTable = class DataTable extends (0, _lit_labs_preact_signals.SignalWatch
3788
4220
  const fromRow = this._editingRow.value;
3789
4221
  const fromCol = this._editingCol.value;
3790
4222
  const target = e.shiftKey ? this.prevEditableCell(fromRow, fromCol) : this.nextEditableCell(fromRow, fromCol);
4223
+ const targetRowId = target ? this.rowIdInModel(target.row) : null;
3791
4224
  const committed = this.commitEdit(void 0, true);
3792
- if (committed && target) {
3793
- this._activeRow.value = target.row;
4225
+ const advanceRow = targetRowId != null ? this.modelIndexOfRowId(targetRowId) : -1;
4226
+ if (committed && target && advanceRow >= 0) {
4227
+ this._activeRow.value = advanceRow;
3794
4228
  this._activeColIndex.value = target.col;
3795
- this.beginEdit(target.row, target.col, null);
3796
- } else if (committed) this.focusCellWhenReady(fromRow, fromCol);
4229
+ this.beginEdit(advanceRow, target.col, null);
4230
+ } else if (committed && target) {} else if (committed) this.focusCellWhenReady(fromRow, fromCol);
3797
4231
  } else if (key === "Escape") {
3798
4232
  e.preventDefault();
3799
4233
  this.cancelEdit();
@@ -4682,6 +5116,7 @@ let DataTable = class DataTable extends (0, _lit_labs_preact_signals.SignalWatch
4682
5116
  this.columnOrder,
4683
5117
  this.columnPinning,
4684
5118
  this.selectionMode,
5119
+ this.holdView,
4685
5120
  (this.data || []).length,
4686
5121
  this.data,
4687
5122
  this._dataDefault.value,
@@ -4738,13 +5173,15 @@ let DataTable = class DataTable extends (0, _lit_labs_preact_signals.SignalWatch
4738
5173
  }));
4739
5174
  this._dataDefault.value = this.data || [];
4740
5175
  this.seedColumnPinning();
5176
+ const cols0 = this.tableColumns();
5177
+ this.lastColumnSig = this.columnSetSignature(cols0);
4741
5178
  this.table = (0, _tanstack_table_core.createTable)({
4742
5179
  data: this.feedData(),
4743
- columns: this.tableColumns(),
5180
+ columns: cols0,
4744
5181
  state: this.currentState(),
4745
5182
  getCoreRowModel: (0, _tanstack_table_core.getCoreRowModel)(),
4746
- getSortedRowModel: (0, _tanstack_table_core.getSortedRowModel)(),
4747
- getFilteredRowModel: (0, _tanstack_table_core.getFilteredRowModel)(),
5183
+ getSortedRowModel: this.heldSortedRowModel,
5184
+ getFilteredRowModel: this.heldFilteredRowModel,
4748
5185
  getPaginationRowModel: (0, _tanstack_table_core.getPaginationRowModel)(),
4749
5186
  getExpandedRowModel: (0, _tanstack_table_core.getExpandedRowModel)(),
4750
5187
  getSubRows: this.getSubRows || void 0,
@@ -4779,6 +5216,21 @@ let DataTable = class DataTable extends (0, _lit_labs_preact_signals.SignalWatch
4779
5216
  renderFallbackValue: null,
4780
5217
  onStateChange: () => {}
4781
5218
  });
5219
+ this.table.resetPageIndex = () => {
5220
+ if (!this.table) return;
5221
+ const pg = this.table.getState().pagination;
5222
+ this.writePagination({
5223
+ pageIndex: pg.pageIndex,
5224
+ pageSize: pg.pageSize
5225
+ });
5226
+ };
5227
+ const st0 = this.currentState();
5228
+ this.lastHoldSorting = st0.sorting;
5229
+ this.lastHoldColumnFilters = st0.columnFilters;
5230
+ this.lastHoldGlobalFilter = st0.globalFilter;
5231
+ this.lastPageGrouping = st0.grouping;
5232
+ this.syncHoldActive(st0);
5233
+ this.lastPropsData = this.data;
4782
5234
  this.refreshRowModel = () => {
4783
5235
  if (!this.table) return;
4784
5236
  const nextRows = this.windowSource().slice();
@@ -4794,7 +5246,16 @@ let DataTable = class DataTable extends (0, _lit_labs_preact_signals.SignalWatch
4794
5246
  this.remeasureColumnWindow();
4795
5247
  const nextRowCount = nextRows.length;
4796
5248
  const nextColCount = nextRows.length ? nextRows[0].getVisibleCells().length : nextGroups.length ? (nextGroups[nextGroups.length - 1].headers || []).length : 0;
4797
- this.clampActiveCell(nextRowCount, nextColCount);
5249
+ const nextIds = nextRows.map((r) => r.id);
5250
+ const fedData = this.table.options.data;
5251
+ const dataChanged = fedData !== this.lastFedData;
5252
+ this.lastFedData = fedData;
5253
+ const viewChangeOrWrite = this.pendingViewChange || this.pendingWrite && dataChanged;
5254
+ const swapWithoutIds = dataChanged && !this.pendingWrite && !this.pendingViewChange && this.table.options.getRowId == null;
5255
+ this.pendingViewChange = false;
5256
+ if (dataChanged) this.pendingWrite = false;
5257
+ this.followActiveCell(swapWithoutIds ? null : this.prevRowIds, nextIds, viewChangeOrWrite, nextRowCount, nextColCount, this.pendingEditFollow != null);
5258
+ this.prevRowIds = nextIds;
4798
5259
  const pgState = this.table.getState().pagination;
4799
5260
  const pc = this.table.getPageCount();
4800
5261
  if (pc > 0 && pgState.pageIndex > pc - 1) this.writePagination({
@@ -4810,6 +5271,15 @@ let DataTable = class DataTable extends (0, _lit_labs_preact_signals.SignalWatch
4810
5271
  this.syncIndeterminate();
4811
5272
  if (typeof queueMicrotask !== "undefined") queueMicrotask(this.syncIndeterminate);
4812
5273
  else Promise.resolve().then(this.syncIndeterminate);
5274
+ const vs = this.holdCtrl.viewState(this.table);
5275
+ if (!viewStatesEqual(vs, this.lastViewState)) {
5276
+ this.lastViewState = vs;
5277
+ this.dispatchEvent(new CustomEvent("view-state-change", {
5278
+ detail: vs,
5279
+ bubbles: true,
5280
+ composed: true
5281
+ }));
5282
+ }
4813
5283
  };
4814
5284
  this.refreshRowModel();
4815
5285
  this.gridRoot = this._ref__rozieRoot ? this._ref__rozieRoot.querySelector(".rozie-data-table") : null;
@@ -4857,7 +5327,7 @@ let DataTable = class DataTable extends (0, _lit_labs_preact_signals.SignalWatch
4857
5327
  this.announceState.globalFilter = this.effectiveGlobalFilter();
4858
5328
  }
4859
5329
  updated(changedProperties) {
4860
- if (this.__rozieFirstUpdateDone && (changedProperties.has("rowCount") || changedProperties.has("pageCount") || changedProperties.has("expandable") || changedProperties.has("groupable") || changedProperties.has("selectionMode") || changedProperties.has("columns"))) {
5330
+ if (this.__rozieFirstUpdateDone && (changedProperties.has("rowCount") || changedProperties.has("pageCount") || changedProperties.has("expandable") || changedProperties.has("groupable") || changedProperties.has("selectionMode") || changedProperties.has("holdView") || changedProperties.has("columns"))) {
4861
5331
  const __watchVal = (0, _lit_labs_preact_signals.untracked)(() => [
4862
5332
  this.sorting,
4863
5333
  this.globalFilter,
@@ -4875,6 +5345,7 @@ let DataTable = class DataTable extends (0, _lit_labs_preact_signals.SignalWatch
4875
5345
  this.columnOrder,
4876
5346
  this.columnPinning,
4877
5347
  this.selectionMode,
5348
+ this.holdView,
4878
5349
  (this.data || []).length,
4879
5350
  this.data,
4880
5351
  this._dataDefault.value,
@@ -6069,6 +6540,11 @@ __decorate([(0, lit_decorators_js.property)({
6069
6540
  reflect: true,
6070
6541
  attribute: "undo-limit"
6071
6542
  })], DataTable.prototype, "undoLimit", void 0);
6543
+ __decorate([(0, lit_decorators_js.property)({
6544
+ type: Boolean,
6545
+ reflect: true,
6546
+ attribute: "hold-view"
6547
+ })], DataTable.prototype, "holdView", void 0);
6072
6548
  __decorate([(0, lit_decorators_js.property)({ converter: { fromAttribute: (v) => v === null ? false : v === "true" ? true : v === "false" ? false : v === "" ? true : v } })], DataTable.prototype, "virtual", void 0);
6073
6549
  __decorate([(0, lit_decorators_js.property)({
6074
6550
  type: Number,
@@ -6169,6 +6645,8 @@ let Column = class Column extends (0, _lit_labs_preact_signals.SignalWatcher)(li
6169
6645
  this.expandable = false;
6170
6646
  this.groupable = true;
6171
6647
  this.aggregationFn = null;
6648
+ this.filterFn = null;
6649
+ this.sortingFn = null;
6172
6650
  this.editable = false;
6173
6651
  this.editor = "text";
6174
6652
  this.editorOptions = [];
@@ -6182,23 +6660,36 @@ let Column = class Column extends (0, _lit_labs_preact_signals.SignalWatcher)(li
6182
6660
  this._rozieTornDown = false;
6183
6661
  this.reg = null;
6184
6662
  this.registered = false;
6185
- this.colId = () => this.id !== "" ? this.id : this.field;
6186
- this.buildSpec = () => ({
6187
- id: this.colId(),
6188
- field: this.field !== "" ? this.field : this.colId(),
6189
- header: this.header,
6190
- sortable: this.sortable,
6191
- filterable: this.filterable,
6192
- pinned: this.pinned,
6193
- width: this.width,
6194
- expandable: this.expandable,
6195
- groupable: this.groupable,
6196
- aggregationFn: this.aggregationFn,
6197
- editable: this.editable,
6198
- editor: this.editor,
6199
- editorOptions: this.editorOptions,
6200
- validate: this.validate
6663
+ this.latestFilterFn = null;
6664
+ this.latestSortingFn = null;
6665
+ this.filterTrampoline = Object.assign((...args) => typeof this.latestFilterFn === "function" ? this.latestFilterFn(...args) : true, {
6666
+ autoRemove: (value, column) => typeof this.latestFilterFn === "function" && typeof this.latestFilterFn.autoRemove === "function" ? this.latestFilterFn.autoRemove(value, column) : false,
6667
+ resolveFilterValue: (value) => typeof this.latestFilterFn === "function" && typeof this.latestFilterFn.resolveFilterValue === "function" ? this.latestFilterFn.resolveFilterValue(value) : value
6201
6668
  });
6669
+ this.sortingTrampoline = (...args) => typeof this.latestSortingFn === "function" ? this.latestSortingFn(...args) : 0;
6670
+ this.colId = () => this.id !== "" ? this.id : this.field;
6671
+ this.buildSpec = () => {
6672
+ this.latestFilterFn = this.filterFn;
6673
+ this.latestSortingFn = this.sortingFn;
6674
+ return {
6675
+ id: this.colId(),
6676
+ field: this.field !== "" ? this.field : this.colId(),
6677
+ header: this.header,
6678
+ sortable: this.sortable,
6679
+ filterable: this.filterable,
6680
+ pinned: this.pinned,
6681
+ width: this.width,
6682
+ expandable: this.expandable,
6683
+ groupable: this.groupable,
6684
+ aggregationFn: this.aggregationFn,
6685
+ filterFn: typeof this.filterFn === "function" ? this.filterTrampoline : this.filterFn,
6686
+ sortingFn: typeof this.sortingFn === "function" ? this.sortingTrampoline : this.sortingFn,
6687
+ editable: this.editable,
6688
+ editor: this.editor,
6689
+ editorOptions: this.editorOptions,
6690
+ validate: this.validate
6691
+ };
6692
+ };
6202
6693
  }
6203
6694
  static {
6204
6695
  this.styles = lit.css`
@@ -6219,9 +6710,11 @@ let Column = class Column extends (0, _lit_labs_preact_signals.SignalWatcher)(li
6219
6710
  }
6220
6711
  }
6221
6712
  updated(changedProperties) {
6222
- 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"))) {
6223
- 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;
6713
+ 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"))) {
6714
+ 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;
6224
6715
  (() => {
6716
+ this.latestFilterFn = this.filterFn;
6717
+ this.latestSortingFn = this.sortingFn;
6225
6718
  if (this.reg) this.reg.registerColumn(this.colId(), this.buildSpec());
6226
6719
  })();
6227
6720
  }
@@ -6288,6 +6781,14 @@ __decorate([(0, lit_decorators_js.property)({
6288
6781
  type: String,
6289
6782
  attribute: "aggregation-fn"
6290
6783
  })], Column.prototype, "aggregationFn", void 0);
6784
+ __decorate([(0, lit_decorators_js.property)({
6785
+ type: String,
6786
+ attribute: "filter-fn"
6787
+ })], Column.prototype, "filterFn", void 0);
6788
+ __decorate([(0, lit_decorators_js.property)({
6789
+ type: String,
6790
+ attribute: "sorting-fn"
6791
+ })], Column.prototype, "sortingFn", void 0);
6291
6792
  __decorate([(0, lit_decorators_js.property)({
6292
6793
  type: Boolean,
6293
6794
  reflect: true