@toolbox-web/grid 0.0.3 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/all.d.ts +50 -6
  2. package/all.js +101 -98
  3. package/all.js.map +1 -1
  4. package/index.d.ts +54 -0
  5. package/index.js +793 -692
  6. package/index.js.map +1 -1
  7. package/lib/plugins/clipboard/index.js +55 -35
  8. package/lib/plugins/clipboard/index.js.map +1 -1
  9. package/lib/plugins/column-virtualization/index.js +49 -29
  10. package/lib/plugins/column-virtualization/index.js.map +1 -1
  11. package/lib/plugins/context-menu/index.js +35 -15
  12. package/lib/plugins/context-menu/index.js.map +1 -1
  13. package/lib/plugins/export/index.js +52 -32
  14. package/lib/plugins/export/index.js.map +1 -1
  15. package/lib/plugins/filtering/index.js +116 -99
  16. package/lib/plugins/filtering/index.js.map +1 -1
  17. package/lib/plugins/grouping-columns/index.js +42 -22
  18. package/lib/plugins/grouping-columns/index.js.map +1 -1
  19. package/lib/plugins/grouping-rows/index.js +20 -0
  20. package/lib/plugins/grouping-rows/index.js.map +1 -1
  21. package/lib/plugins/master-detail/index.js +50 -27
  22. package/lib/plugins/master-detail/index.js.map +1 -1
  23. package/lib/plugins/multi-sort/index.js +25 -5
  24. package/lib/plugins/multi-sort/index.js.map +1 -1
  25. package/lib/plugins/pinned-columns/index.js +20 -0
  26. package/lib/plugins/pinned-columns/index.js.map +1 -1
  27. package/lib/plugins/pinned-rows/index.js +20 -0
  28. package/lib/plugins/pinned-rows/index.js.map +1 -1
  29. package/lib/plugins/pivot/index.js +20 -0
  30. package/lib/plugins/pivot/index.js.map +1 -1
  31. package/lib/plugins/reorder/index.js +56 -33
  32. package/lib/plugins/reorder/index.js.map +1 -1
  33. package/lib/plugins/selection/index.js +138 -100
  34. package/lib/plugins/selection/index.js.map +1 -1
  35. package/lib/plugins/server-side/index.js +20 -0
  36. package/lib/plugins/server-side/index.js.map +1 -1
  37. package/lib/plugins/tree/index.js +76 -53
  38. package/lib/plugins/tree/index.js.map +1 -1
  39. package/lib/plugins/undo-redo/index.js +20 -0
  40. package/lib/plugins/undo-redo/index.js.map +1 -1
  41. package/lib/plugins/visibility/index.js +20 -0
  42. package/lib/plugins/visibility/index.js.map +1 -1
  43. package/package.json +4 -1
  44. package/themes/dg-theme-contrast.css +43 -43
  45. package/themes/dg-theme-large.css +54 -54
  46. package/themes/dg-theme-standard.css +19 -19
  47. package/themes/dg-theme-vibrant.css +16 -16
  48. package/umd/grid.all.umd.js +24 -24
  49. package/umd/grid.all.umd.js.map +1 -1
  50. package/umd/grid.umd.js +14 -14
  51. package/umd/grid.umd.js.map +1 -1
  52. package/umd/plugins/filtering.umd.js +3 -3
  53. package/umd/plugins/filtering.umd.js.map +1 -1
  54. package/umd/plugins/master-detail.umd.js +2 -2
  55. package/umd/plugins/master-detail.umd.js.map +1 -1
  56. package/umd/plugins/multi-sort.umd.js.map +1 -1
  57. package/umd/plugins/reorder.umd.js +1 -1
  58. package/umd/plugins/reorder.umd.js.map +1 -1
  59. package/umd/plugins/selection.umd.js +2 -2
  60. package/umd/plugins/selection.umd.js.map +1 -1
  61. package/umd/plugins/tree.umd.js +2 -2
  62. package/umd/plugins/tree.umd.js.map +1 -1
  63. package/umd/plugins/visibility.umd.js.map +1 -1
