@toolbox-web/grid 0.2.5 → 0.2.7

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 (71) hide show
  1. package/all.d.ts +486 -80
  2. package/all.js +1364 -1029
  3. package/all.js.map +1 -1
  4. package/index-DG2CZ_Zo.js +3229 -0
  5. package/index-DG2CZ_Zo.js.map +1 -0
  6. package/index.d.ts +222 -11
  7. package/index.js +25 -3143
  8. package/index.js.map +1 -1
  9. package/lib/plugins/clipboard/index.js +1 -1
  10. package/lib/plugins/clipboard/index.js.map +1 -1
  11. package/lib/plugins/column-virtualization/index.js +1 -1
  12. package/lib/plugins/column-virtualization/index.js.map +1 -1
  13. package/lib/plugins/context-menu/index.js +1 -1
  14. package/lib/plugins/context-menu/index.js.map +1 -1
  15. package/lib/plugins/export/index.js +1 -1
  16. package/lib/plugins/export/index.js.map +1 -1
  17. package/lib/plugins/filtering/index.js +184 -149
  18. package/lib/plugins/filtering/index.js.map +1 -1
  19. package/lib/plugins/grouping-columns/index.js +46 -45
  20. package/lib/plugins/grouping-columns/index.js.map +1 -1
  21. package/lib/plugins/grouping-rows/index.js +117 -83
  22. package/lib/plugins/grouping-rows/index.js.map +1 -1
  23. package/lib/plugins/master-detail/index.js +140 -82
  24. package/lib/plugins/master-detail/index.js.map +1 -1
  25. package/lib/plugins/multi-sort/index.js +18 -18
  26. package/lib/plugins/multi-sort/index.js.map +1 -1
  27. package/lib/plugins/pinned-columns/index.js +1 -1
  28. package/lib/plugins/pinned-columns/index.js.map +1 -1
  29. package/lib/plugins/pinned-rows/index.js +55 -47
  30. package/lib/plugins/pinned-rows/index.js.map +1 -1
  31. package/lib/plugins/pivot/index.js +385 -351
  32. package/lib/plugins/pivot/index.js.map +1 -1
  33. package/lib/plugins/reorder/index.js +278 -85
  34. package/lib/plugins/reorder/index.js.map +1 -1
  35. package/lib/plugins/selection/index.js +28 -27
  36. package/lib/plugins/selection/index.js.map +1 -1
  37. package/lib/plugins/server-side/index.js +2 -2
  38. package/lib/plugins/server-side/index.js.map +1 -1
  39. package/lib/plugins/tree/index.js +181 -170
  40. package/lib/plugins/tree/index.js.map +1 -1
  41. package/lib/plugins/undo-redo/index.js +1 -1
  42. package/lib/plugins/undo-redo/index.js.map +1 -1
  43. package/lib/plugins/visibility/index.js +1 -1
  44. package/lib/plugins/visibility/index.js.map +1 -1
  45. package/package.json +1 -1
  46. package/umd/grid.all.umd.js +22 -22
  47. package/umd/grid.all.umd.js.map +1 -1
  48. package/umd/grid.umd.js +12 -12
  49. package/umd/grid.umd.js.map +1 -1
  50. package/umd/plugins/filtering.umd.js +1 -1
  51. package/umd/plugins/filtering.umd.js.map +1 -1
  52. package/umd/plugins/grouping-columns.umd.js +1 -1
  53. package/umd/plugins/grouping-columns.umd.js.map +1 -1
  54. package/umd/plugins/grouping-rows.umd.js +1 -1
  55. package/umd/plugins/grouping-rows.umd.js.map +1 -1
  56. package/umd/plugins/master-detail.umd.js +1 -1
  57. package/umd/plugins/master-detail.umd.js.map +1 -1
  58. package/umd/plugins/multi-sort.umd.js +1 -1
  59. package/umd/plugins/multi-sort.umd.js.map +1 -1
  60. package/umd/plugins/pinned-rows.umd.js +1 -1
  61. package/umd/plugins/pinned-rows.umd.js.map +1 -1
  62. package/umd/plugins/pivot.umd.js +1 -1
  63. package/umd/plugins/pivot.umd.js.map +1 -1
  64. package/umd/plugins/reorder.umd.js +1 -1
  65. package/umd/plugins/reorder.umd.js.map +1 -1
  66. package/umd/plugins/selection.umd.js +1 -1
  67. package/umd/plugins/selection.umd.js.map +1 -1
  68. package/umd/plugins/server-side.umd.js +1 -1
  69. package/umd/plugins/server-side.umd.js.map +1 -1
  70. package/umd/plugins/tree.umd.js +1 -1
  71. package/umd/plugins/tree.umd.js.map +1 -1
