@toolbox-web/grid 1.17.0 → 1.18.0

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 (61) hide show
  1. package/README.md +126 -41
  2. package/all.js +1045 -935
  3. package/all.js.map +1 -1
  4. package/index.js +39 -33
  5. package/index.js.map +1 -1
  6. package/lib/core/grid.d.ts +12 -2
  7. package/lib/core/grid.d.ts.map +1 -1
  8. package/lib/core/internal/header.d.ts.map +1 -1
  9. package/lib/core/internal/keyboard.d.ts.map +1 -1
  10. package/lib/core/types.d.ts +34 -1
  11. package/lib/core/types.d.ts.map +1 -1
  12. package/lib/plugins/clipboard/index.js.map +1 -1
  13. package/lib/plugins/column-virtualization/index.js.map +1 -1
  14. package/lib/plugins/context-menu/index.js.map +1 -1
  15. package/lib/plugins/editing/EditingPlugin.d.ts.map +1 -1
  16. package/lib/plugins/editing/index.js +155 -145
  17. package/lib/plugins/editing/index.js.map +1 -1
  18. package/lib/plugins/export/index.js.map +1 -1
  19. package/lib/plugins/filtering/FilteringPlugin.d.ts +31 -0
  20. package/lib/plugins/filtering/FilteringPlugin.d.ts.map +1 -1
  21. package/lib/plugins/filtering/filter-model.d.ts +30 -3
  22. package/lib/plugins/filtering/filter-model.d.ts.map +1 -1
  23. package/lib/plugins/filtering/index.d.ts +1 -0
  24. package/lib/plugins/filtering/index.d.ts.map +1 -1
  25. package/lib/plugins/filtering/index.js +471 -361
  26. package/lib/plugins/filtering/index.js.map +1 -1
  27. package/lib/plugins/filtering/types.d.ts +32 -0
  28. package/lib/plugins/filtering/types.d.ts.map +1 -1
  29. package/lib/plugins/grouping-columns/index.js.map +1 -1
  30. package/lib/plugins/grouping-rows/index.js.map +1 -1
  31. package/lib/plugins/master-detail/index.js.map +1 -1
  32. package/lib/plugins/multi-sort/MultiSortPlugin.d.ts +4 -0
  33. package/lib/plugins/multi-sort/MultiSortPlugin.d.ts.map +1 -1
  34. package/lib/plugins/multi-sort/index.js +49 -39
  35. package/lib/plugins/multi-sort/index.js.map +1 -1
  36. package/lib/plugins/pinned-columns/index.js.map +1 -1
  37. package/lib/plugins/pinned-rows/index.js.map +1 -1
  38. package/lib/plugins/pivot/index.js.map +1 -1
  39. package/lib/plugins/print/index.js.map +1 -1
  40. package/lib/plugins/reorder/index.js +81 -78
  41. package/lib/plugins/reorder/index.js.map +1 -1
  42. package/lib/plugins/responsive/index.js +58 -55
  43. package/lib/plugins/responsive/index.js.map +1 -1
  44. package/lib/plugins/row-reorder/index.js +5 -2
  45. package/lib/plugins/row-reorder/index.js.map +1 -1
  46. package/lib/plugins/selection/index.js.map +1 -1
  47. package/lib/plugins/server-side/index.js.map +1 -1
  48. package/lib/plugins/tree/index.js.map +1 -1
  49. package/lib/plugins/undo-redo/index.js.map +1 -1
  50. package/lib/plugins/visibility/index.js.map +1 -1
  51. package/package.json +1 -1
  52. package/umd/grid.all.umd.js +29 -29
  53. package/umd/grid.all.umd.js.map +1 -1
  54. package/umd/grid.umd.js +2 -2
  55. package/umd/grid.umd.js.map +1 -1
  56. package/umd/plugins/editing.umd.js +1 -1
  57. package/umd/plugins/editing.umd.js.map +1 -1
  58. package/umd/plugins/filtering.umd.js +1 -1
  59. package/umd/plugins/filtering.umd.js.map +1 -1
  60. package/umd/plugins/multi-sort.umd.js +1 -1
  61. package/umd/plugins/multi-sort.umd.js.map +1 -1