@@ -1,4 +1,19 @@
1
- class P {
1
+ function M(f) {
2
+ const { totalRows: e, viewportHeight: t, scrollTop: r, rowHeight: l, overscan: s } = f, i = Math.ceil(t / l);
3
+ let n = Math.floor(r / l) - s;
4
+ n < 0 && (n = 0);
5
+ let u = n + i + s * 2;
6
+ return u > e && (u = e), u === e && n > 0 && (n = Math.max(0, u - i - s * 2)), {
7
+ start: n,
8
+ end: u,
9
+ offsetY: n * l,
10
+ totalHeight: e * l
11
+ };
12
+ }
13
+ function P(f, e) {
14
+ return f <= e;
15
+ }
16
+ class V {
2
17
  /** Plugin version - override in subclass if needed */
3
18
  version = "1.0.0";
4
19
  /** CSS styles to inject into the grid's shadow DOM */
@@ -97,6 +112,26 @@ class P {
97
112
  get shadowRoot() {
98
113
  return this.grid?.shadowRoot ?? null;
99
114
  }
115
+ /**
116
+ * Get the disconnect signal for event listener cleanup.
117
+ * This signal is aborted when the grid disconnects from the DOM.
118
+ * Use this when adding event listeners that should be cleaned up automatically.
119
+ *
120
+ * Best for:
121
+ * - Document/window-level listeners added in attach()
122
+ * - Listeners on the grid element itself
123
+ * - Any listener that should persist across renders
124
+ *
125
+ * Not needed for:
126
+ * - Listeners on elements created in afterRender() (removed with element)
127
+ *
128
+ * @example
129
+ * element.addEventListener('click', handler, { signal: this.disconnectSignal });
130
+ * document.addEventListener('keydown', handler, { signal: this.disconnectSignal });
131
+ */
132
+ get disconnectSignal() {
133
+ return this.grid?.disconnectSignal;
134
+ }
100
135
  /**
101
136
  * Log a warning message.
102
137
  */
@@ -104,21 +139,6 @@ class P {
104
139
  console.warn(`[tbw-grid:${this.name}] ${e}`);
105
140
  }
106
141
  }
107
- function A(f) {
108
- const { totalRows: e, viewportHeight: t, scrollTop: r, rowHeight: l, overscan: s } = f, i = Math.ceil(t / l);
109
- let n = Math.floor(r / l) - s;
110
- n < 0 && (n = 0);
111
- let u = n + i + s * 2;
112
- return u > e && (u = e), u === e && n > 0 && (n = Math.max(0, u - i - s * 2)), {
113
- start: n,
114
- end: u,
115
- offsetY: n * l,
116
- totalHeight: e * l
117
- };
118
- }
119
- function V(f, e) {
120
- return f <= e;
121
- }
122
142
  function _(f, e, t = !1) {
123
143
  const r = f[e.field];
124
144
  if (e.operator === "blank")
@@ -174,7 +194,7 @@ function B(f) {
174
194
  }))
175
195
  );
176
196
  }