@@ -1,19 +1,19 @@
1
- function M(f) {
2
- const { totalRows: e, viewportHeight: t, scrollTop: r, rowHeight: l, overscan: a } = f, i = Math.ceil(t / l);
3
- let n = Math.floor(r / l) - a;
4
- n < 0 && (n = 0);
5
- let u = n + i + a * 2;
6
- return u > e && (u = e), u === e && n > 0 && (n = Math.max(0, u - i - a * 2)), {
7
- start: n,
8
- end: u,
9
- offsetY: n * l,
1
+ function P(p) {
2
+ const { totalRows: e, viewportHeight: t, scrollTop: r, rowHeight: l, overscan: n } = p, o = Math.ceil(t / l);
3
+ let i = Math.floor(r / l) - n;
4
+ i < 0 && (i = 0);
5
+ let a = i + o + n * 2;
6
+ return a > e && (a = e), a === e && i > 0 && (i = Math.max(0, a - o - n * 2)), {
7
+ start: i,
8
+ end: a,
9
+ offsetY: i * l,
10
10
  totalHeight: e * l
11
11
  };
12
12
  }
13
- function P(f, e) {
14
- return f <= e;
13
+ function M(p, e) {
14
+ return p <= e;
15
15
  }
16
- const _ = {
16
+ const V = {
17
17
  expand: "▶",
18
18
  collapse: "▼",
19
19
  sortAsc: "▲",
@@ -23,7 +23,7 @@ const _ = {
23
23
  dragHandle: "⋮⋮",
24
24
  toolPanel: "☰"
25
25
  };
26
- class V {
26
+ class _ {
27
27
  /** Plugin version - override in subclass if needed */
28
28
  version = "1.0.0";
29
29
  /** CSS styles to inject into the grid's shadow DOM */
@@ -114,7 +114,7 @@ class V {
114
114
  * Use this for rendering that needs to match the grid template.
115
115
  */
116
116
  get visibleColumns() {
117
- return this.grid?.visibleColumns ?? [];
117
+ return this.grid?._visibleColumns ?? [];
118
118
  }
119
119
  /**
120
120
  * Get the shadow root of the grid.
@@ -148,7 +148,7 @@ class V {
148
148
  */
149
149
  get gridIcons() {
150
150
  const e = this.grid?.gridConfig?.icons ?? {};
151
- return { ..._, ...e };
151
+ return { ...V, ...e };
152
152
  }
153
153
  /**
154
154
  * Resolve an icon value to string or HTMLElement.
@@ -179,28 +179,28 @@ class V {
179
179
  }
180
180
  // #endregion
181
181
  }
182
- function q(f, e, t = !1) {
183
- const r = f[e.field];
182
+ function q(p, e, t = !1) {
183
+ const r = p[e.field];
184
184
  if (e.operator === "blank")
185
185
  return r == null || r === "";
186
186
  if (e.operator === "notBlank")
187
187
  return r != null && r !== "";
188
188
  if (r == null) return !1;
189
- const l = String(r), a = t ? l : l.toLowerCase(), i = t ? String(e.value) : String(e.value).toLowerCase();
189
+ const l = String(r), n = t ? l : l.toLowerCase(), o = t ? String(e.value) : String(e.value).toLowerCase();
190
190
  switch (e.operator) {
191
191
  // Text operators
192
192
  case "contains":
193
- return a.includes(i);
193
+ return n.includes(o);
194
194
  case "notContains":
195
- return !a.includes(i);
195
+ return !n.includes(o);
196
196
  case "equals":
197
- return a === i;
197
+ return n === o;
198
198
  case "notEquals":
199
- return a !== i;
199
+ return n !== o;
200
200
  case "startsWith":
201
- return a.startsWith(i);
201
+ return n.startsWith(o);
202
202
  case "endsWith":
203
- return a.endsWith(i);
203
+ return n.endsWith(o);
204
204
  // Number/Date operators (use raw numeric values)
205
205
  case "lessThan":
206
206
  return Number(r) < Number(e.value);
@@ -221,12 +221,12 @@ function q(f, e, t = !1) {
221
221
  return !0;
222
222
  }
223
223
  }
224
- function B(f, e, t = !1) {
225
- return e.length ? f.filter((r) => e.every((l) => q(r, l, t))) : f;
224
+ function B(p, e, t = !1) {
225
+ return e.length ? p.filter((r) => e.every((l) => q(r, l, t))) : p;
226
226
  }
227
- function z(f) {
227
+ function z(p) {
228
228
  return JSON.stringify(
229
- f.map((e) => ({
229
+ p.map((e) => ({
230
230
  field: e.field,
231
231
  operator: e.operator,
232
232
  value: e.value,
@@ -234,16 +234,16 @@ function z(f) {
234
234
  }))
235
235
  );
236
236
  }
237
- function H(f, e) {
237
+ function N(p, e) {
238
238
  const t = /* @__PURE__ */ new Set();
239
- for (const r of f) {
239
+ for (const r of p) {
240
240
  const l = r[e];
241
241
  l != null && t.add(l);
242
242
  }
243
243
  return [...t].sort((r, l) => typeof r == "number" && typeof l == "number" ? r - l : String(r).localeCompare(String(l)));
244
244
  }
245
- const K = '.header-cell.filtered:before{content:"";position:absolute;top:4px;right:4px;width:6px;height:6px;background:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6));border-radius:50%}.tbw-filter-btn{display:inline-flex;align-items:center;justify-content:center;background:transparent;border:none;cursor:pointer;padding:2px;margin-left:4px;opacity:.4;transition:opacity .15s;color:inherit;vertical-align:middle}.tbw-filter-btn:hover,.tbw-filter-btn.active{opacity:1}.tbw-filter-btn.active{color:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6))}', $ = ".tbw-filter-panel{position:fixed;background:var(--tbw-filter-panel-bg, var(--tbw-color-panel-bg, light-dark(#eeeeee, #222222)));color:var(--tbw-filter-panel-fg, var(--tbw-color-fg, light-dark(#222222, #eeeeee)));border:1px solid var(--tbw-filter-panel-border, var(--tbw-color-border, light-dark(#d0d0d4, #454545)));border-radius:var(--tbw-filter-panel-radius, var(--tbw-border-radius, 4px));box-shadow:0 4px 16px var(--tbw-filter-panel-shadow, var(--tbw-color-shadow, light-dark(rgba(0, 0, 0, .1), rgba(0, 0, 0, .3))));padding:12px;z-index:10000;min-width:200px;max-width:280px;max-height:350px;display:flex;flex-direction:column;font-family:var(--tbw-font-family, system-ui, sans-serif);font-size:var(--tbw-font-size, 13px)}.tbw-filter-search{margin-bottom:8px}.tbw-filter-search-input{width:100%;padding:6px 10px;background:var(--tbw-filter-input-bg, var(--tbw-color-bg, transparent));color:inherit;border:1px solid var(--tbw-filter-input-border, var(--tbw-color-border, light-dark(#d0d0d4, #454545)));border-radius:var(--tbw-filter-input-radius, 4px);font-size:inherit;box-sizing:border-box}.tbw-filter-search-input:focus{outline:none;border-color:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6));box-shadow:0 0 0 2px rgba(from var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6)) r g b / 15%)}.tbw-filter-actions{display:flex;padding:4px 2px;margin-bottom:8px;border-bottom:1px solid var(--tbw-filter-divider, var(--tbw-color-border, light-dark(#d0d0d4, #454545)))}.tbw-filter-action-btn{background:transparent;border:none;color:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6));cursor:pointer;font-size:12px;padding:2px 0}.tbw-filter-action-btn:hover{text-decoration:underline}.tbw-filter-values{flex:1;overflow-y:auto;margin-bottom:8px;max-height:180px;position:relative}.tbw-filter-values-spacer{width:1px}.tbw-filter-values-content{position:absolute;top:0;left:0;right:0}.tbw-filter-value-item{display:flex;align-items:center;gap:8px;padding:4px 2px;cursor:pointer;border-radius:3px}.tbw-filter-value-item:hover{background:var(--tbw-filter-hover, var(--tbw-color-row-hover, light-dark(#f0f6ff, #1c1c1c)))}.tbw-filter-checkbox{margin:0;cursor:pointer;accent-color:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6))}.tbw-filter-no-match{color:var(--tbw-filter-muted, var(--tbw-color-fg-muted, light-dark(#555555, #aaaaaa)));padding:8px 0;text-align:center;font-style:italic}.tbw-filter-buttons{display:flex;gap:8px;padding-top:8px;border-top:1px solid var(--tbw-filter-divider, var(--tbw-color-border, light-dark(#d0d0d4, #454545)))}.tbw-filter-apply-btn{flex:1;padding:6px 12px;background:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6));color:var(--tbw-filter-accent-fg, var(--tbw-color-accent-fg, light-dark(#ffffff, #000000)));border:none;border-radius:4px;cursor:pointer;font-size:13px}.tbw-filter-apply-btn:hover{filter:brightness(.9)}.tbw-filter-clear-btn{flex:1;padding:6px 12px;background:transparent;color:var(--tbw-filter-muted, var(--tbw-color-fg-muted, light-dark(#555555, #aaaaaa)));border:1px solid var(--tbw-filter-input-border, var(--tbw-color-border, light-dark(#d0d0d4, #454545)));border-radius:4px;cursor:pointer;font-size:13px}.tbw-filter-clear-btn:hover{background:var(--tbw-filter-hover, var(--tbw-color-row-hover, light-dark(#f0f6ff, #1c1c1c)))}";
246
- class x extends V {
245
+ const $ = '.header-cell.filtered:before{content:"";position:absolute;top:4px;right:4px;width:6px;height:6px;background:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6));border-radius:50%}.tbw-filter-btn{display:inline-flex;align-items:center;justify-content:center;background:transparent;border:none;cursor:pointer;padding:2px;margin-left:4px;opacity:.4;transition:opacity .15s;color:inherit;vertical-align:middle}.tbw-filter-btn:hover,.tbw-filter-btn.active{opacity:1}.tbw-filter-btn.active{color:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6))}', K = ".tbw-filter-panel{position:fixed;background:var(--tbw-filter-panel-bg, var(--tbw-color-panel-bg, light-dark(#eeeeee, #222222)));color:var(--tbw-filter-panel-fg, var(--tbw-color-fg, light-dark(#222222, #eeeeee)));border:1px solid var(--tbw-filter-panel-border, var(--tbw-color-border, light-dark(#d0d0d4, #454545)));border-radius:var(--tbw-filter-panel-radius, var(--tbw-border-radius, 4px));box-shadow:0 4px 16px var(--tbw-filter-panel-shadow, var(--tbw-color-shadow, light-dark(rgba(0, 0, 0, .1), rgba(0, 0, 0, .3))));padding:12px;z-index:10000;min-width:200px;max-width:280px;max-height:350px;display:flex;flex-direction:column;font-family:var(--tbw-font-family, system-ui, sans-serif);font-size:var(--tbw-font-size, 13px)}.tbw-filter-search{margin-bottom:8px}.tbw-filter-search-input{width:100%;padding:6px 10px;background:var(--tbw-filter-input-bg, var(--tbw-color-bg, transparent));color:inherit;border:1px solid var(--tbw-filter-input-border, var(--tbw-color-border, light-dark(#d0d0d4, #454545)));border-radius:var(--tbw-filter-input-radius, 4px);font-size:inherit;box-sizing:border-box}.tbw-filter-search-input:focus{outline:none;border-color:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6));box-shadow:0 0 0 2px rgba(from var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6)) r g b / 15%)}.tbw-filter-actions{display:flex;padding:4px 2px;margin-bottom:8px;border-bottom:1px solid var(--tbw-filter-divider, var(--tbw-color-border, light-dark(#d0d0d4, #454545)))}.tbw-filter-action-btn{background:transparent;border:none;color:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6));cursor:pointer;font-size:12px;padding:2px 0}.tbw-filter-action-btn:hover{text-decoration:underline}.tbw-filter-values{flex:1;overflow-y:auto;margin-bottom:8px;max-height:180px;position:relative}.tbw-filter-values-spacer{width:1px}.tbw-filter-values-content{position:absolute;top:0;left:0;right:0}.tbw-filter-value-item{display:flex;align-items:center;gap:8px;padding:4px 2px;cursor:pointer;border-radius:3px}.tbw-filter-value-item:hover{background:var(--tbw-filter-hover, var(--tbw-color-row-hover, light-dark(#f0f6ff, #1c1c1c)))}.tbw-filter-checkbox{margin:0;cursor:pointer;accent-color:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6))}.tbw-filter-no-match{color:var(--tbw-filter-muted, var(--tbw-color-fg-muted, light-dark(#555555, #aaaaaa)));padding:8px 0;text-align:center;font-style:italic}.tbw-filter-buttons{display:flex;gap:8px;padding-top:8px;border-top:1px solid var(--tbw-filter-divider, var(--tbw-color-border, light-dark(#d0d0d4, #454545)))}.tbw-filter-apply-btn{flex:1;padding:6px 12px;background:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6));color:var(--tbw-filter-accent-fg, var(--tbw-color-accent-fg, light-dark(#ffffff, #000000)));border:none;border-radius:4px;cursor:pointer;font-size:13px}.tbw-filter-apply-btn:hover{filter:brightness(.9)}.tbw-filter-clear-btn{flex:1;padding:6px 12px;background:transparent;color:var(--tbw-filter-muted, var(--tbw-color-fg-muted, light-dark(#555555, #aaaaaa)));border:1px solid var(--tbw-filter-input-border, var(--tbw-color-border, light-dark(#d0d0d4, #454545)));border-radius:4px;cursor:pointer;font-size:13px}.tbw-filter-clear-btn:hover{background:var(--tbw-filter-hover, var(--tbw-color-row-hover, light-dark(#f0f6ff, #1c1c1c)))}";
246
+ class x extends _ {
247
247
  name = "filtering";
248
248
  version = "1.0.0";
249
249
  get defaultConfig() {
@@ -283,6 +283,8 @@ class x extends V {
283
283
  processRows(e) {
284
284
  const t = [...this.filters.values()];
285
285
  if (!t.length) return [...e];
286
+ if (this.config.filterHandler)
287
+ return this.cachedResult ? this.cachedResult : [...e];
286
288
  const r = z(t);
287
289
  if (this.cacheKey === r && this.cachedResult)
288
290
  return this.cachedResult;
@@ -295,14 +297,19 @@ class x extends V {
295
297
  e.querySelectorAll('[part~="header-cell"]').forEach((r) => {
296
298
  const l = r.getAttribute("data-col");
297
299
  if (l === null) return;
298
- const a = this.columns[parseInt(l, 10)];
299
- if (!a || a.filterable === !1 || r.querySelector(".tbw-filter-btn")) return;
300
- const i = a.field;
301
- if (!i) return;
302
- const n = document.createElement("button");
303
- n.className = "tbw-filter-btn", n.setAttribute("aria-label", `Filter ${a.header ?? i}`), n.innerHTML = '<svg viewBox="0 0 16 16" width="12" height="12"><path fill="currentColor" d="M6 10.5a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 0 1h-3a.5.5 0 0 1-.5-.5zm-2-3a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5zm-2-3a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5z"/></svg>', this.filters.has(i) && (n.classList.add("active"), r.classList.add("filtered")), n.addEventListener("click", (u) => {
304
- u.stopPropagation(), this.toggleFilterPanel(i, a, n);
305
- }), r.appendChild(n);
300
+ const n = this.visibleColumns[parseInt(l, 10)];
301
+ if (!n || n.filterable === !1) return;
302
+ const o = n.field;
303
+ if (!o) return;
304
+ const i = this.filters.has(o);
305
+ let a = r.querySelector(".tbw-filter-btn");
306
+ if (a) {
307
+ a.classList.toggle("active", i), r.classList.toggle("filtered", i);
308
+ return;
309
+ }
310
+ a = document.createElement("button"), a.className = "tbw-filter-btn", a.setAttribute("aria-label", `Filter ${n.header ?? o}`), a.innerHTML = '<svg viewBox="0 0 16 16" width="12" height="12"><path fill="currentColor" d="M6 10.5a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 0 1h-3a.5.5 0 0 1-.5-.5zm-2-3a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5zm-2-3a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5z"/></svg>', i && (a.classList.add("active"), r.classList.add("filtered")), a.addEventListener("click", (f) => {
311
+ f.stopPropagation(), this.toggleFilterPanel(o, n, a);
312
+ }), r.appendChild(a);
306
313
  });
307
314
  }
308
315
  // #endregion
@@ -312,7 +319,7 @@ class x extends V {
312
319
  * Pass null to remove the filter.
313
320
  */
314
321
  setFilter(e, t) {
315
- t === null ? this.filters.delete(e) : this.filters.set(e, { ...t, field: e }), this.cachedResult = null, this.cacheKey = null, this.emit("filter-change", {
322
+ t === null ? (this.filters.delete(e), this.excludedValues.delete(e)) : (this.filters.set(e, { ...t, field: e }), t.type === "set" && t.operator === "notIn" && Array.isArray(t.value) ? this.excludedValues.set(e, new Set(t.value)) : t.type === "set" && this.excludedValues.delete(e)), this.cachedResult = null, this.cacheKey = null, this.emit("filter-change", {
316
323
  filters: [...this.filters.values()],
317
324
  filteredRowCount: 0
318
325
  // Will be accurate after processRows
@@ -340,9 +347,9 @@ class x extends V {
340
347
  * Set filters from an array (replaces all existing filters).
341
348
  */
342
349
  setFilterModel(e) {
343
- this.filters.clear();
350
+ this.filters.clear(), this.excludedValues.clear();
344
351
  for (const t of e)
345
- this.filters.set(t.field, t);
352
+ this.filters.set(t.field, t), t.type === "set" && t.operator === "notIn" && Array.isArray(t.value) && this.excludedValues.set(t.field, new Set(t.value));
346
353
  this.cachedResult = null, this.cacheKey = null, this.emit("filter-change", {
347
354
  filters: [...this.filters.values()],
348
355
  filteredRowCount: 0
@@ -352,19 +359,13 @@ class x extends V {
352
359
  * Clear all filters.
353
360
  */
354
361
  clearAllFilters() {
355
- this.filters.clear(), this.excludedValues.clear(), this.searchText.clear(), this.cachedResult = null, this.cacheKey = null, this.emit("filter-change", {
356
- filters: [],
357
- filteredRowCount: this.rows.length
358
- }), this.requestRender();
362
+ this.filters.clear(), this.excludedValues.clear(), this.searchText.clear(), this.applyFiltersInternal();
359
363
  }
360
364
  /**
361
365
  * Clear filter for a specific field.
362
366
  */
363
367
  clearFieldFilter(e) {
364
- this.filters.delete(e), this.excludedValues.delete(e), this.searchText.delete(e), this.cachedResult = null, this.cacheKey = null, this.emit("filter-change", {
365
- filters: [...this.filters.values()],
366
- filteredRowCount: 0
367
- }), this.requestRender();
368
+ this.filters.delete(e), this.excludedValues.delete(e), this.searchText.delete(e), this.applyFiltersInternal();
368
369
  }
369
370
  /**
370
371
  * Check if a field has an active filter.
@@ -389,7 +390,7 @@ class x extends V {
389
390
  * Uses sourceRows to include all values regardless of current filter.
390
391
  */
391
392
  getUniqueValues(e) {
392
- return H(this.sourceRows, e);
393
+ return N(this.sourceRows, e);
393
394
  }
394
395
  // #endregion
395
396
  // #region Private Methods
@@ -403,7 +404,7 @@ class x extends V {
403
404
  return;
404
405
  }
405
406
  const e = document.createElement("style");
406
- e.id = "tbw-filter-panel-styles", e.textContent = $, document.head.appendChild(e), this.globalStylesInjected = !0;
407
+ e.id = "tbw-filter-panel-styles", e.textContent = K, document.head.appendChild(e), this.globalStylesInjected = !0;
407
408
  }
408
409
  /**
409
410
  * Toggle the filter panel for a field
@@ -415,33 +416,50 @@ class x extends V {
415
416
  }
416
417
  this.closeFilterPanel();
417
418
  const l = document.createElement("div");
418
- l.className = "tbw-filter-panel", this.panelElement = l, this.openPanelField = e;
419
- const a = H(this.sourceRows, e);
420
- let i = this.excludedValues.get(e);
421
- i || (i = /* @__PURE__ */ new Set(), this.excludedValues.set(e, i));
422
- const n = this.searchText.get(e) ?? "", u = {
419
+ if (l.className = "tbw-filter-panel", this.panelElement = l, this.openPanelField = e, this.config.valuesHandler) {
420
+ l.innerHTML = '<div class="tbw-filter-loading">Loading...</div>', document.body.appendChild(l), this.positionPanel(l, r), this.setupPanelCloseHandler(l, r), this.config.valuesHandler(e, t).then((o) => {
421
+ this.openPanelField !== e || !this.panelElement || (l.innerHTML = "", this.renderPanelContent(e, t, l, o));
422
+ });
423
+ return;
424
+ }
425
+ const n = N(this.sourceRows, e);
426
+ this.renderPanelContent(e, t, l, n), document.body.appendChild(l), this.positionPanel(l, r), this.setupPanelCloseHandler(l, r);
427
+ }
428
+ /**
429
+ * Render filter panel content with given values
430
+ */
431
+ renderPanelContent(e, t, r, l) {
432
+ let n = this.excludedValues.get(e);
433
+ n || (n = /* @__PURE__ */ new Set(), this.excludedValues.set(e, n));
434
+ const o = this.searchText.get(e) ?? "", i = {
423
435
  field: e,
424
436
  column: t,
425
- uniqueValues: a,
426
- excludedValues: i,
427
- searchText: n,
428
- applySetFilter: (h) => {
429
- this.applySetFilter(e, h), this.closeFilterPanel();
437
+ uniqueValues: l,
438
+ excludedValues: n,
439
+ searchText: o,
440
+ applySetFilter: (f) => {
441
+ this.applySetFilter(e, f), this.closeFilterPanel();
430
442
  },
431
- applyTextFilter: (h, E, C) => {
432
- this.applyTextFilter(e, h, E, C), this.closeFilterPanel();
443
+ applyTextFilter: (f, g, C) => {
444
+ this.applyTextFilter(e, f, g, C), this.closeFilterPanel();
433
445
  },
434
446
  clearFilter: () => {
435
447
  this.clearFieldFilter(e), this.closeFilterPanel();
436
448
  },
437
449
  closePanel: () => this.closeFilterPanel()
438
450
  };
439
- let g = !1;
440
- this.config.filterPanelRenderer && (this.config.filterPanelRenderer(l, u), g = l.children.length > 0), g || this.renderDefaultFilterPanel(l, u, a, i), document.body.appendChild(l), this.positionPanel(l, r), this.panelAbortController = new AbortController(), setTimeout(() => {
451
+ let a = !1;
452
+ this.config.filterPanelRenderer && (this.config.filterPanelRenderer(r, i), a = r.children.length > 0), a || this.renderDefaultFilterPanel(r, i, l, n);
453
+ }
454
+ /**
455
+ * Setup click-outside handler to close the panel
456
+ */
457
+ setupPanelCloseHandler(e, t) {
458
+ this.panelAbortController = new AbortController(), setTimeout(() => {
441
459
  document.addEventListener(
442
460
  "click",
443
- (h) => {
444
- !l.contains(h.target) && h.target !== r && this.closeFilterPanel();
461
+ (r) => {
462
+ !e.contains(r.target) && r.target !== t && this.closeFilterPanel();
445
463
  },
446
464
  { signal: this.panelAbortController?.signal }
447
465
  );
@@ -467,112 +485,112 @@ class x extends V {
467
485
  * Render the default filter panel content
468
486
  */
469
487
  renderDefaultFilterPanel(e, t, r, l) {
470
- const { field: a } = t, i = document.createElement("div");
471
- i.className = "tbw-filter-search";
472
- const n = document.createElement("input");
473
- n.type = "text", n.placeholder = "Search...", n.className = "tbw-filter-search-input", n.value = this.searchText.get(a) ?? "", i.appendChild(n), e.appendChild(i);
474
- const u = document.createElement("div");
475
- u.className = "tbw-filter-actions";
476
- const g = document.createElement("label");
477
- g.className = "tbw-filter-value-item", g.style.padding = "0", g.style.margin = "0";
478
- const h = document.createElement("input");
479
- h.type = "checkbox", h.className = "tbw-filter-checkbox";
480
- const E = document.createElement("span");
481
- E.textContent = "Select All", g.appendChild(h), g.appendChild(E), u.appendChild(g);
482
- const C = () => {
483
- const s = [...v.values()], d = s.every((c) => c), p = s.every((c) => !c);
484
- h.checked = d, h.indeterminate = !d && !p;
488
+ const { field: n } = t, o = document.createElement("div");
489
+ o.className = "tbw-filter-search";
490
+ const i = document.createElement("input");
491
+ i.type = "text", i.placeholder = "Search...", i.className = "tbw-filter-search-input", i.value = this.searchText.get(n) ?? "", o.appendChild(i), e.appendChild(o);
492
+ const a = document.createElement("div");
493
+ a.className = "tbw-filter-actions";
494
+ const f = document.createElement("label");
495
+ f.className = "tbw-filter-value-item", f.style.padding = "0", f.style.margin = "0";
496
+ const g = document.createElement("input");
497
+ g.type = "checkbox", g.className = "tbw-filter-checkbox";
498
+ const C = document.createElement("span");
499
+ C.textContent = "Select All", f.appendChild(g), f.appendChild(C), a.appendChild(f);
500
+ const k = () => {
501
+ const s = [...m.values()], u = s.every((d) => d), h = s.every((d) => !d);
502
+ g.checked = u, g.indeterminate = !u && !h;
485
503
  };
486
- h.addEventListener("change", () => {
487
- const s = h.checked;
488
- for (const d of v.keys())
489
- v.set(d, s);
490
- C(), F();
491
- }), e.appendChild(u);
492
- const m = document.createElement("div");
493
- m.className = "tbw-filter-values";
494
- const R = document.createElement("div");
495
- R.className = "tbw-filter-values-spacer", m.appendChild(R);
504
+ g.addEventListener("change", () => {
505
+ const s = g.checked;
506
+ for (const u of m.keys())
507
+ m.set(u, s);
508
+ k(), F();
509
+ }), e.appendChild(a);
510
+ const v = document.createElement("div");
511
+ v.className = "tbw-filter-values";
512
+ const S = document.createElement("div");
513
+ S.className = "tbw-filter-values-spacer", v.appendChild(S);
496
514
  const b = document.createElement("div");
497
- b.className = "tbw-filter-values-content", m.appendChild(b);
498
- const v = /* @__PURE__ */ new Map();
515
+ b.className = "tbw-filter-values-content", v.appendChild(b);
516
+ const m = /* @__PURE__ */ new Map();
499
517
  r.forEach((s) => {
500
- const d = s == null ? "__null__" : String(s);
501
- v.set(d, !l.has(s));
502
- }), C();
518
+ const u = s == null ? "__null__" : String(s);
519
+ m.set(u, !l.has(s));
520
+ }), k();
503
521
  let y = [];
504
- const I = (s, d) => {
505
- const p = s == null ? "(Blank)" : String(s), c = s == null ? "__null__" : String(s), o = document.createElement("label");
506
- 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";
522
+ const I = (s, u) => {
523
+ const h = s == null ? "(Blank)" : String(s), d = s == null ? "__null__" : String(s), c = document.createElement("label");
524
+ c.className = "tbw-filter-value-item", c.style.position = "absolute", c.style.top = `${u * x.LIST_ITEM_HEIGHT}px`, c.style.left = "0", c.style.right = "0", c.style.height = `${x.LIST_ITEM_HEIGHT}px`, c.style.boxSizing = "border-box";
507
525
  const w = document.createElement("input");
508
- w.type = "checkbox", w.className = "tbw-filter-checkbox", w.checked = v.get(c) ?? !0, w.dataset.value = c, w.addEventListener("change", () => {
509
- v.set(c, w.checked), C();
526
+ w.type = "checkbox", w.className = "tbw-filter-checkbox", w.checked = m.get(d) ?? !0, w.dataset.value = d, w.addEventListener("change", () => {
527
+ m.set(d, w.checked), k();
510
528
  });
511
- const A = document.createElement("span");
512
- return A.textContent = p, o.appendChild(w), o.appendChild(A), o;
529
+ const H = document.createElement("span");
530
+ return H.textContent = h, c.appendChild(w), c.appendChild(H), c;
513
531
  }, F = () => {
514
- const s = y.length, d = m.clientHeight, p = m.scrollTop;
515
- if (R.style.height = `${s * x.LIST_ITEM_HEIGHT}px`, P(s, x.LIST_BYPASS_THRESHOLD / 3)) {
516
- b.innerHTML = "", b.style.transform = "translateY(0px)", y.forEach((o, w) => {
517
- b.appendChild(I(o, w));
532
+ const s = y.length, u = v.clientHeight, h = v.scrollTop;
533
+ if (S.style.height = `${s * x.LIST_ITEM_HEIGHT}px`, M(s, x.LIST_BYPASS_THRESHOLD / 3)) {
534
+ b.innerHTML = "", b.style.transform = "translateY(0px)", y.forEach((c, w) => {
535
+ b.appendChild(I(c, w));
518
536
  });
519
537
  return;
520
538
  }
521
- const c = M({
539
+ const d = P({
522
540
  totalRows: s,
523
- viewportHeight: d,
524
- scrollTop: p,
541
+ viewportHeight: u,
542
+ scrollTop: h,
525
543
  rowHeight: x.LIST_ITEM_HEIGHT,
526
544
  overscan: x.LIST_OVERSCAN
527
545
  });
528
- b.style.transform = `translateY(${c.offsetY}px)`, b.innerHTML = "";
529
- for (let o = c.start; o < c.end; o++)
530
- b.appendChild(I(y[o], o - c.start));
546
+ b.style.transform = `translateY(${d.offsetY}px)`, b.innerHTML = "";
547
+ for (let c = d.start; c < d.end; c++)
548
+ b.appendChild(I(y[c], c - d.start));
531
549
  }, L = (s) => {
532
- const d = s.toLowerCase();
533
- if (y = r.filter((p) => {
534
- const c = p == null ? "(Blank)" : String(p);
535
- return !s || c.toLowerCase().includes(d);
550
+ const u = s.toLowerCase();
551
+ if (y = r.filter((h) => {
552
+ const d = h == null ? "(Blank)" : String(h);
553
+ return !s || d.toLowerCase().includes(u);
536
554
  }), y.length === 0) {
537
- R.style.height = "0px", b.innerHTML = "";
538
- const p = document.createElement("div");
539
- p.className = "tbw-filter-no-match", p.textContent = "No matching values", b.appendChild(p);
555
+ S.style.height = "0px", b.innerHTML = "";
556
+ const h = document.createElement("div");
557
+ h.className = "tbw-filter-no-match", h.textContent = "No matching values", b.appendChild(h);
540
558
  return;
541
559
  }
542
560
  F();
543
561
  };
544
- m.addEventListener(
562
+ v.addEventListener(
545
563
  "scroll",
546
564
  () => {
547
565
  y.length > 0 && F();
548
566
  },
549
567
  { passive: !0 }
550
- ), L(n.value), e.appendChild(m);
551
- let N;
552
- n.addEventListener("input", () => {
553
- clearTimeout(N), N = setTimeout(() => {
554
- this.searchText.set(a, n.value), L(n.value);
568
+ ), L(i.value), e.appendChild(v);
569
+ let A;
570
+ i.addEventListener("input", () => {
571
+ clearTimeout(A), A = setTimeout(() => {
572
+ this.searchText.set(n, i.value), L(i.value);
555
573
  }, this.config.debounceMs ?? 150);
556
574
  });
557
- const S = document.createElement("div");
558
- S.className = "tbw-filter-buttons";
559
- const T = document.createElement("button");
560
- T.className = "tbw-filter-apply-btn", T.textContent = "Apply", T.addEventListener("click", () => {
575
+ const T = document.createElement("div");
576
+ T.className = "tbw-filter-buttons";
577
+ const E = document.createElement("button");
578
+ E.className = "tbw-filter-apply-btn", E.textContent = "Apply", E.addEventListener("click", () => {
561
579
  const s = [];
562
- for (const [d, p] of v)
563
- if (!p)
564
- if (d === "__null__")
580
+ for (const [u, h] of m)
581
+ if (!h)
582
+ if (u === "__null__")
565
583
  s.push(null);
566
584
  else {
567
- const c = r.find((o) => String(o) === d);
568
- s.push(c !== void 0 ? c : d);
585
+ const d = r.find((c) => String(c) === u);
586
+ s.push(d !== void 0 ? d : u);
569
587
  }
570
588
  t.applySetFilter(s);
571
- }), S.appendChild(T);
572
- const k = document.createElement("button");
573
- k.className = "tbw-filter-clear-btn", k.textContent = "Clear Filter", k.addEventListener("click", () => {
589
+ }), T.appendChild(E);
590
+ const R = document.createElement("button");
591
+ R.className = "tbw-filter-clear-btn", R.textContent = "Clear Filter", R.addEventListener("click", () => {
574
592
  t.clearFilter();
575
- }), S.appendChild(k), e.appendChild(S);
593
+ }), T.appendChild(R), e.appendChild(T);
576
594
  }
577
595
  /**
578
596
  * Apply a set filter (exclude values)
@@ -583,10 +601,7 @@ class x extends V {
583
601
  type: "set",
584
602
  operator: "notIn",
585
603
  value: t
586
- }), this.cachedResult = null, this.cacheKey = null, this.emit("filter-change", {
587
- filters: [...this.filters.values()],
588
- filteredRowCount: 0
589
- }), this.requestRender();
604
+ }), this.applyFiltersInternal();
590
605
  }
591
606
  /**
592
607
  * Apply a text filter
@@ -598,8 +613,28 @@ class x extends V {
598
613
  operator: t,
599
614
  value: r,
600
615
  valueTo: l
601
- }), this.cachedResult = null, this.cacheKey = null, this.emit("filter-change", {
602
- filters: [...this.filters.values()],
616
+ }), this.applyFiltersInternal();
617
+ }
618
+ /**
619
+ * Internal method to apply filters (sync or async based on config)
620
+ */
621
+ applyFiltersInternal() {
622
+ this.cachedResult = null, this.cacheKey = null;
623
+ const e = [...this.filters.values()];
624
+ if (this.config.filterHandler) {
625
+ const t = this.grid;
626
+ t.setAttribute("aria-busy", "true");
627
+ const r = this.config.filterHandler(e, this.sourceRows), l = (n) => {
628
+ t.removeAttribute("aria-busy"), this.cachedResult = n, this.grid.rows = n, this.emit("filter-change", {
629
+ filters: e,
630
+ filteredRowCount: n.length
631
+ }), this.requestRender();
632
+ };
633
+ r && typeof r.then == "function" ? r.then(l) : l(r);
634
+ return;
635
+ }
636
+ this.emit("filter-change", {
637
+ filters: e,
603
638
  filteredRowCount: 0
604
639
  }), this.requestRender();
605
640
  }
@@ -639,7 +674,7 @@ class x extends V {
639
674
  }
640
675
  // #endregion
641
676
  // #region Styles
642
- styles = K;
677
+ styles = $;
643
678
  // #endregion
644
679
  }
645
680
  export {