@@ -1,19 +1,141 @@
1
- function G(x) {
2
- const { totalRows: e, viewportHeight: t, scrollTop: r, rowHeight: n, overscan: i } = x, l = Math.ceil(t / n);
3
- let u = Math.floor(r / n) - i;
4
- u < 0 && (u = 0);
5
- let a = u + l + i * 2;
6
- return a > e && (a = e), a === e && u > 0 && (u = Math.max(0, a - l - i * 2)), {
7
- start: u,
8
- end: a,
9
- offsetY: u * n,
10
- totalHeight: e * n
1
+ const B = "(Blank)";
2
+ function M(w) {
3
+ if (w instanceof Date) return w.getTime();
4
+ const e = Number(w);
5
+ return isNaN(e) ? new Date(w).getTime() : e;
6
+ }
7
+ function $(w, e, t = !1, n) {
8
+ const r = w[e.field];
9
+ if (e.operator === "blank")
10
+ return r == null || r === "";
11
+ if (e.operator === "notBlank")
12
+ return r != null && r !== "";
13
+ if (n && (e.operator === "notIn" || e.operator === "in")) {
14
+ const s = n(r, w), u = Array.isArray(s) ? s : s != null ? [s] : [];
15
+ if (e.operator === "notIn") {
16
+ const m = e.value;
17
+ return Array.isArray(m) ? u.length === 0 ? !m.includes(B) : !u.some((S) => m.includes(S)) : !0;
18
+ }
19
+ if (e.operator === "in") {
20
+ const m = e.value;
21
+ return Array.isArray(m) ? u.length === 0 ? m.includes(B) : u.some((S) => m.includes(S)) : !1;
22
+ }
23
+ }
24
+ if (e.operator === "notIn")
25
+ return r == null ? !0 : Array.isArray(e.value) && !e.value.includes(r);
26
+ if (e.operator === "in")
27
+ return Array.isArray(e.value) && e.value.includes(r);
28
+ if (r == null) return !1;
29
+ const i = String(r), a = t ? i : i.toLowerCase(), l = t ? String(e.value) : String(e.value).toLowerCase();
30
+ switch (e.operator) {
31
+ // Text operators
32
+ case "contains":
33
+ return a.includes(l);
34
+ case "notContains":
35
+ return !a.includes(l);
36
+ case "equals":
37
+ return a === l;
38
+ case "notEquals":
39
+ return a !== l;
40
+ case "startsWith":
41
+ return a.startsWith(l);
42
+ case "endsWith":
43
+ return a.endsWith(l);
44
+ // Number/Date operators (use toNumeric for Date objects and date strings)
45
+ case "lessThan":
46
+ return M(r) < M(e.value);
47
+ case "lessThanOrEqual":
48
+ return M(r) <= M(e.value);
49
+ case "greaterThan":
50
+ return M(r) > M(e.value);
51
+ case "greaterThanOrEqual":
52
+ return M(r) >= M(e.value);
53
+ case "between":
54
+ return M(r) >= M(e.value) && M(r) <= M(e.valueTo);
55
+ default:
56
+ return !0;
57
+ }
58
+ }
59
+ function K(w, e, t = !1, n) {
60
+ return e.length ? w.filter(
61
+ (r) => e.every(
62
+ (i) => $(
63
+ r,
64
+ i,
65
+ t,
66
+ n?.get(i.field)
67
+ )
68
+ )
69
+ ) : w;
70
+ }
71
+ function U(w) {
72
+ return JSON.stringify(
73
+ w.map((e) => ({
74
+ field: e.field,
75
+ operator: e.operator,
76
+ value: e.value,
77
+ valueTo: e.valueTo
78
+ }))
79
+ );
80
+ }
81
+ function O(w, e, t) {
82
+ const n = /* @__PURE__ */ new Set();
83
+ let r = !1;
84
+ for (const i of w) {
85
+ const a = i[e];
86
+ if (t) {
87
+ const l = t(a, i);
88
+ if (Array.isArray(l)) {
89
+ l.length === 0 && (r = !0);
90
+ for (const s of l)
91
+ s != null && n.add(s);
92
+ } else l != null ? n.add(l) : r = !0;
93
+ } else
94
+ a != null && n.add(a);
95
+ }
96
+ return t && r && n.add(B), [...n].sort((i, a) => typeof i == "number" && typeof a == "number" ? i - a : String(i).localeCompare(String(a)));
97
+ }
98
+ function W(w, e) {
99
+ const t = /* @__PURE__ */ new Map();
100
+ for (const { field: r, filterValue: i } of e)
101
+ t.set(r, { values: /* @__PURE__ */ new Set(), hasBlank: !1, hasExtractor: !!i });
102
+ for (const r of w)
103
+ for (const { field: i, filterValue: a } of e) {
104
+ const l = t.get(i), s = r[i];
105
+ if (a) {
106
+ const u = a(s, r);
107
+ if (Array.isArray(u)) {
108
+ u.length === 0 && (l.hasBlank = !0);
109
+ for (const m of u)
110
+ m != null && l.values.add(m);
111
+ } else u != null ? l.values.add(u) : l.hasBlank = !0;
112
+ } else
113
+ s != null && l.values.add(s);
114
+ }
115
+ const n = /* @__PURE__ */ new Map();
116
+ for (const [r, { values: i, hasBlank: a, hasExtractor: l }] of t)
117
+ l && a && i.add(B), n.set(
118
+ r,
119
+ [...i].sort((s, u) => typeof s == "number" && typeof u == "number" ? s - u : String(s).localeCompare(String(u)))
120
+ );
121
+ return n;
122
+ }
123
+ function j(w) {
124
+ const { totalRows: e, viewportHeight: t, scrollTop: n, rowHeight: r, overscan: i } = w, a = Math.ceil(t / r);
125
+ let l = Math.floor(n / r) - i;
126
+ l < 0 && (l = 0);
127
+ let s = l + a + i * 2;
128
+ return s > e && (s = e), s === e && l > 0 && (l = Math.max(0, s - a - i * 2)), {
129
+ start: l,
130
+ end: s,
131
+ offsetY: l * r,
132
+ totalHeight: e * r
11
133
  };
12
134
  }
13
- function $(x, e) {
14
- return x <= e;
135
+ function J(w, e) {
136
+ return w <= e;
15
137
  }
16
- const B = '<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>', K = {
138
+ const Y = '<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>', Q = {
17
139
  expand: "▶",
18
140
  collapse: "▼",
19
141
  sortAsc: "▲",
@@ -22,11 +144,11 @@ const B = '<svg viewBox="0 0 16 16" width="12" height="12"><path fill="currentCo
22
144
  submenuArrow: "▶",
23
145
  dragHandle: "⋮⋮",
24
146
  toolPanel: "☰",
25
- filter: B,
26
- filterActive: B,
147
+ filter: Y,
148
+ filterActive: Y,
27
149
  print: "🖨️"
28
150
  };
29
- class W {
151
+ class X {
30
152
  /**
31
153
  * Plugin dependencies - declare other plugins this one requires.
32
154
  *
@@ -161,8 +283,8 @@ class W {
161
283
  * @returns `true` if the event was cancelled (preventDefault called), `false` otherwise
162
284
  */
163
285
  emitCancelable(e, t) {
164
- const r = new CustomEvent(e, { detail: t, bubbles: !0, cancelable: !0 });
165
- return this.grid?.dispatchEvent?.(r), r.defaultPrevented;
286
+ const n = new CustomEvent(e, { detail: t, bubbles: !0, cancelable: !0 });
287
+ return this.grid?.dispatchEvent?.(n), n.defaultPrevented;
166
288
  }
167
289
  // =========================================================================
168
290
  // Event Bus - Plugin-to-Plugin Communication
@@ -316,7 +438,7 @@ class W {
316
438
  */
317
439
  get gridIcons() {
318
440
  const e = this.grid?.gridConfig?.icons ?? {};
319
- return { ...K, ...e };
441
+ return { ...Q, ...e };
320
442
  }
321
443
  // #region Animation Helpers
322
444
  /**
@@ -357,8 +479,8 @@ class W {
357
479
  get animationDuration() {
358
480
  const e = this.gridElement;
359
481
  if (e) {
360
- const t = getComputedStyle(e).getPropertyValue("--tbw-animation-duration").trim(), r = parseInt(t, 10);
361
- if (!isNaN(r)) return r;
482
+ const t = getComputedStyle(e).getPropertyValue("--tbw-animation-duration").trim(), n = parseInt(t, 10);
483
+ if (!isNaN(n)) return n;
362
484
  }
363
485
  return 200;
364
486
  }
@@ -392,78 +514,11 @@ class W {
392
514
  }
393
515
  // #endregion
394
516
  }
395
- function U(x) {
396
- return x.meta?.utility === !0;
397
- }
398
- function V(x) {
399
- if (x instanceof Date) return x.getTime();
400
- const e = Number(x);
401
- return isNaN(e) ? new Date(x).getTime() : e;
402
- }
403
- function j(x, e, t = !1) {
404
- const r = x[e.field];
405
- if (e.operator === "blank")
406
- return r == null || r === "";
407
- if (e.operator === "notBlank")
408
- return r != null && r !== "";
409
- if (e.operator === "notIn")
410
- return r == null ? !0 : Array.isArray(e.value) && !e.value.includes(r);
411
- if (e.operator === "in")
412
- return Array.isArray(e.value) && e.value.includes(r);
413
- if (r == null) return !1;
414
- const n = String(r), i = t ? n : n.toLowerCase(), l = t ? String(e.value) : String(e.value).toLowerCase();
415
- switch (e.operator) {
416
- // Text operators
417
- case "contains":
418
- return i.includes(l);
419
- case "notContains":
420
- return !i.includes(l);
421
- case "equals":
422
- return i === l;
423
- case "notEquals":
424
- return i !== l;
425
- case "startsWith":
426
- return i.startsWith(l);
427
- case "endsWith":
428
- return i.endsWith(l);
429
- // Number/Date operators (use toNumeric for Date objects and date strings)
430
- case "lessThan":
431
- return V(r) < V(e.value);
432
- case "lessThanOrEqual":
433
- return V(r) <= V(e.value);
434
- case "greaterThan":
435
- return V(r) > V(e.value);
436
- case "greaterThanOrEqual":
437
- return V(r) >= V(e.value);
438
- case "between":
439
- return V(r) >= V(e.value) && V(r) <= V(e.valueTo);
440
- default:
441
- return !0;
442
- }
443
- }
444
- function J(x, e, t = !1) {
445
- return e.length ? x.filter((r) => e.every((n) => j(r, n, t))) : x;
446
- }
447
- function Q(x) {
448
- return JSON.stringify(
449
- x.map((e) => ({
450
- field: e.field,
451
- operator: e.operator,
452
- value: e.value,
453
- valueTo: e.valueTo
454
- }))
455
- );
456
- }
457
- function O(x, e) {
458
- const t = /* @__PURE__ */ new Set();
459
- for (const r of x) {
460
- const n = r[e];
461
- n != null && t.add(n);
462
- }
463
- return [...t].sort((r, n) => typeof r == "number" && typeof n == "number" ? r - n : String(r).localeCompare(String(n)));
517
+ function Z(w) {
518
+ return w.meta?.utility === !0;
464
519
  }
465
- const X = '@layer tbw-plugins{tbw-grid .tbw-quick-filter-input{flex:1;max-width:300px;height:var(--tbw-input-height, 1.75rem);padding:var(--tbw-input-padding, 0 .5rem);border:1px solid var(--tbw-color-border);border-radius:var(--tbw-border-radius);background:var(--tbw-color-bg);color:var(--tbw-color-fg);font-size:var(--tbw-font-size-sm, .8125rem)}tbw-grid .tbw-quick-filter-input:focus{outline:none;border-color:var(--tbw-color-accent)}tbw-grid .header-cell.filtered:before{content:"";position:absolute;top:var(--tbw-spacing-xs, .25rem);right:var(--tbw-spacing-xs, .25rem);width:var(--tbw-indicator-size, .375rem);height:var(--tbw-indicator-size, .375rem);background:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6));border-radius:50%}tbw-grid .tbw-filter-btn{display:var(--tbw-filter-btn-display, inline-flex);visibility:var(--tbw-filter-btn-visibility, visible);align-items:center;justify-content:center;background:transparent;border:none;cursor:pointer;padding:2px;margin-left:var(--tbw-spacing-xs, .25rem);opacity:.4;transition:opacity .15s,visibility 0s,display 0s allow-discrete;color:inherit;vertical-align:middle;transition-behavior:allow-discrete}tbw-grid .tbw-filter-btn:hover,tbw-grid .tbw-filter-btn.active{opacity:1;visibility:visible;display:inline-flex}tbw-grid .tbw-filter-btn.active{color:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6))}tbw-grid .header-row .cell:hover .tbw-filter-btn,tbw-grid .header-row .cell.filtered .tbw-filter-btn{display:inline-flex;visibility:visible}}', Z = "@layer tbw-plugins{.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, .25rem));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:var(--tbw-panel-padding, var(--tbw-spacing-lg, .75rem));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, .8125rem);transform-origin:top center}.tbw-filter-panel.tbw-filter-panel-above{transform-origin:bottom center}.tbw-filter-panel.tbw-filter-panel-animated{animation:tbw-filter-panel-enter var(--tbw-animation-duration, .15s) var(--tbw-animation-easing, ease-out)}.tbw-filter-panel.tbw-filter-panel-above.tbw-filter-panel-animated{animation:tbw-filter-panel-enter-above var(--tbw-animation-duration, .15s) var(--tbw-animation-easing, ease-out)}@keyframes tbw-filter-panel-enter{0%{opacity:0;transform:scaleY(.3) translateY(-10px)}to{opacity:1;transform:scaleY(1) translateY(0)}}@keyframes tbw-filter-panel-enter-above{0%{opacity:0;transform:scaleY(.3) translateY(10px)}to{opacity:1;transform:scaleY(1) translateY(0)}}@supports (anchor-name: --test){.tbw-filter-panel{position-anchor:--tbw-filter-anchor;top:anchor(bottom);left:anchor(left);margin-top:4px;position-try-fallbacks:flip-inline,flip-block,flip-block flip-inline}}.tbw-filter-search{margin-bottom:var(--tbw-panel-gap, var(--tbw-spacing-md, .5rem));min-height:var(--tbw-filter-item-height, 28px)}.tbw-filter-search-input{height:var(--tbw-filter-item-height, 28px);width:100%;padding:var(--tbw-filter-search-padding, var(--tbw-spacing-sm, .375rem) var(--tbw-spacing-md, .5rem));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, var(--tbw-border-radius, .25rem));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:var(--tbw-button-padding-sm, .25rem .125rem);margin-bottom:var(--tbw-panel-gap, var(--tbw-spacing-md, .5rem));border-bottom:1px solid var(--tbw-filter-divider, var(--tbw-color-border, light-dark(#d0d0d4, #454545)));min-height:var(--tbw-filter-item-height, 28px)}.tbw-filter-actions .tbw-filter-value-item{flex:1}.tbw-filter-values{flex:1;overflow-y:auto;margin-bottom:var(--tbw-panel-gap, var(--tbw-spacing-md, .5rem));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:var(--tbw-panel-gap, var(--tbw-spacing-md, .5rem));padding:var(--tbw-button-padding-sm, .25rem .125rem);cursor:pointer;border-radius:3px;height:var(--tbw-filter-item-height, 28px)}.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:var(--tbw-panel-gap, var(--tbw-spacing-md, .5rem)) 0;text-align:center;font-style:italic}.tbw-filter-buttons{display:flex;gap:var(--tbw-panel-gap, var(--tbw-spacing-md, .5rem));padding-top:var(--tbw-panel-gap, var(--tbw-spacing-md, .5rem));border-top:1px solid var(--tbw-filter-divider, var(--tbw-color-border, light-dark(#d0d0d4, #454545)))}.tbw-filter-apply-btn{flex:1;padding:var(--tbw-filter-btn-padding, var(--tbw-button-padding, .375rem .75rem));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:var(--tbw-border-radius, .25rem);cursor:pointer;font-size:var(--tbw-font-size-sm, .8125rem);font-weight:var(--tbw-filter-btn-font-weight, 500);min-height:var(--tbw-filter-btn-min-height, auto)}.tbw-filter-apply-btn:hover{filter:brightness(.9)}.tbw-filter-clear-btn{flex:1;padding:var(--tbw-filter-btn-padding, var(--tbw-button-padding, .375rem .75rem));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:var(--tbw-border-radius, .25rem);cursor:pointer;font-size:var(--tbw-font-size-sm, .8125rem);font-weight:var(--tbw-filter-btn-font-weight, 500);min-height:var(--tbw-filter-btn-min-height, auto)}.tbw-filter-clear-btn:hover{background:var(--tbw-filter-hover, var(--tbw-color-row-hover, light-dark(#f0f6ff, #1c1c1c)))}.tbw-filter-range-inputs,.tbw-filter-date-range{display:flex;align-items:flex-end;gap:var(--tbw-spacing-sm, .375rem);margin-bottom:var(--tbw-panel-gap, var(--tbw-spacing-md, .5rem))}.tbw-filter-range-group,.tbw-filter-date-group{display:flex;flex-direction:column;gap:var(--tbw-spacing-xs, .25rem);flex:1}.tbw-filter-range-label{font-size:var(--tbw-font-size-xs, .75rem);color:var(--tbw-filter-muted, var(--tbw-color-fg-muted, light-dark(#555555, #aaaaaa)))}.tbw-filter-range-input,.tbw-filter-date-input{width:100%;height:var(--tbw-filter-item-height, 28px);padding:var(--tbw-spacing-xs, .25rem) var(--tbw-spacing-sm, .375rem);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, var(--tbw-border-radius, .25rem));font-size:inherit;box-sizing:border-box}.tbw-filter-range-input:focus,.tbw-filter-date-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-range-separator{color:var(--tbw-filter-muted, var(--tbw-color-fg-muted, light-dark(#555555, #aaaaaa)));padding-bottom:var(--tbw-spacing-xs, .25rem)}.tbw-filter-blank-option{display:flex;align-items:center;gap:var(--tbw-spacing-sm, .375rem);padding:var(--tbw-spacing-xs, .25rem) 0;margin-bottom:var(--tbw-spacing-xs, .25rem);font-size:var(--tbw-font-size-sm, .8125rem);cursor:pointer;-webkit-user-select:none;user-select:none}.tbw-filter-blank-checkbox{accent-color:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6));margin:0;cursor:pointer}.tbw-filter-date-range.tbw-filter-disabled{opacity:.4;pointer-events:none}.tbw-filter-range-slider{position:relative;height:24px;margin:var(--tbw-spacing-md, .5rem) 0 var(--tbw-panel-gap, var(--tbw-spacing-md, .5rem))}.tbw-filter-range-track{position:absolute;top:50%;left:0;right:0;height:4px;background:var(--tbw-filter-input-border, var(--tbw-color-border, light-dark(#d0d0d4, #454545)));border-radius:2px;transform:translateY(-50%)}.tbw-filter-range-fill{position:absolute;top:50%;height:4px;background:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6));border-radius:2px;transform:translateY(-50%)}.tbw-filter-range-thumb{position:absolute;top:0;width:100%;height:100%;background:none;pointer-events:none;-webkit-appearance:none;appearance:none}.tbw-filter-range-thumb::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:16px;height:16px;background:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6));border:2px solid var(--tbw-filter-panel-bg, var(--tbw-color-panel-bg, light-dark(#eeeeee, #222222)));border-radius:50%;cursor:pointer;pointer-events:all;box-shadow:0 1px 3px #0003}.tbw-filter-range-thumb::-moz-range-thumb{width:16px;height:16px;background:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6));border:2px solid var(--tbw-filter-panel-bg, var(--tbw-color-panel-bg, light-dark(#eeeeee, #222222)));border-radius:50%;cursor:pointer;pointer-events:all;box-shadow:0 1px 3px #0003}.tbw-filter-range-thumb::-webkit-slider-thumb:hover{transform:scale(1.1)}.tbw-filter-range-thumb::-moz-range-thumb:hover{transform:scale(1.1)}}";
466
- class _ extends W {
520
+ const ee = '@layer tbw-plugins{tbw-grid .tbw-quick-filter-input{flex:1;max-width:300px;height:var(--tbw-input-height, 1.75rem);padding:var(--tbw-input-padding, 0 .5rem);border:1px solid var(--tbw-color-border);border-radius:var(--tbw-border-radius);background:var(--tbw-color-bg);color:var(--tbw-color-fg);font-size:var(--tbw-font-size-sm, .8125rem)}tbw-grid .tbw-quick-filter-input:focus{outline:none;border-color:var(--tbw-color-accent)}tbw-grid .header-cell.filtered:before{content:"";position:absolute;top:var(--tbw-spacing-xs, .25rem);right:var(--tbw-spacing-xs, .25rem);width:var(--tbw-indicator-size, .375rem);height:var(--tbw-indicator-size, .375rem);background:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6));border-radius:50%}tbw-grid .tbw-filter-btn{display:var(--tbw-filter-btn-display, inline-flex);visibility:var(--tbw-filter-btn-visibility, visible);align-items:center;justify-content:center;background:transparent;border:none;cursor:pointer;padding:2px;margin-left:var(--tbw-spacing-xs, .25rem);opacity:.4;transition:opacity .15s,visibility 0s,display 0s allow-discrete;color:inherit;vertical-align:middle;transition-behavior:allow-discrete}tbw-grid .tbw-filter-btn:hover,tbw-grid .tbw-filter-btn.active{opacity:1;visibility:visible;display:inline-flex}tbw-grid .tbw-filter-btn.active{color:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6))}tbw-grid .header-row .cell:hover .tbw-filter-btn,tbw-grid .header-row .cell.filtered .tbw-filter-btn{display:inline-flex;visibility:visible}}', te = "@layer tbw-plugins{.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, .25rem));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:var(--tbw-panel-padding, var(--tbw-spacing-lg, .75rem));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, .8125rem);transform-origin:top center}.tbw-filter-panel.tbw-filter-panel-above{transform-origin:bottom center}.tbw-filter-panel.tbw-filter-panel-animated{animation:tbw-filter-panel-enter var(--tbw-animation-duration, .15s) var(--tbw-animation-easing, ease-out)}.tbw-filter-panel.tbw-filter-panel-above.tbw-filter-panel-animated{animation:tbw-filter-panel-enter-above var(--tbw-animation-duration, .15s) var(--tbw-animation-easing, ease-out)}@keyframes tbw-filter-panel-enter{0%{opacity:0;transform:scaleY(.3) translateY(-10px)}to{opacity:1;transform:scaleY(1) translateY(0)}}@keyframes tbw-filter-panel-enter-above{0%{opacity:0;transform:scaleY(.3) translateY(10px)}to{opacity:1;transform:scaleY(1) translateY(0)}}@supports (anchor-name: --test){.tbw-filter-panel{position-anchor:--tbw-filter-anchor;top:anchor(bottom);left:anchor(left);margin-top:4px;position-try-fallbacks:flip-inline,flip-block,flip-block flip-inline}}.tbw-filter-search{margin-bottom:var(--tbw-panel-gap, var(--tbw-spacing-md, .5rem));min-height:var(--tbw-filter-item-height, 28px)}.tbw-filter-search-input{height:var(--tbw-filter-item-height, 28px);width:100%;padding:var(--tbw-filter-search-padding, var(--tbw-spacing-sm, .375rem) var(--tbw-spacing-md, .5rem));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, var(--tbw-border-radius, .25rem));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:var(--tbw-button-padding-sm, .25rem .125rem);margin-bottom:var(--tbw-panel-gap, var(--tbw-spacing-md, .5rem));border-bottom:1px solid var(--tbw-filter-divider, var(--tbw-color-border, light-dark(#d0d0d4, #454545)));min-height:var(--tbw-filter-item-height, 28px)}.tbw-filter-actions .tbw-filter-value-item{flex:1}.tbw-filter-values{flex:1;overflow-y:auto;margin-bottom:var(--tbw-panel-gap, var(--tbw-spacing-md, .5rem));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:var(--tbw-panel-gap, var(--tbw-spacing-md, .5rem));padding:var(--tbw-button-padding-sm, .25rem .125rem);cursor:pointer;border-radius:3px;height:var(--tbw-filter-item-height, 28px)}.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:var(--tbw-panel-gap, var(--tbw-spacing-md, .5rem)) 0;text-align:center;font-style:italic}.tbw-filter-buttons{display:flex;gap:var(--tbw-panel-gap, var(--tbw-spacing-md, .5rem));padding-top:var(--tbw-panel-gap, var(--tbw-spacing-md, .5rem));border-top:1px solid var(--tbw-filter-divider, var(--tbw-color-border, light-dark(#d0d0d4, #454545)))}.tbw-filter-apply-btn{flex:1;padding:var(--tbw-filter-btn-padding, var(--tbw-button-padding, .375rem .75rem));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:var(--tbw-border-radius, .25rem);cursor:pointer;font-size:var(--tbw-font-size-sm, .8125rem);font-weight:var(--tbw-filter-btn-font-weight, 500);min-height:var(--tbw-filter-btn-min-height, auto)}.tbw-filter-apply-btn:hover{filter:brightness(.9)}.tbw-filter-clear-btn{flex:1;padding:var(--tbw-filter-btn-padding, var(--tbw-button-padding, .375rem .75rem));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:var(--tbw-border-radius, .25rem);cursor:pointer;font-size:var(--tbw-font-size-sm, .8125rem);font-weight:var(--tbw-filter-btn-font-weight, 500);min-height:var(--tbw-filter-btn-min-height, auto)}.tbw-filter-clear-btn:hover{background:var(--tbw-filter-hover, var(--tbw-color-row-hover, light-dark(#f0f6ff, #1c1c1c)))}.tbw-filter-range-inputs,.tbw-filter-date-range{display:flex;align-items:flex-end;gap:var(--tbw-spacing-sm, .375rem);margin-bottom:var(--tbw-panel-gap, var(--tbw-spacing-md, .5rem))}.tbw-filter-range-group,.tbw-filter-date-group{display:flex;flex-direction:column;gap:var(--tbw-spacing-xs, .25rem);flex:1}.tbw-filter-range-label{font-size:var(--tbw-font-size-xs, .75rem);color:var(--tbw-filter-muted, var(--tbw-color-fg-muted, light-dark(#555555, #aaaaaa)))}.tbw-filter-range-input,.tbw-filter-date-input{width:100%;height:var(--tbw-filter-item-height, 28px);padding:var(--tbw-spacing-xs, .25rem) var(--tbw-spacing-sm, .375rem);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, var(--tbw-border-radius, .25rem));font-size:inherit;box-sizing:border-box}.tbw-filter-range-input:focus,.tbw-filter-date-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-range-separator{color:var(--tbw-filter-muted, var(--tbw-color-fg-muted, light-dark(#555555, #aaaaaa)));padding-bottom:var(--tbw-spacing-xs, .25rem)}.tbw-filter-blank-option{display:flex;align-items:center;gap:var(--tbw-spacing-sm, .375rem);padding:var(--tbw-spacing-xs, .25rem) 0;margin-bottom:var(--tbw-spacing-xs, .25rem);font-size:var(--tbw-font-size-sm, .8125rem);cursor:pointer;-webkit-user-select:none;user-select:none}.tbw-filter-blank-checkbox{accent-color:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6));margin:0;cursor:pointer}.tbw-filter-date-range.tbw-filter-disabled{opacity:.4;pointer-events:none}.tbw-filter-range-slider{position:relative;height:24px;margin:var(--tbw-spacing-md, .5rem) 0 var(--tbw-panel-gap, var(--tbw-spacing-md, .5rem))}.tbw-filter-range-track{position:absolute;top:50%;left:0;right:0;height:4px;background:var(--tbw-filter-input-border, var(--tbw-color-border, light-dark(#d0d0d4, #454545)));border-radius:2px;transform:translateY(-50%)}.tbw-filter-range-fill{position:absolute;top:50%;height:4px;background:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6));border-radius:2px;transform:translateY(-50%)}.tbw-filter-range-thumb{position:absolute;top:0;width:100%;height:100%;background:none;pointer-events:none;-webkit-appearance:none;appearance:none}.tbw-filter-range-thumb::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:16px;height:16px;background:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6));border:2px solid var(--tbw-filter-panel-bg, var(--tbw-color-panel-bg, light-dark(#eeeeee, #222222)));border-radius:50%;cursor:pointer;pointer-events:all;box-shadow:0 1px 3px #0003}.tbw-filter-range-thumb::-moz-range-thumb{width:16px;height:16px;background:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6));border:2px solid var(--tbw-filter-panel-bg, var(--tbw-color-panel-bg, light-dark(#eeeeee, #222222)));border-radius:50%;cursor:pointer;pointer-events:all;box-shadow:0 1px 3px #0003}.tbw-filter-range-thumb::-webkit-slider-thumb:hover{transform:scale(1.1)}.tbw-filter-range-thumb::-moz-range-thumb:hover{transform:scale(1.1)}}";
521
+ class _ extends X {
467
522
  /**
468
523
  * Plugin manifest - declares events emitted by this plugin.
469
524
  * @internal
@@ -485,7 +540,7 @@ class _ extends W {
485
540
  /** @internal */
486
541
  name = "filtering";
487
542
  /** @internal */
488
- styles = X;
543
+ styles = ee;
489
544
  /** @internal */
490
545
  get defaultConfig() {
491
546
  return {
@@ -509,6 +564,18 @@ class _ extends W {
509
564
  isColumnFilterable(e) {
510
565
  return this.isFilteringEnabled() ? e.filterable !== !1 : !1;
511
566
  }
567
+ /**
568
+ * Build a map of field → filterValue extractor for columns that have one.
569
+ * Used to pass array-aware value extraction to the pure filter functions.
570
+ */
571
+ getFilterValues() {
572
+ const e = this.grid.effectiveConfig?.columns;
573
+ if (!e) return;
574
+ let t;
575
+ for (const n of e)
576
+ n.field && n.filterValue && (t || (t = /* @__PURE__ */ new Map()), t.set(n.field, n.filterValue));
577
+ return t;
578
+ }
512
579
  // #endregion
513
580
  // #region Internal State
514
581
  filters = /* @__PURE__ */ new Map();
@@ -545,6 +612,27 @@ class _ extends W {
545
612
  }
546
613
  return _.DEFAULT_LIST_ITEM_HEIGHT;
547
614
  }
615
+ /**
616
+ * Compute the inclusion (selected) map from the current filters and excluded values.
617
+ * For set filters this is: uniqueValues \ excludedValues.
618
+ * Only includes entries for fields that have a set filter.
619
+ * Uses a single-pass batch extraction to avoid iterating sourceRows per field.
620
+ */
621
+ computeSelected() {
622
+ const e = [];
623
+ for (const [r, i] of this.filters) {
624
+ if (i.type !== "set" || i.operator !== "notIn") continue;
625
+ const a = this.grid.effectiveConfig?.columns?.find((l) => l.field === r);
626
+ e.push({ field: r, filterValue: a?.filterValue });
627
+ }
628
+ if (e.length === 0) return {};
629
+ const t = W(this.sourceRows, e), n = {};
630
+ for (const { field: r } of e) {
631
+ const i = this.excludedValues.get(r), a = t.get(r) ?? [];
632
+ n[r] = i ? a.filter((l) => !i.has(l)) : a;
633
+ }
634
+ return n;
635
+ }
548
636
  /**
549
637
  * Sync excludedValues map from a filter model (for set filters).
550
638
  */
@@ -572,23 +660,23 @@ class _ extends W {
572
660
  if (e.type === "getContextMenuItems") {
573
661
  const t = e.context;
574
662
  if (!t.isHeader) return;
575
- const r = t.column;
576
- if (!r?.field || !this.isFilteringEnabled() || !this.isColumnFilterable(r)) return;
577
- const n = [], i = this.isFieldFiltered(r.field), l = this.filters.size > 0;
578
- return i && n.push({
663
+ const n = t.column;
664
+ if (!n?.field || !this.isFilteringEnabled() || !this.isColumnFilterable(n)) return;
665
+ const r = [], i = this.isFieldFiltered(n.field), a = this.filters.size > 0;
666
+ return i && r.push({
579
667
  id: "filtering/clear-column-filter",
580
668
  label: "Clear Filter",
581
669
  icon: "✕",
582
670
  order: 20,
583
- action: () => this.clearFieldFilter(r.field)
584
- }), l && n.push({
671
+ action: () => this.clearFieldFilter(n.field)
672
+ }), a && r.push({
585
673
  id: "filtering/clear-all-filters",
586
674
  label: "Clear All Filters",
587
675
  icon: "✕",
588
676
  order: 21,
589
- disabled: !l,
677
+ disabled: !a,
590
678
  action: () => this.clearAllFilters()
591
- }), n.length > 0 ? n : void 0;
679
+ }), r.length > 0 ? r : void 0;
592
680
  }
593
681
  }
594
682
  // #endregion
@@ -599,45 +687,50 @@ class _ extends W {
599
687
  if (!t.length) return [...e];
600
688
  if (this.config.filterHandler)
601
689
  return this.cachedResult ? this.cachedResult : [...e];
602
- const r = Q(t), n = {
690
+ const n = U(t), r = {
603
691
  len: e.length,
604
692
  first: e[0],
605
693
  mid: e[Math.floor(e.length / 2)],
606
694
  last: e[e.length - 1]
607
- }, i = this.cachedInputSpot != null && n.len === this.cachedInputSpot.len && n.first === this.cachedInputSpot.first && n.mid === this.cachedInputSpot.mid && n.last === this.cachedInputSpot.last;
608
- if (this.cacheKey === r && this.cachedResult && i)
695
+ }, i = this.cachedInputSpot != null && r.len === this.cachedInputSpot.len && r.first === this.cachedInputSpot.first && r.mid === this.cachedInputSpot.mid && r.last === this.cachedInputSpot.last;
696
+ if (this.cacheKey === n && this.cachedResult && i)
609
697
  return this.cachedResult;
610
- const l = J([...e], t, this.config.caseSensitive);
611
- return this.cachedResult = l, this.cacheKey = r, this.cachedInputSpot = n, l;
698
+ const a = K(
699
+ [...e],
700
+ t,
701
+ this.config.caseSensitive,
702
+ this.getFilterValues()
703
+ );
704
+ return this.cachedResult = a, this.cacheKey = n, this.cachedInputSpot = r, a;
612
705
  }
613
706
  /** @internal */
614
707
  afterRender() {
615
708
  const e = this.gridElement;
616
709
  if (!e) return;
617
- e.querySelectorAll('[part~="header-cell"]').forEach((r) => {
618
- const n = r.getAttribute("data-col");
619
- if (n === null) return;
620
- const i = this.visibleColumns[parseInt(n, 10)];
621
- if (!i || !this.isColumnFilterable(i) || U(i)) return;
622
- const l = i.field;
623
- if (!l) return;
624
- const u = this.filters.has(l);
625
- let a = r.querySelector(".tbw-filter-btn");
626
- if (a) {
627
- const y = a.classList.contains("active");
628
- if (a.classList.toggle("active", u), r.classList.toggle("filtered", u), y !== u) {
629
- const h = u ? "filterActive" : "filter";
630
- this.setIcon(a, this.resolveIcon(h));
710
+ e.querySelectorAll('[part~="header-cell"]').forEach((n) => {
711
+ const r = n.getAttribute("data-col");
712
+ if (r === null) return;
713
+ const i = this.visibleColumns[parseInt(r, 10)];
714
+ if (!i || !this.isColumnFilterable(i) || Z(i)) return;
715
+ const a = i.field;
716
+ if (!a) return;
717
+ const l = this.filters.has(a);
718
+ let s = n.querySelector(".tbw-filter-btn");
719
+ if (s) {
720
+ const S = s.classList.contains("active");
721
+ if (s.classList.toggle("active", l), n.classList.toggle("filtered", l), S !== l) {
722
+ const f = l ? "filterActive" : "filter";
723
+ this.setIcon(s, this.resolveIcon(f));
631
724
  }
632
725
  return;
633
726
  }
634
- a = document.createElement("button"), a.className = "tbw-filter-btn", a.setAttribute("aria-label", `Filter ${i.header ?? l}`);
635
- const f = u ? "filterActive" : "filter";
636
- this.setIcon(a, this.resolveIcon(f)), u && (a.classList.add("active"), r.classList.add("filtered")), a.addEventListener("click", (y) => {
637
- y.stopPropagation(), this.toggleFilterPanel(l, i, a);
727
+ s = document.createElement("button"), s.className = "tbw-filter-btn", s.setAttribute("aria-label", `Filter ${i.header ?? a}`);
728
+ const u = l ? "filterActive" : "filter";
729
+ this.setIcon(s, this.resolveIcon(u)), l && (s.classList.add("active"), n.classList.add("filtered")), s.addEventListener("click", (S) => {
730
+ S.stopPropagation(), this.toggleFilterPanel(a, i, s);
638
731
  });
639
- const N = r.querySelector(".resize-handle");
640
- N ? r.insertBefore(a, N) : r.appendChild(a);
732
+ const m = n.querySelector(".resize-handle");
733
+ m ? n.insertBefore(s, m) : n.appendChild(s);
641
734
  });
642
735
  }
643
736
  // #endregion
@@ -650,13 +743,14 @@ class _ extends W {
650
743
  if (t === null)
651
744
  this.filters.delete(e), this.syncExcludedValues(e, null);
652
745
  else {
653
- const r = { ...t, field: e };
654
- this.filters.set(e, r), this.syncExcludedValues(e, r);
746
+ const n = { ...t, field: e };
747
+ this.filters.set(e, n), this.syncExcludedValues(e, n);
655
748
  }
656
749
  this.cachedResult = null, this.cacheKey = null, this.cachedInputSpot = null, this.emit("filter-change", {
657
750
  filters: [...this.filters.values()],
658
- filteredRowCount: 0
751
+ filteredRowCount: 0,
659
752
  // Will be accurate after processRows
753
+ selected: this.computeSelected()
660
754
  }), this.emitPluginEvent("filter-applied", { filters: [...this.filters.values()] }), this.requestRender();
661
755
  }
662
756
  /**
@@ -686,7 +780,8 @@ class _ extends W {
686
780
  this.filters.set(t.field, t), this.syncExcludedValues(t.field, t);
687
781
  this.cachedResult = null, this.cacheKey = null, this.cachedInputSpot = null, this.emit("filter-change", {
688
782
  filters: [...this.filters.values()],
689
- filteredRowCount: 0
783
+ filteredRowCount: 0,
784
+ selected: this.computeSelected()
690
785
  }), this.emitPluginEvent("filter-applied", { filters: [...this.filters.values()] }), this.requestRender();
691
786
  }
692
787
  /**
@@ -722,9 +817,11 @@ class _ extends W {
722
817
  /**
723
818
  * Get unique values for a field (for set filter dropdowns).
724
819
  * Uses sourceRows to include all values regardless of current filter.
820
+ * When a column has `filterValue`, individual extracted values are returned.
725
821
  */
726
822
  getUniqueValues(e) {
727
- return O(this.sourceRows, e);
823
+ const n = this.grid.effectiveConfig?.columns?.find((r) => r.field === e)?.filterValue;
824
+ return O(this.sourceRows, e, n);
728
825
  }
729
826
  // #endregion
730
827
  // #region Private Methods
@@ -735,10 +832,10 @@ class _ extends W {
735
832
  copyGridThemeContext(e) {
736
833
  const t = this.gridElement;
737
834
  if (!t) return;
738
- for (const n of t.classList)
739
- n.startsWith("tbw-") || n === "selecting" || e.classList.add(n);
740
- const r = t.dataset.theme;
741
- r && (e.dataset.theme = r);
835
+ for (const r of t.classList)
836
+ r.startsWith("tbw-") || r === "selecting" || e.classList.add(r);
837
+ const n = t.dataset.theme;
838
+ n && (e.dataset.theme = n);
742
839
  }
743
840
  /**
744
841
  * Inject global styles for filter panel (rendered in document.body)
@@ -750,58 +847,58 @@ class _ extends W {
750
847
  return;
751
848
  }
752
849
  const e = document.createElement("style");
753
- e.id = "tbw-filter-panel-styles", e.textContent = Z, document.head.appendChild(e), this.globalStylesInjected = !0;
850
+ e.id = "tbw-filter-panel-styles", e.textContent = te, document.head.appendChild(e), this.globalStylesInjected = !0;
754
851
  }
755
852
  /**
756
853
  * Toggle the filter panel for a field
757
854
  */
758
- toggleFilterPanel(e, t, r) {
855
+ toggleFilterPanel(e, t, n) {
759
856
  if (this.openPanelField === e) {
760
857
  this.closeFilterPanel();
761
858
  return;
762
859
  }
763
860
  this.closeFilterPanel();
764
- const n = document.createElement("div");
765
- if (n.className = "tbw-filter-panel", this.copyGridThemeContext(n), this.isAnimationEnabled && n.classList.add("tbw-filter-panel-animated"), this.panelElement = n, this.openPanelField = e, this.config.valuesHandler) {
766
- n.innerHTML = '<div class="tbw-filter-loading">Loading...</div>', document.body.appendChild(n), this.positionPanel(n, r), this.setupPanelCloseHandler(n, r), this.config.valuesHandler(e, t).then((l) => {
767
- this.openPanelField !== e || !this.panelElement || (n.innerHTML = "", this.renderPanelContent(e, t, n, l));
861
+ const r = document.createElement("div");
862
+ if (r.className = "tbw-filter-panel", this.copyGridThemeContext(r), this.isAnimationEnabled && r.classList.add("tbw-filter-panel-animated"), this.panelElement = r, this.openPanelField = e, this.config.valuesHandler) {
863
+ r.innerHTML = '<div class="tbw-filter-loading">Loading...</div>', document.body.appendChild(r), this.positionPanel(r, n), this.setupPanelCloseHandler(r, n), this.config.valuesHandler(e, t).then((a) => {
864
+ this.openPanelField !== e || !this.panelElement || (r.innerHTML = "", this.renderPanelContent(e, t, r, a));
768
865
  });
769
866
  return;
770
867
  }
771
- const i = O(this.sourceRows, e);
772
- document.body.appendChild(n), this.positionPanel(n, r), this.renderPanelContent(e, t, n, i), this.setupPanelCloseHandler(n, r);
868
+ const i = O(this.sourceRows, e, t.filterValue);
869
+ document.body.appendChild(r), this.positionPanel(r, n), this.renderPanelContent(e, t, r, i), this.setupPanelCloseHandler(r, n);
773
870
  }
774
871
  /**
775
872
  * Render filter panel content with given values
776
873
  */
777
- renderPanelContent(e, t, r, n) {
874
+ renderPanelContent(e, t, n, r) {
778
875
  let i = this.excludedValues.get(e);
779
876
  i || (i = /* @__PURE__ */ new Set(), this.excludedValues.set(e, i));
780
- const l = this.searchText.get(e) ?? "", u = {
877
+ const a = this.searchText.get(e) ?? "", l = {
781
878
  field: e,
782
879
  column: t,
783
- uniqueValues: n,
880
+ uniqueValues: r,
784
881
  excludedValues: i,
785
- searchText: l,
786
- applySetFilter: (f) => {
787
- this.applySetFilter(e, f), this.closeFilterPanel();
882
+ searchText: a,
883
+ applySetFilter: (u) => {
884
+ this.applySetFilter(e, u), this.closeFilterPanel();
788
885
  },
789
- applyTextFilter: (f, N, y) => {
790
- this.applyTextFilter(e, f, N, y), this.closeFilterPanel();
886
+ applyTextFilter: (u, m, S) => {
887
+ this.applyTextFilter(e, u, m, S), this.closeFilterPanel();
791
888
  },
792
889
  clearFilter: () => {
793
890
  this.clearFieldFilter(e), this.closeFilterPanel();
794
891
  },
795
892
  closePanel: () => this.closeFilterPanel()
796
893
  };
797
- let a = !1;
798
- if (this.config.filterPanelRenderer && (this.config.filterPanelRenderer(r, u), a = r.children.length > 0), !a && t.type) {
799
- const f = this.grid.effectiveConfig.typeDefaults?.[t.type];
800
- f?.filterPanelRenderer && (f.filterPanelRenderer(r, u), a = r.children.length > 0);
894
+ let s = !1;
895
+ if (this.config.filterPanelRenderer && (this.config.filterPanelRenderer(n, l), s = n.children.length > 0), !s && t.type) {
896
+ const u = this.grid.effectiveConfig.typeDefaults?.[t.type];
897
+ u?.filterPanelRenderer && (u.filterPanelRenderer(n, l), s = n.children.length > 0);
801
898
  }
802
- if (!a) {
803
- const f = t.type;
804
- f === "number" ? this.renderNumberFilterPanel(r, u, n) : f === "date" ? this.renderDateFilterPanel(r, u, n) : this.renderDefaultFilterPanel(r, u, n, i);
899
+ if (!s) {
900
+ const u = t.type;
901
+ u === "number" ? this.renderNumberFilterPanel(n, l, r) : u === "date" ? this.renderDateFilterPanel(n, l, r) : this.renderDefaultFilterPanel(n, l, r, i);
805
902
  }
806
903
  }
807
904
  /**
@@ -811,8 +908,8 @@ class _ extends W {
811
908
  this.panelAbortController = new AbortController(), setTimeout(() => {
812
909
  document.addEventListener(
813
910
  "click",
814
- (r) => {
815
- !e.contains(r.target) && r.target !== t && this.closeFilterPanel();
911
+ (n) => {
912
+ !e.contains(n.target) && n.target !== t && this.closeFilterPanel();
816
913
  },
817
914
  { signal: this.panelAbortController?.signal }
818
915
  );
@@ -838,251 +935,260 @@ class _ extends W {
838
935
  * Uses CSS Anchor Positioning if supported, falls back to JS positioning
839
936
  */
840
937
  positionPanel(e, t) {
841
- const n = t.closest(".cell") ?? t;
842
- if (n.style.anchorName = "--tbw-filter-anchor", this.panelAnchorElement = n, _.checkAnchorPositioningSupport()) {
938
+ const r = t.closest(".cell") ?? t;
939
+ if (r.style.anchorName = "--tbw-filter-anchor", this.panelAnchorElement = r, _.checkAnchorPositioningSupport()) {
843
940
  requestAnimationFrame(() => {
844
- const l = e.getBoundingClientRect(), u = n.getBoundingClientRect();
845
- l.top < u.top && e.classList.add("tbw-filter-panel-above");
941
+ const a = e.getBoundingClientRect(), l = r.getBoundingClientRect();
942
+ a.top < l.top && e.classList.add("tbw-filter-panel-above");
846
943
  });
847
944
  return;
848
945
  }
849
- const i = n.getBoundingClientRect();
946
+ const i = r.getBoundingClientRect();
850
947
  e.style.position = "fixed", e.style.top = `${i.bottom + 4}px`, e.style.left = `${i.left}px`, requestAnimationFrame(() => {
851
- const l = e.getBoundingClientRect();
852
- l.right > window.innerWidth - 8 && (e.style.left = `${i.right - l.width}px`), l.bottom > window.innerHeight - 8 && (e.style.top = `${i.top - l.height - 4}px`, e.classList.add("tbw-filter-panel-above"));
948
+ const a = e.getBoundingClientRect();
949
+ a.right > window.innerWidth - 8 && (e.style.left = `${i.right - a.width}px`), a.bottom > window.innerHeight - 8 && (e.style.top = `${i.top - a.height - 4}px`, e.classList.add("tbw-filter-panel-above"));
853
950
  });
854
951
  }
855
952
  /**
856
953
  * Render the default filter panel content
857
954
  */
858
- renderDefaultFilterPanel(e, t, r, n) {
859
- const { field: i } = t, l = this.getListItemHeight(), u = document.createElement("div");
955
+ renderDefaultFilterPanel(e, t, n, r) {
956
+ const { field: i, column: a } = t, l = this.getListItemHeight(), s = (o) => {
957
+ if (o == null) return "(Blank)";
958
+ if (a.format && !a.filterValue) {
959
+ const d = a.format(o, void 0);
960
+ if (d) return d;
961
+ }
962
+ return String(o);
963
+ };
964
+ n = n.slice().sort((o, d) => s(o).localeCompare(s(d)));
965
+ const u = document.createElement("div");
860
966
  u.className = "tbw-filter-search";
861
- const a = document.createElement("input");
862
- a.type = "text", a.placeholder = "Search...", a.className = "tbw-filter-search-input", a.value = this.searchText.get(i) ?? "", u.appendChild(a), e.appendChild(u);
863
- const f = document.createElement("div");
864
- f.className = "tbw-filter-actions";
865
- const N = document.createElement("label");
866
- N.className = "tbw-filter-value-item", N.style.padding = "0", N.style.margin = "0";
867
- const y = document.createElement("input");
868
- y.type = "checkbox", y.className = "tbw-filter-checkbox";
869
- const h = document.createElement("span");
870
- h.textContent = "Select All", N.appendChild(y), N.appendChild(h), f.appendChild(N);
967
+ const m = document.createElement("input");
968
+ m.type = "text", m.placeholder = "Search...", m.className = "tbw-filter-search-input", m.value = this.searchText.get(i) ?? "", u.appendChild(m), e.appendChild(u);
969
+ const S = document.createElement("div");
970
+ S.className = "tbw-filter-actions";
971
+ const f = document.createElement("label");
972
+ f.className = "tbw-filter-value-item", f.style.padding = "0", f.style.margin = "0";
973
+ const v = document.createElement("input");
974
+ v.type = "checkbox", v.className = "tbw-filter-checkbox";
975
+ const L = document.createElement("span");
976
+ L.textContent = "Select All", f.appendChild(v), f.appendChild(L), S.appendChild(f);
871
977
  const C = () => {
872
- const s = [...k.values()], b = s.every((o) => o), m = s.every((o) => !o);
873
- y.checked = b, y.indeterminate = !b && !m;
978
+ const o = [...k.values()], d = o.every((p) => p), g = o.every((p) => !p);
979
+ v.checked = d, v.indeterminate = !d && !g;
874
980
  };
875
- y.addEventListener("change", () => {
876
- const s = y.checked;
877
- for (const b of k.keys())
878
- k.set(b, s);
879
- C(), L();
880
- }), e.appendChild(f);
981
+ v.addEventListener("change", () => {
982
+ const o = v.checked;
983
+ for (const d of k.keys())
984
+ k.set(d, o);
985
+ C(), A();
986
+ }), e.appendChild(S);
987
+ const T = document.createElement("div");
988
+ T.className = "tbw-filter-values";
989
+ const R = document.createElement("div");
990
+ R.className = "tbw-filter-values-spacer", T.appendChild(R);
881
991
  const F = document.createElement("div");
882
- F.className = "tbw-filter-values";
883
- const w = document.createElement("div");
884
- w.className = "tbw-filter-values-spacer", F.appendChild(w);
885
- const E = document.createElement("div");
886
- E.className = "tbw-filter-values-content", F.appendChild(E);
992
+ F.className = "tbw-filter-values-content", T.appendChild(F);
887
993
  const k = /* @__PURE__ */ new Map();
888
- r.forEach((s) => {
889
- const b = s == null ? "__null__" : String(s);
890
- k.set(b, !n.has(s));
994
+ n.forEach((o) => {
995
+ const d = o == null ? "__null__" : String(o);
996
+ k.set(d, !r.has(o));
891
997
  }), C();
892
- let T = [];
893
- const R = (s, b) => {
894
- const m = s == null ? "(Blank)" : String(s), o = s == null ? "__null__" : String(s), c = document.createElement("label");
895
- c.className = "tbw-filter-value-item", c.style.position = "absolute", c.style.top = `calc(var(--tbw-filter-item-height, 28px) * ${b})`, c.style.left = "0", c.style.right = "0", c.style.boxSizing = "border-box";
896
- const g = document.createElement("input");
897
- g.type = "checkbox", g.className = "tbw-filter-checkbox", g.checked = k.get(o) ?? !0, g.dataset.value = o, g.addEventListener("change", () => {
898
- k.set(o, g.checked), C();
998
+ let I = [];
999
+ const N = (o, d) => {
1000
+ const g = s(o), p = o == null ? "__null__" : String(o), h = document.createElement("label");
1001
+ h.className = "tbw-filter-value-item", h.style.position = "absolute", h.style.top = `calc(var(--tbw-filter-item-height, 28px) * ${d})`, h.style.left = "0", h.style.right = "0", h.style.boxSizing = "border-box";
1002
+ const y = document.createElement("input");
1003
+ y.type = "checkbox", y.className = "tbw-filter-checkbox", y.checked = k.get(p) ?? !0, y.dataset.value = p, y.addEventListener("change", () => {
1004
+ k.set(p, y.checked), C();
899
1005
  });
900
- const M = document.createElement("span");
901
- return M.textContent = m, c.appendChild(g), c.appendChild(M), c;
902
- }, L = () => {
903
- const s = T.length, b = F.clientHeight, m = F.scrollTop;
904
- if (w.style.height = `${s * l}px`, $(s, _.LIST_BYPASS_THRESHOLD / 3)) {
905
- E.innerHTML = "", E.style.transform = "translateY(0px)", T.forEach((c, g) => {
906
- E.appendChild(R(c, g));
1006
+ const c = document.createElement("span");
1007
+ return c.textContent = g, h.appendChild(y), h.appendChild(c), h;
1008
+ }, A = () => {
1009
+ const o = I.length, d = T.clientHeight, g = T.scrollTop;
1010
+ if (R.style.height = `${o * l}px`, J(o, _.LIST_BYPASS_THRESHOLD / 3)) {
1011
+ F.innerHTML = "", F.style.transform = "translateY(0px)", I.forEach((h, y) => {
1012
+ F.appendChild(N(h, y));
907
1013
  });
908
1014
  return;
909
1015
  }
910
- const o = G({
911
- totalRows: s,
912
- viewportHeight: b,
913
- scrollTop: m,
1016
+ const p = j({
1017
+ totalRows: o,
1018
+ viewportHeight: d,
1019
+ scrollTop: g,
914
1020
  rowHeight: l,
915
1021
  overscan: _.LIST_OVERSCAN
916
1022
  });
917
- E.style.transform = `translateY(${o.offsetY}px)`, E.innerHTML = "";
918
- for (let c = o.start; c < o.end; c++)
919
- E.appendChild(R(T[c], c - o.start));
920
- }, S = (s) => {
921
- const b = this.config.caseSensitive ?? !1, m = b ? s : s.toLowerCase();
922
- if (T = r.filter((o) => {
923
- const c = o == null ? "(Blank)" : String(o), g = b ? c : c.toLowerCase();
924
- return !s || g.includes(m);
925
- }), T.length === 0) {
926
- w.style.height = "0px", E.innerHTML = "";
927
- const o = document.createElement("div");
928
- o.className = "tbw-filter-no-match", o.textContent = "No matching values", E.appendChild(o);
1023
+ F.style.transform = `translateY(${p.offsetY}px)`, F.innerHTML = "";
1024
+ for (let h = p.start; h < p.end; h++)
1025
+ F.appendChild(N(I[h], h - p.start));
1026
+ }, V = (o) => {
1027
+ const d = this.config.caseSensitive ?? !1, g = d ? o : o.toLowerCase();
1028
+ if (I = n.filter((p) => {
1029
+ const h = s(p), y = d ? h : h.toLowerCase();
1030
+ return !o || y.includes(g);
1031
+ }), I.length === 0) {
1032
+ R.style.height = "0px", F.innerHTML = "";
1033
+ const p = document.createElement("div");
1034
+ p.className = "tbw-filter-no-match", p.textContent = "No matching values", F.appendChild(p);
929
1035
  return;
930
1036
  }
931
- L();
1037
+ A();
932
1038
  };
933
- F.addEventListener(
1039
+ T.addEventListener(
934
1040
  "scroll",
935
1041
  () => {
936
- T.length > 0 && L();
1042
+ I.length > 0 && A();
937
1043
  },
938
1044
  { passive: !0 }
939
- ), S(a.value), e.appendChild(F);
940
- let I;
941
- a.addEventListener("input", () => {
942
- clearTimeout(I), I = setTimeout(() => {
943
- this.searchText.set(i, a.value), S(a.value);
1045
+ ), V(m.value), e.appendChild(T);
1046
+ let H;
1047
+ m.addEventListener("input", () => {
1048
+ clearTimeout(H), H = setTimeout(() => {
1049
+ this.searchText.set(i, m.value), V(m.value);
944
1050
  }, this.config.debounceMs ?? 150);
945
1051
  });
946
- const P = document.createElement("div");
947
- P.className = "tbw-filter-buttons";
948
- const A = document.createElement("button");
949
- A.className = "tbw-filter-apply-btn", A.textContent = "Apply", A.addEventListener("click", () => {
950
- const s = [];
951
- for (const [b, m] of k)
952
- if (!m)
953
- if (b === "__null__")
954
- s.push(null);
1052
+ const x = document.createElement("div");
1053
+ x.className = "tbw-filter-buttons";
1054
+ const E = document.createElement("button");
1055
+ E.className = "tbw-filter-apply-btn", E.textContent = "Apply", E.addEventListener("click", () => {
1056
+ const o = [];
1057
+ for (const [d, g] of k)
1058
+ if (!g)
1059
+ if (d === "__null__")
1060
+ o.push(null);
955
1061
  else {
956
- const o = r.find((c) => String(c) === b);
957
- s.push(o !== void 0 ? o : b);
1062
+ const p = n.find((h) => String(h) === d);
1063
+ o.push(p !== void 0 ? p : d);
958
1064
  }
959
- t.applySetFilter(s);
960
- }), P.appendChild(A);
961
- const v = document.createElement("button");
962
- v.className = "tbw-filter-clear-btn", v.textContent = "Clear Filter", v.addEventListener("click", () => {
1065
+ t.applySetFilter(o);
1066
+ }), x.appendChild(E);
1067
+ const P = document.createElement("button");
1068
+ P.className = "tbw-filter-clear-btn", P.textContent = "Clear Filter", P.addEventListener("click", () => {
963
1069
  t.clearFilter();
964
- }), P.appendChild(v), e.appendChild(P);
1070
+ }), x.appendChild(P), e.appendChild(x);
965
1071
  }
966
1072
  /**
967
1073
  * Render a number range filter panel with min/max inputs and slider
968
1074
  */
969
- renderNumberFilterPanel(e, t, r) {
970
- const { field: n, column: i } = t, l = i.filterParams, u = i.editorParams, a = (p, D) => {
971
- if (typeof p == "number") return p;
972
- if (typeof p == "string") {
973
- const z = parseFloat(p);
1075
+ renderNumberFilterPanel(e, t, n) {
1076
+ const { field: r, column: i } = t, a = i.filterParams, l = i.editorParams, s = (b, D) => {
1077
+ if (typeof b == "number") return b;
1078
+ if (typeof b == "string") {
1079
+ const z = parseFloat(b);
974
1080
  return isNaN(z) ? D : z;
975
1081
  }
976
1082
  return D;
977
- }, f = r.filter((p) => typeof p == "number" && !isNaN(p)), N = f.length > 0 ? Math.min(...f) : 0, y = f.length > 0 ? Math.max(...f) : 100, h = a(l?.min ?? u?.min, N), C = a(l?.max ?? u?.max, y), F = l?.step ?? u?.step ?? 1, w = this.filters.get(n);
978
- let E = h, k = C;
979
- w?.operator === "between" ? (E = a(w.value, h), k = a(w.valueTo, C)) : w?.operator === "greaterThanOrEqual" ? E = a(w.value, h) : w?.operator === "lessThanOrEqual" && (k = a(w.value, C));
980
- const T = document.createElement("div");
981
- T.className = "tbw-filter-range-inputs";
982
- const R = document.createElement("div");
983
- R.className = "tbw-filter-range-group";
984
- const L = document.createElement("label");
985
- L.textContent = "Min", L.className = "tbw-filter-range-label";
986
- const S = document.createElement("input");
987
- S.type = "number", S.className = "tbw-filter-range-input", S.min = String(h), S.max = String(C), S.step = String(F), S.value = String(E), R.appendChild(L), R.appendChild(S), T.appendChild(R);
988
- const I = document.createElement("span");
989
- I.className = "tbw-filter-range-separator", I.textContent = "–", T.appendChild(I);
1083
+ }, u = n.filter((b) => typeof b == "number" && !isNaN(b)), m = u.length > 0 ? Math.min(...u) : 0, S = u.length > 0 ? Math.max(...u) : 100, f = s(a?.min ?? l?.min, m), v = s(a?.max ?? l?.max, S), L = a?.step ?? l?.step ?? 1, C = this.filters.get(r);
1084
+ let T = f, R = v;
1085
+ C?.operator === "between" ? (T = s(C.value, f), R = s(C.valueTo, v)) : C?.operator === "greaterThanOrEqual" ? T = s(C.value, f) : C?.operator === "lessThanOrEqual" && (R = s(C.value, v));
1086
+ const F = document.createElement("div");
1087
+ F.className = "tbw-filter-range-inputs";
1088
+ const k = document.createElement("div");
1089
+ k.className = "tbw-filter-range-group";
1090
+ const I = document.createElement("label");
1091
+ I.textContent = "Min", I.className = "tbw-filter-range-label";
1092
+ const N = document.createElement("input");
1093
+ N.type = "number", N.className = "tbw-filter-range-input", N.min = String(f), N.max = String(v), N.step = String(L), N.value = String(T), k.appendChild(I), k.appendChild(N), F.appendChild(k);
1094
+ const A = document.createElement("span");
1095
+ A.className = "tbw-filter-range-separator", A.textContent = "–", F.appendChild(A);
1096
+ const V = document.createElement("div");
1097
+ V.className = "tbw-filter-range-group";
1098
+ const H = document.createElement("label");
1099
+ H.textContent = "Max", H.className = "tbw-filter-range-label";
1100
+ const x = document.createElement("input");
1101
+ x.type = "number", x.className = "tbw-filter-range-input", x.min = String(f), x.max = String(v), x.step = String(L), x.value = String(R), V.appendChild(H), V.appendChild(x), F.appendChild(V), e.appendChild(F);
1102
+ const E = document.createElement("div");
1103
+ E.className = "tbw-filter-range-slider";
990
1104
  const P = document.createElement("div");
991
- P.className = "tbw-filter-range-group";
992
- const A = document.createElement("label");
993
- A.textContent = "Max", A.className = "tbw-filter-range-label";
994
- const v = document.createElement("input");
995
- v.type = "number", v.className = "tbw-filter-range-input", v.min = String(h), v.max = String(C), v.step = String(F), v.value = String(k), P.appendChild(A), P.appendChild(v), T.appendChild(P), e.appendChild(T);
996
- const s = document.createElement("div");
997
- s.className = "tbw-filter-range-slider";
998
- const b = document.createElement("div");
999
- b.className = "tbw-filter-range-track";
1000
- const m = document.createElement("div");
1001
- m.className = "tbw-filter-range-fill";
1002
- const o = document.createElement("input");
1003
- o.type = "range", o.className = "tbw-filter-range-thumb tbw-filter-range-thumb-min", o.min = String(h), o.max = String(C), o.step = String(F), o.value = String(E);
1004
- const c = document.createElement("input");
1005
- c.type = "range", c.className = "tbw-filter-range-thumb tbw-filter-range-thumb-max", c.min = String(h), c.max = String(C), c.step = String(F), c.value = String(k), s.appendChild(b), s.appendChild(m), s.appendChild(o), s.appendChild(c), e.appendChild(s);
1006
- const g = () => {
1007
- const p = parseFloat(o.value), D = parseFloat(c.value), z = C - h, q = (p - h) / z * 100, Y = (D - h) / z * 100;
1008
- m.style.left = `${q}%`, m.style.width = `${Y - q}%`;
1105
+ P.className = "tbw-filter-range-track";
1106
+ const o = document.createElement("div");
1107
+ o.className = "tbw-filter-range-fill";
1108
+ const d = document.createElement("input");
1109
+ d.type = "range", d.className = "tbw-filter-range-thumb tbw-filter-range-thumb-min", d.min = String(f), d.max = String(v), d.step = String(L), d.value = String(T);
1110
+ const g = document.createElement("input");
1111
+ g.type = "range", g.className = "tbw-filter-range-thumb tbw-filter-range-thumb-max", g.min = String(f), g.max = String(v), g.step = String(L), g.value = String(R), E.appendChild(P), E.appendChild(o), E.appendChild(d), E.appendChild(g), e.appendChild(E);
1112
+ const p = () => {
1113
+ const b = parseFloat(d.value), D = parseFloat(g.value), z = v - f, q = (b - f) / z * 100, G = (D - f) / z * 100;
1114
+ o.style.left = `${q}%`, o.style.width = `${G - q}%`;
1009
1115
  };
1010
- o.addEventListener("input", () => {
1011
- const p = Math.min(parseFloat(o.value), parseFloat(c.value));
1012
- o.value = String(p), S.value = String(p), g();
1013
- }), c.addEventListener("input", () => {
1014
- const p = Math.max(parseFloat(c.value), parseFloat(o.value));
1015
- c.value = String(p), v.value = String(p), g();
1016
- }), S.addEventListener("input", () => {
1017
- let p = parseFloat(S.value) || h;
1018
- p = Math.max(h, Math.min(p, parseFloat(v.value))), o.value = String(p), g();
1019
- }), v.addEventListener("input", () => {
1020
- let p = parseFloat(v.value) || C;
1021
- p = Math.min(C, Math.max(p, parseFloat(S.value))), c.value = String(p), g();
1022
- }), g();
1023
- const M = document.createElement("div");
1024
- M.className = "tbw-filter-buttons";
1025
- const H = document.createElement("button");
1026
- H.className = "tbw-filter-apply-btn", H.textContent = "Apply", H.addEventListener("click", () => {
1027
- const p = parseFloat(S.value), D = parseFloat(v.value);
1028
- t.applyTextFilter("between", p, D);
1029
- }), M.appendChild(H);
1030
- const d = document.createElement("button");
1031
- d.className = "tbw-filter-clear-btn", d.textContent = "Clear Filter", d.addEventListener("click", () => {
1116
+ d.addEventListener("input", () => {
1117
+ const b = Math.min(parseFloat(d.value), parseFloat(g.value));
1118
+ d.value = String(b), N.value = String(b), p();
1119
+ }), g.addEventListener("input", () => {
1120
+ const b = Math.max(parseFloat(g.value), parseFloat(d.value));
1121
+ g.value = String(b), x.value = String(b), p();
1122
+ }), N.addEventListener("input", () => {
1123
+ let b = parseFloat(N.value) || f;
1124
+ b = Math.max(f, Math.min(b, parseFloat(x.value))), d.value = String(b), p();
1125
+ }), x.addEventListener("input", () => {
1126
+ let b = parseFloat(x.value) || v;
1127
+ b = Math.min(v, Math.max(b, parseFloat(N.value))), g.value = String(b), p();
1128
+ }), p();
1129
+ const h = document.createElement("div");
1130
+ h.className = "tbw-filter-buttons";
1131
+ const y = document.createElement("button");
1132
+ y.className = "tbw-filter-apply-btn", y.textContent = "Apply", y.addEventListener("click", () => {
1133
+ const b = parseFloat(N.value), D = parseFloat(x.value);
1134
+ t.applyTextFilter("between", b, D);
1135
+ }), h.appendChild(y);
1136
+ const c = document.createElement("button");
1137
+ c.className = "tbw-filter-clear-btn", c.textContent = "Clear Filter", c.addEventListener("click", () => {
1032
1138
  t.clearFilter();
1033
- }), M.appendChild(d), e.appendChild(M);
1139
+ }), h.appendChild(c), e.appendChild(h);
1034
1140
  }
1035
1141
  /**
1036
1142
  * Render a date range filter panel with from/to date inputs
1037
1143
  */
1038
- renderDateFilterPanel(e, t, r) {
1039
- const { field: n, column: i } = t, l = i.filterParams, u = i.editorParams, a = r.filter((d) => d instanceof Date || typeof d == "string" && !isNaN(Date.parse(d))).map((d) => d instanceof Date ? d : new Date(d)).filter((d) => !isNaN(d.getTime())), f = a.length > 0 ? new Date(Math.min(...a.map((d) => d.getTime()))) : null, N = a.length > 0 ? new Date(Math.max(...a.map((d) => d.getTime()))) : null, y = (d) => d ? d.toISOString().split("T")[0] : "", h = (d) => d ? typeof d == "string" ? d : typeof d == "number" ? y(new Date(d)) : "" : "", C = h(l?.min) || h(u?.min) || y(f), F = h(l?.max) || h(u?.max) || y(N), w = this.filters.get(n);
1040
- let E = "", k = "";
1041
- const T = w?.operator === "blank";
1042
- w?.operator === "between" ? (E = h(w.value) || "", k = h(w.valueTo) || "") : w?.operator === "greaterThanOrEqual" ? E = h(w.value) || "" : w?.operator === "lessThanOrEqual" && (k = h(w.value) || "");
1043
- const R = document.createElement("div");
1044
- R.className = "tbw-filter-date-range";
1045
- const L = document.createElement("div");
1046
- L.className = "tbw-filter-date-group";
1047
- const S = document.createElement("label");
1048
- S.textContent = "From", S.className = "tbw-filter-range-label";
1049
- const I = document.createElement("input");
1050
- I.type = "date", I.className = "tbw-filter-date-input", C && (I.min = C), F && (I.max = F), I.value = E, L.appendChild(S), L.appendChild(I), R.appendChild(L);
1051
- const P = document.createElement("span");
1052
- P.className = "tbw-filter-range-separator", P.textContent = "–", R.appendChild(P);
1053
- const A = document.createElement("div");
1054
- A.className = "tbw-filter-date-group";
1055
- const v = document.createElement("label");
1056
- v.textContent = "To", v.className = "tbw-filter-range-label";
1057
- const s = document.createElement("input");
1058
- s.type = "date", s.className = "tbw-filter-date-input", C && (s.min = C), F && (s.max = F), s.value = k, A.appendChild(v), A.appendChild(s), R.appendChild(A), e.appendChild(R);
1059
- const b = document.createElement("label");
1060
- b.className = "tbw-filter-blank-option";
1061
- const m = document.createElement("input");
1062
- m.type = "checkbox", m.className = "tbw-filter-blank-checkbox", m.checked = T;
1063
- const o = document.createTextNode("Show only blank");
1064
- b.appendChild(m), b.appendChild(o);
1065
- const c = (d) => {
1066
- I.disabled = d, s.disabled = d, R.classList.toggle("tbw-filter-disabled", d);
1144
+ renderDateFilterPanel(e, t, n) {
1145
+ const { field: r, column: i } = t, a = i.filterParams, l = i.editorParams, s = n.filter((c) => c instanceof Date || typeof c == "string" && !isNaN(Date.parse(c))).map((c) => c instanceof Date ? c : new Date(c)).filter((c) => !isNaN(c.getTime())), u = s.length > 0 ? new Date(Math.min(...s.map((c) => c.getTime()))) : null, m = s.length > 0 ? new Date(Math.max(...s.map((c) => c.getTime()))) : null, S = (c) => c ? c.toISOString().split("T")[0] : "", f = (c) => c ? typeof c == "string" ? c : typeof c == "number" ? S(new Date(c)) : "" : "", v = f(a?.min) || f(l?.min) || S(u), L = f(a?.max) || f(l?.max) || S(m), C = this.filters.get(r);
1146
+ let T = "", R = "";
1147
+ const F = C?.operator === "blank";
1148
+ C?.operator === "between" ? (T = f(C.value) || "", R = f(C.valueTo) || "") : C?.operator === "greaterThanOrEqual" ? T = f(C.value) || "" : C?.operator === "lessThanOrEqual" && (R = f(C.value) || "");
1149
+ const k = document.createElement("div");
1150
+ k.className = "tbw-filter-date-range";
1151
+ const I = document.createElement("div");
1152
+ I.className = "tbw-filter-date-group";
1153
+ const N = document.createElement("label");
1154
+ N.textContent = "From", N.className = "tbw-filter-range-label";
1155
+ const A = document.createElement("input");
1156
+ A.type = "date", A.className = "tbw-filter-date-input", v && (A.min = v), L && (A.max = L), A.value = T, I.appendChild(N), I.appendChild(A), k.appendChild(I);
1157
+ const V = document.createElement("span");
1158
+ V.className = "tbw-filter-range-separator", V.textContent = "–", k.appendChild(V);
1159
+ const H = document.createElement("div");
1160
+ H.className = "tbw-filter-date-group";
1161
+ const x = document.createElement("label");
1162
+ x.textContent = "To", x.className = "tbw-filter-range-label";
1163
+ const E = document.createElement("input");
1164
+ E.type = "date", E.className = "tbw-filter-date-input", v && (E.min = v), L && (E.max = L), E.value = R, H.appendChild(x), H.appendChild(E), k.appendChild(H), e.appendChild(k);
1165
+ const P = document.createElement("label");
1166
+ P.className = "tbw-filter-blank-option";
1167
+ const o = document.createElement("input");
1168
+ o.type = "checkbox", o.className = "tbw-filter-blank-checkbox", o.checked = F;
1169
+ const d = document.createTextNode("Show only blank");
1170
+ P.appendChild(o), P.appendChild(d);
1171
+ const g = (c) => {
1172
+ A.disabled = c, E.disabled = c, k.classList.toggle("tbw-filter-disabled", c);
1067
1173
  };
1068
- c(T), m.addEventListener("change", () => {
1069
- c(m.checked);
1070
- }), e.appendChild(b);
1071
- const g = document.createElement("div");
1072
- g.className = "tbw-filter-buttons";
1073
- const M = document.createElement("button");
1074
- M.className = "tbw-filter-apply-btn", M.textContent = "Apply", M.addEventListener("click", () => {
1075
- if (m.checked) {
1174
+ g(F), o.addEventListener("change", () => {
1175
+ g(o.checked);
1176
+ }), e.appendChild(P);
1177
+ const p = document.createElement("div");
1178
+ p.className = "tbw-filter-buttons";
1179
+ const h = document.createElement("button");
1180
+ h.className = "tbw-filter-apply-btn", h.textContent = "Apply", h.addEventListener("click", () => {
1181
+ if (o.checked) {
1076
1182
  t.applyTextFilter("blank", "");
1077
1183
  return;
1078
1184
  }
1079
- const d = I.value, p = s.value;
1080
- d && p ? t.applyTextFilter("between", d, p) : d ? t.applyTextFilter("greaterThanOrEqual", d) : p ? t.applyTextFilter("lessThanOrEqual", p) : t.clearFilter();
1081
- }), g.appendChild(M);
1082
- const H = document.createElement("button");
1083
- H.className = "tbw-filter-clear-btn", H.textContent = "Clear Filter", H.addEventListener("click", () => {
1185
+ const c = A.value, b = E.value;
1186
+ c && b ? t.applyTextFilter("between", c, b) : c ? t.applyTextFilter("greaterThanOrEqual", c) : b ? t.applyTextFilter("lessThanOrEqual", b) : t.clearFilter();
1187
+ }), p.appendChild(h);
1188
+ const y = document.createElement("button");
1189
+ y.className = "tbw-filter-clear-btn", y.textContent = "Clear Filter", y.addEventListener("click", () => {
1084
1190
  t.clearFilter();
1085
- }), g.appendChild(H), e.appendChild(g);
1191
+ }), p.appendChild(y), e.appendChild(p);
1086
1192
  }
1087
1193
  /**
1088
1194
  * Apply a set filter (exclude values)
@@ -1098,13 +1204,13 @@ class _ extends W {
1098
1204
  /**
1099
1205
  * Apply a text/number/date filter
1100
1206
  */
1101
- applyTextFilter(e, t, r, n) {
1207
+ applyTextFilter(e, t, n, r) {
1102
1208
  this.filters.set(e, {
1103
1209
  field: e,
1104
1210
  type: "text",
1105
1211
  operator: t,
1106
- value: r,
1107
- valueTo: n
1212
+ value: n,
1213
+ valueTo: r
1108
1214
  }), this.applyFiltersInternal();
1109
1215
  }
1110
1216
  /**
@@ -1116,18 +1222,20 @@ class _ extends W {
1116
1222
  if (this.config.filterHandler) {
1117
1223
  const t = this.grid;
1118
1224
  t.setAttribute("aria-busy", "true");
1119
- const r = this.config.filterHandler(e, this.sourceRows), n = (i) => {
1225
+ const n = this.config.filterHandler(e, this.sourceRows), r = (i) => {
1120
1226
  t.removeAttribute("aria-busy"), this.cachedResult = i, this.grid.rows = i, this.emit("filter-change", {
1121
1227
  filters: e,
1122
- filteredRowCount: i.length
1228
+ filteredRowCount: i.length,
1229
+ selected: this.computeSelected()
1123
1230
  }), this.emitPluginEvent("filter-applied", { filters: e }), this.requestRender();
1124
1231
  };
1125
- r && typeof r.then == "function" ? r.then(n) : n(r);
1232
+ n && typeof n.then == "function" ? n.then(r) : r(n);
1126
1233
  return;
1127
1234
  }
1128
1235
  this.emit("filter-change", {
1129
1236
  filters: e,
1130
- filteredRowCount: 0
1237
+ filteredRowCount: 0,
1238
+ selected: this.computeSelected()
1131
1239
  }), this.emitPluginEvent("filter-applied", { filters: e }), this.requestRender();
1132
1240
  }
1133
1241
  // #endregion
@@ -1157,18 +1265,20 @@ class _ extends W {
1157
1265
  this.filters.delete(e);
1158
1266
  return;
1159
1267
  }
1160
- const r = {
1268
+ const n = {
1161
1269
  field: e,
1162
1270
  type: t.filter.type,
1163
1271
  operator: t.filter.operator,
1164
1272
  value: t.filter.value,
1165
1273
  valueTo: t.filter.valueTo
1166
1274
  };
1167
- this.filters.set(e, r), this.cachedResult = null, this.cacheKey = null, this.cachedInputSpot = null;
1275
+ this.filters.set(e, n), this.cachedResult = null, this.cacheKey = null, this.cachedInputSpot = null;
1168
1276
  }
1169
1277
  // #endregion
1170
1278
  }
1171
1279
  export {
1172
- _ as FilteringPlugin
1280
+ B as BLANK_FILTER_VALUE,
1281
+ _ as FilteringPlugin,
1282
+ W as getUniqueValuesBatch
1173
1283
  };
1174
1284
  //# sourceMappingURL=index.js.map