177
- function M(f, e) {
197
+ function H(f, e) {
178
198
  const t = /* @__PURE__ */ new Set();
179
199
  for (const r of f) {
180
200
  const l = r[e];
@@ -324,7 +344,7 @@ const z = `
324
344
  background: var(--tbw-filter-hover, var(--tbw-color-row-hover, light-dark(#f0f6ff, #1c1c1c)));
325
345
  }
326
346
  `;
327
- class y extends P {
347
+ class x extends V {
328
348
  name = "filtering";
329
349
  version = "1.0.0";
330
350
  get defaultConfig() {
@@ -344,7 +364,8 @@ class y extends P {
344
364
  panelElement = null;
345
365
  searchText = /* @__PURE__ */ new Map();
346
366
  excludedValues = /* @__PURE__ */ new Map();
347
- documentClickHandler = null;
367
+ panelAbortController = null;
368
+ // For panel-scoped listeners
348
369
  globalStylesInjected = !1;
349
370
  // Virtualization constants for filter value list
350
371
  static LIST_ITEM_HEIGHT = 28;
@@ -356,7 +377,7 @@ class y extends P {
356
377
  super.attach(e), this.injectGlobalStyles();
357
378
  }
358
379
  detach() {
359
- this.filters.clear(), this.cachedResult = null, this.cacheKey = null, this.openPanelField = null, this.panelElement && (this.panelElement.remove(), this.panelElement = null), this.searchText.clear(), this.excludedValues.clear(), this.removeDocumentClickHandler();
380
+ this.filters.clear(), this.cachedResult = null, this.cacheKey = null, this.openPanelField = null, this.panelElement && (this.panelElement.remove(), this.panelElement = null), this.searchText.clear(), this.excludedValues.clear(), this.panelAbortController?.abort(), this.panelAbortController = null;
360
381
  }
361
382
  // ===== Hooks =====
362
383
  processRows(e) {
@@ -468,7 +489,7 @@ class y extends P {
468
489
  * Uses sourceRows to include all values regardless of current filter.
469
490
  */
470
491
  getUniqueValues(e) {
471
- return M(this.sourceRows, e);
492
+ return H(this.sourceRows, e);
472
493
  }
473
494
  // ===== Private Methods =====
474
495
  /**
@@ -494,7 +515,7 @@ class y extends P {
494
515
  this.closeFilterPanel();
495
516
  const l = document.createElement("div");
496
517
  l.className = "tbw-filter-panel", this.panelElement = l, this.openPanelField = e;
497
- const s = M(this.sourceRows, e);
518
+ const s = H(this.sourceRows, e);
498
519
  let i = this.excludedValues.get(e);
499
520
  i || (i = /* @__PURE__ */ new Set(), this.excludedValues.set(e, i));
500
521
  const n = this.searchText.get(e) ?? "", u = {
@@ -503,11 +524,11 @@ class y extends P {
503
524
  uniqueValues: s,
504
525
  excludedValues: i,
505
526
  searchText: n,
506
- applySetFilter: (m) => {
507
- this.applySetFilter(e, m), this.closeFilterPanel();
527
+ applySetFilter: (h) => {
528
+ this.applySetFilter(e, h), this.closeFilterPanel();
508
529
  },
509
- applyTextFilter: (m, k, w) => {
510
- this.applyTextFilter(e, m, k, w), this.closeFilterPanel();
530
+ applyTextFilter: (h, E, C) => {
531
+ this.applyTextFilter(e, h, E, C), this.closeFilterPanel();
511
532
  },
512
533
  clearFilter: () => {
513
534
  this.clearFieldFilter(e), this.closeFilterPanel();
@@ -515,25 +536,21 @@ class y extends P {
515
536
  closePanel: () => this.closeFilterPanel()
516
537
  };
517
538
  let g = !1;
518
- this.config.filterPanelRenderer && (this.config.filterPanelRenderer(l, u), g = l.children.length > 0), g || this.renderDefaultFilterPanel(l, u, s, i), document.body.appendChild(l), this.positionPanel(l, r);
519
- const p = (m) => {
520
- !l.contains(m.target) && m.target !== r && this.closeFilterPanel();
521
- };
522
- this.documentClickHandler = p, setTimeout(() => {
523
- document.addEventListener("click", p);
539
+ this.config.filterPanelRenderer && (this.config.filterPanelRenderer(l, u), g = l.children.length > 0), g || this.renderDefaultFilterPanel(l, u, s, i), document.body.appendChild(l), this.positionPanel(l, r), this.panelAbortController = new AbortController(), setTimeout(() => {
540
+ document.addEventListener(
541
+ "click",
542
+ (h) => {
543
+ !l.contains(h.target) && h.target !== r && this.closeFilterPanel();
544
+ },
545
+ { signal: this.panelAbortController?.signal }
546
+ );
524
547
  }, 0);
525
548
  }
526
549
  /**
527
550
  * Close the filter panel
528
551
  */
529
552
  closeFilterPanel() {
530
- this.panelElement && (this.panelElement.remove(), this.panelElement = null), this.openPanelField = null, this.removeDocumentClickHandler();
531
- }
532
- /**
533
- * Remove the document click handler
534
- */
535
- removeDocumentClickHandler() {
536
- this.documentClickHandler && (document.removeEventListener("click", this.documentClickHandler), this.documentClickHandler = null);
553
+ this.panelElement && (this.panelElement.remove(), this.panelElement = null), this.openPanelField = null, this.panelAbortController?.abort(), this.panelAbortController = null;
537
554
  }
538
555
  /**
539
556
  * Position the panel below the button
@@ -557,92 +574,92 @@ class y extends P {
557
574
  u.className = "tbw-filter-actions";
558
575
  const g = document.createElement("label");
559
576
  g.className = "tbw-filter-value-item", g.style.padding = "0", g.style.margin = "0";
560
- const p = document.createElement("input");
561
- p.type = "checkbox", p.className = "tbw-filter-checkbox";
562
- const m = document.createElement("span");
563
- m.textContent = "Select All", g.appendChild(p), g.appendChild(m), u.appendChild(g);
564
- const k = () => {
565
- const a = [...x.values()], d = a.every((c) => c), h = a.every((c) => !c);
566
- p.checked = d, p.indeterminate = !d && !h;
577
+ const h = document.createElement("input");
578
+ h.type = "checkbox", h.className = "tbw-filter-checkbox";
579
+ const E = document.createElement("span");
580
+ E.textContent = "Select All", g.appendChild(h), g.appendChild(E), u.appendChild(g);
581
+ const C = () => {
582
+ const a = [...v.values()], d = a.every((c) => c), p = a.every((c) => !c);
583
+ h.checked = d, h.indeterminate = !d && !p;
567
584
  };
568
- p.addEventListener("change", () => {
569
- const a = p.checked;
570
- for (const d of x.keys())
571
- x.set(d, a);
572
- k(), F();
585
+ h.addEventListener("change", () => {
586
+ const a = h.checked;
587
+ for (const d of v.keys())
588
+ v.set(d, a);
589
+ C(), F();
573
590
  }), e.appendChild(u);
574
- const w = document.createElement("div");
575
- w.className = "tbw-filter-values";
576
- const E = document.createElement("div");
577
- E.className = "tbw-filter-values-spacer", w.appendChild(E);
591
+ const m = document.createElement("div");
592
+ m.className = "tbw-filter-values";
593
+ const R = document.createElement("div");
594
+ R.className = "tbw-filter-values-spacer", m.appendChild(R);
578
595
  const b = document.createElement("div");
579
- b.className = "tbw-filter-values-content", w.appendChild(b);
580
- const x = /* @__PURE__ */ new Map();
596
+ b.className = "tbw-filter-values-content", m.appendChild(b);
597
+ const v = /* @__PURE__ */ new Map();
581
598
  r.forEach((a) => {
582
599
  const d = a == null ? "__null__" : String(a);
583
- x.set(d, !l.has(a));
584
- }), k();
585
- let C = [];
586
- const H = (a, d) => {
587
- const h = a == null ? "(Blank)" : String(a), c = a == null ? "__null__" : String(a), o = document.createElement("label");
588
- o.className = "tbw-filter-value-item", o.style.position = "absolute", o.style.top = `${d * y.LIST_ITEM_HEIGHT}px`, o.style.left = "0", o.style.right = "0", o.style.height = `${y.LIST_ITEM_HEIGHT}px`, o.style.boxSizing = "border-box";
589
- const v = document.createElement("input");
590
- v.type = "checkbox", v.className = "tbw-filter-checkbox", v.checked = x.get(c) ?? !0, v.dataset.value = c, v.addEventListener("change", () => {
591
- x.set(c, v.checked), k();
600
+ v.set(d, !l.has(a));
601
+ }), C();
602
+ let y = [];
603
+ const I = (a, d) => {
604
+ const p = a == null ? "(Blank)" : String(a), c = a == null ? "__null__" : String(a), o = document.createElement("label");
605
+ o.className = "tbw-filter-value-item", o.style.position = "absolute", o.style.top = `${d * x.LIST_ITEM_HEIGHT}px`, o.style.left = "0", o.style.right = "0", o.style.height = `${x.LIST_ITEM_HEIGHT}px`, o.style.boxSizing = "border-box";
606
+ const w = document.createElement("input");
607
+ w.type = "checkbox", w.className = "tbw-filter-checkbox", w.checked = v.get(c) ?? !0, w.dataset.value = c, w.addEventListener("change", () => {
608
+ v.set(c, w.checked), C();
592
609
  });
593
- const N = document.createElement("span");
594
- return N.textContent = h, o.appendChild(v), o.appendChild(N), o;
610
+ const A = document.createElement("span");
611
+ return A.textContent = p, o.appendChild(w), o.appendChild(A), o;
595
612
  }, F = () => {
596
- const a = C.length, d = w.clientHeight, h = w.scrollTop;
597
- if (E.style.height = `${a * y.LIST_ITEM_HEIGHT}px`, V(a, y.LIST_BYPASS_THRESHOLD / 3)) {
598
- b.innerHTML = "", b.style.transform = "translateY(0px)", C.forEach((o, v) => {
599
- b.appendChild(H(o, v));
613
+ const a = y.length, d = m.clientHeight, p = m.scrollTop;
614
+ if (R.style.height = `${a * x.LIST_ITEM_HEIGHT}px`, P(a, x.LIST_BYPASS_THRESHOLD / 3)) {
615
+ b.innerHTML = "", b.style.transform = "translateY(0px)", y.forEach((o, w) => {
616
+ b.appendChild(I(o, w));
600
617
  });
601
618
  return;
602
619
  }
603
- const c = A({
620
+ const c = M({
604
621
  totalRows: a,
605
622
  viewportHeight: d,
606
- scrollTop: h,
607
- rowHeight: y.LIST_ITEM_HEIGHT,
608
- overscan: y.LIST_OVERSCAN
623
+ scrollTop: p,
624
+ rowHeight: x.LIST_ITEM_HEIGHT,
625
+ overscan: x.LIST_OVERSCAN
609
626
  });
610
627
  b.style.transform = `translateY(${c.offsetY}px)`, b.innerHTML = "";
611
628
  for (let o = c.start; o < c.end; o++)
612
- b.appendChild(H(C[o], o - c.start));
613
- }, I = (a) => {
629
+ b.appendChild(I(y[o], o - c.start));
630
+ }, N = (a) => {
614
631
  const d = a.toLowerCase();
615
- if (C = r.filter((h) => {
616
- const c = h == null ? "(Blank)" : String(h);
632
+ if (y = r.filter((p) => {
633
+ const c = p == null ? "(Blank)" : String(p);
617
634
  return !a || c.toLowerCase().includes(d);
618
- }), C.length === 0) {
619
- E.style.height = "0px", b.innerHTML = "";
620
- const h = document.createElement("div");
621
- h.className = "tbw-filter-no-match", h.textContent = "No matching values", b.appendChild(h);
635
+ }), y.length === 0) {
636
+ R.style.height = "0px", b.innerHTML = "";
637
+ const p = document.createElement("div");
638
+ p.className = "tbw-filter-no-match", p.textContent = "No matching values", b.appendChild(p);
622
639
  return;
623
640
  }
624
641
  F();
625
642
  };
626
- w.addEventListener(
643
+ m.addEventListener(
627
644
  "scroll",
628
645
  () => {
629
- C.length > 0 && F();
646
+ y.length > 0 && F();
630
647
  },
631
648
  { passive: !0 }
632
- ), I(n.value), e.appendChild(w);
649
+ ), N(n.value), e.appendChild(m);
633
650
  let L;
634
651
  n.addEventListener("input", () => {
635
652
  clearTimeout(L), L = setTimeout(() => {
636
- this.searchText.set(s, n.value), I(n.value);
653
+ this.searchText.set(s, n.value), N(n.value);
637
654
  }, this.config.debounceMs ?? 150);
638
655
  });
639
- const R = document.createElement("div");
640
- R.className = "tbw-filter-buttons";
641
- const S = document.createElement("button");
642
- S.className = "tbw-filter-apply-btn", S.textContent = "Apply", S.addEventListener("click", () => {
656
+ const S = document.createElement("div");
657
+ S.className = "tbw-filter-buttons";
658
+ const T = document.createElement("button");
659
+ T.className = "tbw-filter-apply-btn", T.textContent = "Apply", T.addEventListener("click", () => {
643
660
  const a = [];
644
- for (const [d, h] of x)
645
- if (!h)
661
+ for (const [d, p] of v)
662
+ if (!p)
646
663
  if (d === "__null__")
647
664
  a.push(null);
648
665
  else {
@@ -650,11 +667,11 @@ class y extends P {
650
667
  a.push(c !== void 0 ? c : d);
651
668
  }
652
669
  t.applySetFilter(a);
653
- }), R.appendChild(S);
654
- const T = document.createElement("button");
655
- T.className = "tbw-filter-clear-btn", T.textContent = "Clear Filter", T.addEventListener("click", () => {
670
+ }), S.appendChild(T);
671
+ const k = document.createElement("button");
672
+ k.className = "tbw-filter-clear-btn", k.textContent = "Clear Filter", k.addEventListener("click", () => {
656
673
  t.clearFilter();
657
- }), R.appendChild(T), e.appendChild(R);
674
+ }), S.appendChild(k), e.appendChild(S);
658
675
  }
659
676
  /**
660
677
  * Apply a set filter (exclude values)
@@ -754,6 +771,6 @@ class y extends P {
754
771
  `;
755
772
  }
756
773
  export {
757
- y as FilteringPlugin
774
+ x as FilteringPlugin
758
775
  };
759
776
  //# sourceMappingURL=index.js.map