@toolbox-web/grid 0.4.1 → 0.5.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 (176) hide show
  1. package/README.md +9 -13
  2. package/all.js +1678 -1588
  3. package/all.js.map +1 -1
  4. package/index.js +762 -568
  5. package/index.js.map +1 -1
  6. package/lib/core/grid.d.ts +21 -0
  7. package/lib/core/grid.d.ts.map +1 -1
  8. package/lib/core/internal/config-manager.d.ts +1 -0
  9. package/lib/core/internal/config-manager.d.ts.map +1 -1
  10. package/lib/core/internal/dom-builder.d.ts +2 -0
  11. package/lib/core/internal/dom-builder.d.ts.map +1 -1
  12. package/lib/core/internal/header.d.ts.map +1 -1
  13. package/lib/core/internal/keyboard.d.ts.map +1 -1
  14. package/lib/core/internal/resize.d.ts.map +1 -1
  15. package/lib/core/internal/rows.d.ts.map +1 -1
  16. package/lib/core/internal/shell.d.ts +19 -13
  17. package/lib/core/internal/shell.d.ts.map +1 -1
  18. package/lib/core/internal/utils.d.ts +1 -0
  19. package/lib/core/internal/utils.d.ts.map +1 -1
  20. package/lib/core/plugin/base-plugin.d.ts +70 -3
  21. package/lib/core/plugin/base-plugin.d.ts.map +1 -1
  22. package/lib/core/plugin/expander-column.d.ts +51 -0
  23. package/lib/core/plugin/expander-column.d.ts.map +1 -0
  24. package/lib/core/plugin/plugin-manager.d.ts +6 -2
  25. package/lib/core/plugin/plugin-manager.d.ts.map +1 -1
  26. package/lib/core/plugin/types.d.ts +117 -1
  27. package/lib/core/plugin/types.d.ts.map +1 -1
  28. package/lib/core/types.d.ts +10 -5
  29. package/lib/core/types.d.ts.map +1 -1
  30. package/lib/plugins/clipboard/ClipboardPlugin.d.ts +5 -4
  31. package/lib/plugins/clipboard/ClipboardPlugin.d.ts.map +1 -1
  32. package/lib/plugins/clipboard/index.d.ts +1 -1
  33. package/lib/plugins/clipboard/index.d.ts.map +1 -1
  34. package/lib/plugins/clipboard/index.js +295 -190
  35. package/lib/plugins/clipboard/index.js.map +1 -1
  36. package/lib/plugins/clipboard/types.d.ts +72 -2
  37. package/lib/plugins/clipboard/types.d.ts.map +1 -1
  38. package/lib/plugins/column-virtualization/ColumnVirtualizationPlugin.d.ts +0 -1
  39. package/lib/plugins/column-virtualization/ColumnVirtualizationPlugin.d.ts.map +1 -1
  40. package/lib/plugins/column-virtualization/index.js +143 -56
  41. package/lib/plugins/column-virtualization/index.js.map +1 -1
  42. package/lib/plugins/context-menu/ContextMenuPlugin.d.ts +0 -1
  43. package/lib/plugins/context-menu/ContextMenuPlugin.d.ts.map +1 -1
  44. package/lib/plugins/context-menu/index.js +189 -102
  45. package/lib/plugins/context-menu/index.js.map +1 -1
  46. package/lib/plugins/editing/EditingPlugin.d.ts +2 -7
  47. package/lib/plugins/editing/EditingPlugin.d.ts.map +1 -1
  48. package/lib/plugins/editing/index.js +227 -150
  49. package/lib/plugins/editing/index.js.map +1 -1
  50. package/lib/plugins/export/ExportPlugin.d.ts +0 -1
  51. package/lib/plugins/export/ExportPlugin.d.ts.map +1 -1
  52. package/lib/plugins/export/index.js +184 -97
  53. package/lib/plugins/export/index.js.map +1 -1
  54. package/lib/plugins/filtering/FilteringPlugin.d.ts +14 -3
  55. package/lib/plugins/filtering/FilteringPlugin.d.ts.map +1 -1
  56. package/lib/plugins/filtering/index.js +296 -176
  57. package/lib/plugins/filtering/index.js.map +1 -1
  58. package/lib/plugins/grouping-columns/GroupingColumnsPlugin.d.ts +2 -2
  59. package/lib/plugins/grouping-columns/GroupingColumnsPlugin.d.ts.map +1 -1
  60. package/lib/plugins/grouping-columns/grouping-columns.d.ts +1 -1
  61. package/lib/plugins/grouping-columns/grouping-columns.d.ts.map +1 -1
  62. package/lib/plugins/grouping-columns/index.js +169 -61
  63. package/lib/plugins/grouping-columns/index.js.map +1 -1
  64. package/lib/plugins/grouping-rows/GroupingRowsPlugin.d.ts +14 -2
  65. package/lib/plugins/grouping-rows/GroupingRowsPlugin.d.ts.map +1 -1
  66. package/lib/plugins/grouping-rows/index.js +243 -140
  67. package/lib/plugins/grouping-rows/index.js.map +1 -1
  68. package/lib/plugins/master-detail/MasterDetailPlugin.d.ts +13 -11
  69. package/lib/plugins/master-detail/MasterDetailPlugin.d.ts.map +1 -1
  70. package/lib/plugins/master-detail/index.js +278 -196
  71. package/lib/plugins/master-detail/index.js.map +1 -1
  72. package/lib/plugins/master-detail/types.d.ts +0 -10
  73. package/lib/plugins/master-detail/types.d.ts.map +1 -1
  74. package/lib/plugins/multi-sort/MultiSortPlugin.d.ts +1 -2
  75. package/lib/plugins/multi-sort/MultiSortPlugin.d.ts.map +1 -1
  76. package/lib/plugins/multi-sort/index.js +125 -40
  77. package/lib/plugins/multi-sort/index.js.map +1 -1
  78. package/lib/plugins/pinned-columns/PinnedColumnsPlugin.d.ts +0 -1
  79. package/lib/plugins/pinned-columns/PinnedColumnsPlugin.d.ts.map +1 -1
  80. package/lib/plugins/pinned-columns/index.js +156 -75
  81. package/lib/plugins/pinned-columns/index.js.map +1 -1
  82. package/lib/plugins/pinned-columns/pinned-columns.d.ts +2 -2
  83. package/lib/plugins/pinned-columns/pinned-columns.d.ts.map +1 -1
  84. package/lib/plugins/pinned-rows/PinnedRowsPlugin.d.ts +1 -2
  85. package/lib/plugins/pinned-rows/PinnedRowsPlugin.d.ts.map +1 -1
  86. package/lib/plugins/pinned-rows/index.js +202 -117
  87. package/lib/plugins/pinned-rows/index.js.map +1 -1
  88. package/lib/plugins/pivot/PivotPlugin.d.ts +26 -4
  89. package/lib/plugins/pivot/PivotPlugin.d.ts.map +1 -1
  90. package/lib/plugins/pivot/index.js +413 -314
  91. package/lib/plugins/pivot/index.js.map +1 -1
  92. package/lib/plugins/pivot/pivot-rows.d.ts +2 -1
  93. package/lib/plugins/pivot/pivot-rows.d.ts.map +1 -1
  94. package/lib/plugins/reorder/ReorderPlugin.d.ts +13 -10
  95. package/lib/plugins/reorder/ReorderPlugin.d.ts.map +1 -1
  96. package/lib/plugins/reorder/index.d.ts +1 -1
  97. package/lib/plugins/reorder/index.d.ts.map +1 -1
  98. package/lib/plugins/reorder/index.js +296 -223
  99. package/lib/plugins/reorder/index.js.map +1 -1
  100. package/lib/plugins/selection/SelectionPlugin.d.ts +21 -3
  101. package/lib/plugins/selection/SelectionPlugin.d.ts.map +1 -1
  102. package/lib/plugins/selection/index.d.ts +2 -2
  103. package/lib/plugins/selection/index.d.ts.map +1 -1
  104. package/lib/plugins/selection/index.js +282 -141
  105. package/lib/plugins/selection/index.js.map +1 -1
  106. package/lib/plugins/selection/types.d.ts +24 -0
  107. package/lib/plugins/selection/types.d.ts.map +1 -1
  108. package/lib/plugins/server-side/ServerSidePlugin.d.ts +0 -1
  109. package/lib/plugins/server-side/ServerSidePlugin.d.ts.map +1 -1
  110. package/lib/plugins/server-side/index.js +96 -9
  111. package/lib/plugins/server-side/index.js.map +1 -1
  112. package/lib/plugins/tree/TreePlugin.d.ts +5 -1
  113. package/lib/plugins/tree/TreePlugin.d.ts.map +1 -1
  114. package/lib/plugins/tree/index.js +209 -113
  115. package/lib/plugins/tree/index.js.map +1 -1
  116. package/lib/plugins/tree/types.d.ts +0 -10
  117. package/lib/plugins/tree/types.d.ts.map +1 -1
  118. package/lib/plugins/undo-redo/UndoRedoPlugin.d.ts +0 -1
  119. package/lib/plugins/undo-redo/UndoRedoPlugin.d.ts.map +1 -1
  120. package/lib/plugins/undo-redo/index.js +98 -11
  121. package/lib/plugins/undo-redo/index.js.map +1 -1
  122. package/lib/plugins/visibility/VisibilityPlugin.d.ts +7 -4
  123. package/lib/plugins/visibility/VisibilityPlugin.d.ts.map +1 -1
  124. package/lib/plugins/visibility/index.js +155 -64
  125. package/lib/plugins/visibility/index.js.map +1 -1
  126. package/package.json +1 -1
  127. package/themes/dg-theme-bootstrap.css +55 -53
  128. package/themes/dg-theme-contrast.css +42 -40
  129. package/themes/dg-theme-large.css +38 -37
  130. package/themes/dg-theme-material.css +54 -52
  131. package/themes/dg-theme-standard.css +19 -17
  132. package/themes/dg-theme-vibrant.css +16 -14
  133. package/umd/grid.all.umd.js +23 -24
  134. package/umd/grid.all.umd.js.map +1 -1
  135. package/umd/grid.umd.js +12 -11
  136. package/umd/grid.umd.js.map +1 -1
  137. package/umd/plugins/clipboard.umd.js +5 -7
  138. package/umd/plugins/clipboard.umd.js.map +1 -1
  139. package/umd/plugins/column-virtualization.umd.js +1 -1
  140. package/umd/plugins/column-virtualization.umd.js.map +1 -1
  141. package/umd/plugins/context-menu.umd.js +1 -1
  142. package/umd/plugins/context-menu.umd.js.map +1 -1
  143. package/umd/plugins/editing.umd.js +1 -1
  144. package/umd/plugins/editing.umd.js.map +1 -1
  145. package/umd/plugins/export.umd.js +1 -1
  146. package/umd/plugins/export.umd.js.map +1 -1
  147. package/umd/plugins/filtering.umd.js +1 -1
  148. package/umd/plugins/filtering.umd.js.map +1 -1
  149. package/umd/plugins/grouping-columns.umd.js +1 -1
  150. package/umd/plugins/grouping-columns.umd.js.map +1 -1
  151. package/umd/plugins/grouping-rows.umd.js +1 -1
  152. package/umd/plugins/grouping-rows.umd.js.map +1 -1
  153. package/umd/plugins/master-detail.umd.js +1 -1
  154. package/umd/plugins/master-detail.umd.js.map +1 -1
  155. package/umd/plugins/multi-sort.umd.js +1 -1
  156. package/umd/plugins/multi-sort.umd.js.map +1 -1
  157. package/umd/plugins/pinned-columns.umd.js +1 -1
  158. package/umd/plugins/pinned-columns.umd.js.map +1 -1
  159. package/umd/plugins/pinned-rows.umd.js +1 -1
  160. package/umd/plugins/pinned-rows.umd.js.map +1 -1
  161. package/umd/plugins/pivot.umd.js +1 -1
  162. package/umd/plugins/pivot.umd.js.map +1 -1
  163. package/umd/plugins/reorder.umd.js +1 -1
  164. package/umd/plugins/reorder.umd.js.map +1 -1
  165. package/umd/plugins/selection.umd.js +1 -1
  166. package/umd/plugins/selection.umd.js.map +1 -1
  167. package/umd/plugins/server-side.umd.js +1 -1
  168. package/umd/plugins/server-side.umd.js.map +1 -1
  169. package/umd/plugins/tree.umd.js +1 -1
  170. package/umd/plugins/tree.umd.js.map +1 -1
  171. package/umd/plugins/undo-redo.umd.js +1 -1
  172. package/umd/plugins/undo-redo.umd.js.map +1 -1
  173. package/umd/plugins/visibility.umd.js +1 -1
  174. package/umd/plugins/visibility.umd.js.map +1 -1
  175. package/lib/core/internal/editing.d.ts +0 -76
  176. package/lib/core/internal/editing.d.ts.map +0 -1
@@ -1,19 +1,19 @@
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 s = i + o + n * 2;
6
- return s > e && (s = e), s === e && i > 0 && (i = Math.max(0, s - o - n * 2)), {
7
- start: i,
8
- end: s,
9
- offsetY: i * l,
10
- totalHeight: e * l
1
+ function P(h) {
2
+ const { totalRows: e, viewportHeight: t, scrollTop: r, rowHeight: n, overscan: i } = h, l = Math.ceil(t / n);
3
+ let a = Math.floor(r / n) - i;
4
+ a < 0 && (a = 0);
5
+ let o = a + l + i * 2;
6
+ return o > e && (o = e), o === e && a > 0 && (a = Math.max(0, o - l - i * 2)), {
7
+ start: a,
8
+ end: o,
9
+ offsetY: a * n,
10
+ totalHeight: e * n
11
11
  };
12
12
  }
13
- function M(p, e) {
14
- return p <= e;
13
+ function V(h, e) {
14
+ return h <= e;
15
15
  }
16
- const V = {
16
+ const _ = {
17
17
  expand: "▶",
18
18
  collapse: "▼",
19
19
  sortAsc: "▲",
@@ -23,7 +23,7 @@ const V = {
23
23
  dragHandle: "⋮⋮",
24
24
  toolPanel: "☰"
25
25
  };
26
- class _ {
26
+ class M {
27
27
  /**
28
28
  * Plugin dependencies - declare other plugins this one requires.
29
29
  *
@@ -40,8 +40,11 @@ class _ {
40
40
  * ```
41
41
  */
42
42
  static dependencies;
43
- /** Plugin version - override in subclass if needed */
44
- version = "1.0.0";
43
+ /**
44
+ * Plugin version - defaults to grid version for built-in plugins.
45
+ * Third-party plugins can override with their own semver.
46
+ */
47
+ version = typeof __GRID_VERSION__ < "u" ? __GRID_VERSION__ : "dev";
45
48
  /** CSS styles to inject into the grid's shadow DOM */
46
49
  styles;
47
50
  /** Custom cell renderers keyed by type name */
@@ -128,12 +131,28 @@ class _ {
128
131
  emit(e, t) {
129
132
  this.grid?.dispatchEvent?.(new CustomEvent(e, { detail: t, bubbles: !0 }));
130
133
  }
134
+ /**
135
+ * Emit a cancelable custom event from the grid.
136
+ * @returns `true` if the event was cancelled (preventDefault called), `false` otherwise
137
+ */
138
+ emitCancelable(e, t) {
139
+ const r = new CustomEvent(e, { detail: t, bubbles: !0, cancelable: !0 });
140
+ return this.grid?.dispatchEvent?.(r), r.defaultPrevented;
141
+ }
131
142
  /**
132
143
  * Request a re-render of the grid.
133
144
  */
134
145
  requestRender() {
135
146
  this.grid?.requestRender?.();
136
147
  }
148
+ /**
149
+ * Request a re-render and restore focus styling afterward.
150
+ * Use this when a plugin action (like expand/collapse) triggers a render
151
+ * but needs to maintain keyboard navigation focus.
152
+ */
153
+ requestRenderWithFocus() {
154
+ this.grid?.requestRenderWithFocus?.();
155
+ }
137
156
  /**
138
157
  * Request a lightweight style update without rebuilding DOM.
139
158
  * Use this instead of requestRender() when only CSS classes need updating.
@@ -168,10 +187,34 @@ class _ {
168
187
  return this.grid?._visibleColumns ?? [];
169
188
  }
170
189
  /**
171
- * Get the shadow root of the grid.
190
+ * Get the grid as an HTMLElement for direct DOM operations.
191
+ * Use sparingly - prefer the typed GridElementRef API when possible.
192
+ *
193
+ * @example
194
+ * ```ts
195
+ * const width = this.gridElement.clientWidth;
196
+ * this.gridElement.classList.add('my-plugin-active');
197
+ * ```
198
+ */
199
+ get gridElement() {
200
+ return this.grid;
201
+ }
202
+ /**
203
+ * Get the render root of the grid for DOM queries.
204
+ * @deprecated Use `gridElement` instead. This getter exists only for backward compatibility.
205
+ *
206
+ * With Shadow DOM removed, the grid element itself is the render root.
207
+ * All new code should use `this.gridElement` for DOM queries.
208
+ *
209
+ * @example
210
+ * // OLD (deprecated)
211
+ * const rows = this.shadowRoot?.querySelector('.rows');
212
+ *
213
+ * // NEW (preferred)
214
+ * const rows = this.gridElement.querySelector('.rows');
172
215
  */
173
216
  get shadowRoot() {
174
- return this.grid?.shadowRoot ?? null;
217
+ return this.gridElement;
175
218
  }
176
219
  /**
177
220
  * Get the disconnect signal for event listener cleanup.
@@ -199,8 +242,53 @@ class _ {
199
242
  */
200
243
  get gridIcons() {
201
244
  const e = this.grid?.gridConfig?.icons ?? {};
202
- return { ...V, ...e };
245
+ return { ..._, ...e };
246
+ }
247
+ // #region Animation Helpers
248
+ /**
249
+ * Check if animations are enabled at the grid level.
250
+ * Respects gridConfig.animation.mode and the CSS variable set by the grid.
251
+ *
252
+ * Plugins should use this to skip animations when:
253
+ * - Animation mode is 'off' or `false`
254
+ * - User prefers reduced motion and mode is 'reduced-motion' (default)
255
+ *
256
+ * @example
257
+ * ```ts
258
+ * private get animationStyle(): 'slide' | 'fade' | false {
259
+ * if (!this.isAnimationEnabled) return false;
260
+ * return this.config.animation ?? 'slide';
261
+ * }
262
+ * ```
263
+ */
264
+ get isAnimationEnabled() {
265
+ const e = this.grid?.effectiveConfig?.animation?.mode ?? "reduced-motion";
266
+ if (e === !1 || e === "off") return !1;
267
+ if (e === !0 || e === "on") return !0;
268
+ const t = this.gridElement;
269
+ return t ? getComputedStyle(t).getPropertyValue("--tbw-animation-enabled").trim() !== "0" : !0;
203
270
  }
271
+ /**
272
+ * Get the animation duration in milliseconds from CSS variable.
273
+ * Falls back to 200ms if not set.
274
+ *
275
+ * Plugins can use this for their animation timing to stay consistent
276
+ * with the grid-level animation.duration setting.
277
+ *
278
+ * @example
279
+ * ```ts
280
+ * element.animate(keyframes, { duration: this.animationDuration });
281
+ * ```
282
+ */
283
+ get animationDuration() {
284
+ const e = this.gridElement;
285
+ if (e) {
286
+ const t = getComputedStyle(e).getPropertyValue("--tbw-animation-duration").trim(), r = parseInt(t, 10);
287
+ if (!isNaN(r)) return r;
288
+ }
289
+ return 200;
290
+ }
291
+ // #endregion
204
292
  /**
205
293
  * Resolve an icon value to string or HTMLElement.
206
294
  * Checks plugin config first, then grid-level icons, then defaults.
@@ -230,28 +318,31 @@ class _ {
230
318
  }
231
319
  // #endregion
232
320
  }
233
- function q(p, e, t = !1) {
234
- const r = p[e.field];
321
+ function z(h) {
322
+ return h.meta?.utility === !0;
323
+ }
324
+ function q(h, e, t = !1) {
325
+ const r = h[e.field];
235
326
  if (e.operator === "blank")
236
327
  return r == null || r === "";
237
328
  if (e.operator === "notBlank")
238
329
  return r != null && r !== "";
239
330
  if (r == null) return !1;
240
- const l = String(r), n = t ? l : l.toLowerCase(), o = t ? String(e.value) : String(e.value).toLowerCase();
331
+ const n = String(r), i = t ? n : n.toLowerCase(), l = t ? String(e.value) : String(e.value).toLowerCase();
241
332
  switch (e.operator) {
242
333
  // Text operators
243
334
  case "contains":
244
- return n.includes(o);
335
+ return i.includes(l);
245
336
  case "notContains":
246
- return !n.includes(o);
337
+ return !i.includes(l);
247
338
  case "equals":
248
- return n === o;
339
+ return i === l;
249
340
  case "notEquals":
250
- return n !== o;
341
+ return i !== l;
251
342
  case "startsWith":
252
- return n.startsWith(o);
343
+ return i.startsWith(l);
253
344
  case "endsWith":
254
- return n.endsWith(o);
345
+ return i.endsWith(l);
255
346
  // Number/Date operators (use raw numeric values)
256
347
  case "lessThan":
257
348
  return Number(r) < Number(e.value);
@@ -272,12 +363,12 @@ function q(p, e, t = !1) {
272
363
  return !0;
273
364
  }
274
365
  }
275
- function z(p, e, t = !1) {
276
- return e.length ? p.filter((r) => e.every((l) => q(r, l, t))) : p;
366
+ function B(h, e, t = !1) {
367
+ return e.length ? h.filter((r) => e.every((n) => q(r, n, t))) : h;
277
368
  }
278
- function B(p) {
369
+ function Y(h) {
279
370
  return JSON.stringify(
280
- p.map((e) => ({
371
+ h.map((e) => ({
281
372
  field: e.field,
282
373
  operator: e.operator,
283
374
  value: e.value,
@@ -285,18 +376,18 @@ function B(p) {
285
376
  }))
286
377
  );
287
378
  }
288
- function N(p, e) {
379
+ function H(h, e) {
289
380
  const t = /* @__PURE__ */ new Set();
290
- for (const r of p) {
291
- const l = r[e];
292
- l != null && t.add(l);
381
+ for (const r of h) {
382
+ const n = r[e];
383
+ n != null && t.add(n);
293
384
  }
294
- return [...t].sort((r, l) => typeof r == "number" && typeof l == "number" ? r - l : String(r).localeCompare(String(l)));
385
+ return [...t].sort((r, n) => typeof r == "number" && typeof n == "number" ? r - n : String(r).localeCompare(String(n)));
295
386
  }
296
- const $ = ':host .tbw-quick-filter-input{flex:1;max-width:300px;height:28px;padding:0 8px;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:13px}:host .tbw-quick-filter-input:focus{outline:none;border-color:var(--tbw-color-accent)}:host .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%}:host .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}:host .tbw-filter-btn:hover,:host .tbw-filter-btn.active{opacity:1}:host .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)))}";
297
- class x extends _ {
387
+ const G = '@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:inline-flex;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;color:inherit;vertical-align:middle}tbw-grid .tbw-filter-btn:hover,tbw-grid .tbw-filter-btn.active{opacity:1}tbw-grid .tbw-filter-btn.active{color:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6))}}', $ = "@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))}.tbw-filter-search-input{width:100%;padding:var(--tbw-button-padding, .375rem .625rem);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)))}.tbw-filter-action-btn{background:transparent;border:none;color:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6));cursor:pointer;font-size:var(--tbw-font-size-xs, .75rem);padding:2px 0}.tbw-filter-action-btn:hover{text-decoration:underline}.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}.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-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)}.tbw-filter-apply-btn:hover{filter:brightness(.9)}.tbw-filter-clear-btn{flex:1;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)}.tbw-filter-clear-btn:hover{background:var(--tbw-filter-hover, var(--tbw-color-row-hover, light-dark(#f0f6ff, #1c1c1c)))}}";
388
+ class g extends M {
298
389
  name = "filtering";
299
- version = "1.0.0";
390
+ styles = G;
300
391
  get defaultConfig() {
301
392
  return {
302
393
  debounceMs: 300,
@@ -311,6 +402,8 @@ class x extends _ {
311
402
  cacheKey = null;
312
403
  openPanelField = null;
313
404
  panelElement = null;
405
+ panelAnchorElement = null;
406
+ // For CSS anchor positioning cleanup
314
407
  searchText = /* @__PURE__ */ new Map();
315
408
  excludedValues = /* @__PURE__ */ new Map();
316
409
  panelAbortController = null;
@@ -321,6 +414,12 @@ class x extends _ {
321
414
  static LIST_OVERSCAN = 3;
322
415
  static LIST_BYPASS_THRESHOLD = 50;
323
416
  // Don't virtualize if < 50 items
417
+ /**
418
+ * Sync excludedValues map from a filter model (for set filters).
419
+ */
420
+ syncExcludedValues(e, t) {
421
+ t ? 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.excludedValues.delete(e);
422
+ }
324
423
  // #endregion
325
424
  // #region Lifecycle
326
425
  attach(e) {
@@ -336,33 +435,33 @@ class x extends _ {
336
435
  if (!t.length) return [...e];
337
436
  if (this.config.filterHandler)
338
437
  return this.cachedResult ? this.cachedResult : [...e];
339
- const r = B(t);
438
+ const r = Y(t);
340
439
  if (this.cacheKey === r && this.cachedResult)
341
440
  return this.cachedResult;
342
- const l = z([...e], t, this.config.caseSensitive);
343
- return this.cachedResult = l, this.cacheKey = r, l;
441
+ const n = B([...e], t, this.config.caseSensitive);
442
+ return this.cachedResult = n, this.cacheKey = r, n;
344
443
  }
345
444
  afterRender() {
346
- const e = this.shadowRoot;
445
+ const e = this.gridElement;
347
446
  if (!e) return;
348
447
  e.querySelectorAll('[part~="header-cell"]').forEach((r) => {
349
- const l = r.getAttribute("data-col");
350
- if (l === null) return;
351
- const n = this.visibleColumns[parseInt(l, 10)];
352
- if (!n || n.filterable === !1) return;
353
- const o = n.field;
354
- if (!o) return;
355
- const i = this.filters.has(o);
356
- let s = r.querySelector(".tbw-filter-btn");
357
- if (s) {
358
- s.classList.toggle("active", i), r.classList.toggle("filtered", i);
448
+ const n = r.getAttribute("data-col");
449
+ if (n === null) return;
450
+ const i = this.visibleColumns[parseInt(n, 10)];
451
+ if (!i || i.filterable === !1 || z(i)) return;
452
+ const l = i.field;
453
+ if (!l) return;
454
+ const a = this.filters.has(l);
455
+ let o = r.querySelector(".tbw-filter-btn");
456
+ if (o) {
457
+ o.classList.toggle("active", a), r.classList.toggle("filtered", a);
359
458
  return;
360
459
  }
361
- s = document.createElement("button"), s.className = "tbw-filter-btn", s.setAttribute("aria-label", `Filter ${n.header ?? o}`), s.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 && (s.classList.add("active"), r.classList.add("filtered")), s.addEventListener("click", (b) => {
362
- b.stopPropagation(), this.toggleFilterPanel(o, n, s);
460
+ o = document.createElement("button"), o.className = "tbw-filter-btn", o.setAttribute("aria-label", `Filter ${i.header ?? l}`), o.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>', a && (o.classList.add("active"), r.classList.add("filtered")), o.addEventListener("click", (b) => {
461
+ b.stopPropagation(), this.toggleFilterPanel(l, i, o);
363
462
  });
364
- const f = r.querySelector(".resize-handle");
365
- f ? r.insertBefore(s, f) : r.appendChild(s);
463
+ const p = r.querySelector(".resize-handle");
464
+ p ? r.insertBefore(o, p) : r.appendChild(o);
366
465
  });
367
466
  }
368
467
  // #endregion
@@ -372,7 +471,13 @@ class x extends _ {
372
471
  * Pass null to remove the filter.
373
472
  */
374
473
  setFilter(e, t) {
375
- 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", {
474
+ if (t === null)
475
+ this.filters.delete(e), this.syncExcludedValues(e, null);
476
+ else {
477
+ const r = { ...t, field: e };
478
+ this.filters.set(e, r), this.syncExcludedValues(e, r);
479
+ }
480
+ this.cachedResult = null, this.cacheKey = null, this.emit("filter-change", {
376
481
  filters: [...this.filters.values()],
377
482
  filteredRowCount: 0
378
483
  // Will be accurate after processRows
@@ -402,7 +507,7 @@ class x extends _ {
402
507
  setFilterModel(e) {
403
508
  this.filters.clear(), this.excludedValues.clear();
404
509
  for (const t of e)
405
- 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));
510
+ this.filters.set(t.field, t), this.syncExcludedValues(t.field, t);
406
511
  this.cachedResult = null, this.cacheKey = null, this.emit("filter-change", {
407
512
  filters: [...this.filters.values()],
408
513
  filteredRowCount: 0
@@ -443,7 +548,7 @@ class x extends _ {
443
548
  * Uses sourceRows to include all values regardless of current filter.
444
549
  */
445
550
  getUniqueValues(e) {
446
- return N(this.sourceRows, e);
551
+ return H(this.sourceRows, e);
447
552
  }
448
553
  // #endregion
449
554
  // #region Private Methods
@@ -457,7 +562,7 @@ class x extends _ {
457
562
  return;
458
563
  }
459
564
  const e = document.createElement("style");
460
- e.id = "tbw-filter-panel-styles", e.textContent = K, document.head.appendChild(e), this.globalStylesInjected = !0;
565
+ e.id = "tbw-filter-panel-styles", e.textContent = $, document.head.appendChild(e), this.globalStylesInjected = !0;
461
566
  }
462
567
  /**
463
568
  * Toggle the filter panel for a field
@@ -468,41 +573,41 @@ class x extends _ {
468
573
  return;
469
574
  }
470
575
  this.closeFilterPanel();
471
- const l = document.createElement("div");
472
- if (l.className = "tbw-filter-panel", this.panelElement = l, this.openPanelField = e, this.config.valuesHandler) {
473
- 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) => {
474
- this.openPanelField !== e || !this.panelElement || (l.innerHTML = "", this.renderPanelContent(e, t, l, o));
576
+ const n = document.createElement("div");
577
+ if (n.className = "tbw-filter-panel", this.isAnimationEnabled && n.classList.add("tbw-filter-panel-animated"), this.panelElement = n, this.openPanelField = e, this.config.valuesHandler) {
578
+ 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) => {
579
+ this.openPanelField !== e || !this.panelElement || (n.innerHTML = "", this.renderPanelContent(e, t, n, l));
475
580
  });
476
581
  return;
477
582
  }
478
- const n = N(this.sourceRows, e);
479
- this.renderPanelContent(e, t, l, n), document.body.appendChild(l), this.positionPanel(l, r), this.setupPanelCloseHandler(l, r);
583
+ const i = H(this.sourceRows, e);
584
+ this.renderPanelContent(e, t, n, i), document.body.appendChild(n), this.positionPanel(n, r), this.setupPanelCloseHandler(n, r);
480
585
  }
481
586
  /**
482
587
  * Render filter panel content with given values
483
588
  */
484
- renderPanelContent(e, t, r, l) {
485
- let n = this.excludedValues.get(e);
486
- n || (n = /* @__PURE__ */ new Set(), this.excludedValues.set(e, n));
487
- const o = this.searchText.get(e) ?? "", i = {
589
+ renderPanelContent(e, t, r, n) {
590
+ let i = this.excludedValues.get(e);
591
+ i || (i = /* @__PURE__ */ new Set(), this.excludedValues.set(e, i));
592
+ const l = this.searchText.get(e) ?? "", a = {
488
593
  field: e,
489
594
  column: t,
490
- uniqueValues: l,
491
- excludedValues: n,
492
- searchText: o,
493
- applySetFilter: (f) => {
494
- this.applySetFilter(e, f), this.closeFilterPanel();
595
+ uniqueValues: n,
596
+ excludedValues: i,
597
+ searchText: l,
598
+ applySetFilter: (p) => {
599
+ this.applySetFilter(e, p), this.closeFilterPanel();
495
600
  },
496
- applyTextFilter: (f, b, C) => {
497
- this.applyTextFilter(e, f, b, C), this.closeFilterPanel();
601
+ applyTextFilter: (p, b, C) => {
602
+ this.applyTextFilter(e, p, b, C), this.closeFilterPanel();
498
603
  },
499
604
  clearFilter: () => {
500
605
  this.clearFieldFilter(e), this.closeFilterPanel();
501
606
  },
502
607
  closePanel: () => this.closeFilterPanel()
503
608
  };
504
- let s = !1;
505
- this.config.filterPanelRenderer && (this.config.filterPanelRenderer(r, i), s = r.children.length > 0), s || this.renderDefaultFilterPanel(r, i, l, n);
609
+ let o = !1;
610
+ this.config.filterPanelRenderer && (this.config.filterPanelRenderer(r, a), o = r.children.length > 0), o || this.renderDefaultFilterPanel(r, a, n, i);
506
611
  }
507
612
  /**
508
613
  * Setup click-outside handler to close the panel
@@ -522,128 +627,146 @@ class x extends _ {
522
627
  * Close the filter panel
523
628
  */
524
629
  closeFilterPanel() {
525
- this.panelElement && (this.panelElement.remove(), this.panelElement = null), this.openPanelField = null, this.panelAbortController?.abort(), this.panelAbortController = null;
630
+ const e = this.panelElement;
631
+ e && (e.remove(), this.panelElement = null), this.panelAnchorElement && (this.panelAnchorElement.style.anchorName = "", this.panelAnchorElement = null), this.openPanelField = null, this.panelAbortController?.abort(), this.panelAbortController = null;
526
632
  }
633
+ /** Cache for CSS anchor positioning support check */
634
+ static supportsAnchorPositioning = null;
527
635
  /**
528
- * Position the panel below the button
636
+ * Check if browser supports CSS Anchor Positioning
637
+ */
638
+ static checkAnchorPositioningSupport() {
639
+ return g.supportsAnchorPositioning === null && (g.supportsAnchorPositioning = CSS.supports("anchor-name", "--test")), g.supportsAnchorPositioning;
640
+ }
641
+ /**
642
+ * Position the panel below the header cell
643
+ * Uses CSS Anchor Positioning if supported, falls back to JS positioning
529
644
  */
530
645
  positionPanel(e, t) {
531
- const r = t.getBoundingClientRect();
532
- e.style.position = "fixed", e.style.top = `${r.bottom + 4}px`, e.style.left = `${r.left}px`, requestAnimationFrame(() => {
646
+ const n = t.closest(".cell") ?? t;
647
+ if (n.style.anchorName = "--tbw-filter-anchor", this.panelAnchorElement = n, g.checkAnchorPositioningSupport()) {
648
+ requestAnimationFrame(() => {
649
+ const l = e.getBoundingClientRect(), a = n.getBoundingClientRect();
650
+ l.top < a.top && e.classList.add("tbw-filter-panel-above");
651
+ });
652
+ return;
653
+ }
654
+ const i = n.getBoundingClientRect();
655
+ e.style.position = "fixed", e.style.top = `${i.bottom + 4}px`, e.style.left = `${i.left}px`, requestAnimationFrame(() => {
533
656
  const l = e.getBoundingClientRect();
534
- l.right > window.innerWidth - 8 && (e.style.left = `${window.innerWidth - l.width - 8}px`), l.bottom > window.innerHeight - 8 && (e.style.top = `${r.top - l.height - 4}px`);
657
+ 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"));
535
658
  });
536
659
  }
537
660
  /**
538
661
  * Render the default filter panel content
539
662
  */
540
- renderDefaultFilterPanel(e, t, r, l) {
541
- const { field: n } = t, o = document.createElement("div");
542
- o.className = "tbw-filter-search";
543
- const i = document.createElement("input");
544
- i.type = "text", i.placeholder = "Search...", i.className = "tbw-filter-search-input", i.value = this.searchText.get(n) ?? "", o.appendChild(i), e.appendChild(o);
545
- const s = document.createElement("div");
546
- s.className = "tbw-filter-actions";
547
- const f = document.createElement("label");
548
- f.className = "tbw-filter-value-item", f.style.padding = "0", f.style.margin = "0";
663
+ renderDefaultFilterPanel(e, t, r, n) {
664
+ const { field: i } = t, l = document.createElement("div");
665
+ l.className = "tbw-filter-search";
666
+ const a = document.createElement("input");
667
+ a.type = "text", a.placeholder = "Search...", a.className = "tbw-filter-search-input", a.value = this.searchText.get(i) ?? "", l.appendChild(a), e.appendChild(l);
668
+ const o = document.createElement("div");
669
+ o.className = "tbw-filter-actions";
670
+ const p = document.createElement("label");
671
+ p.className = "tbw-filter-value-item", p.style.padding = "0", p.style.margin = "0";
549
672
  const b = document.createElement("input");
550
673
  b.type = "checkbox", b.className = "tbw-filter-checkbox";
551
674
  const C = document.createElement("span");
552
- C.textContent = "Select All", f.appendChild(b), f.appendChild(C), s.appendChild(f);
553
- const k = () => {
554
- const a = [...m.values()], u = a.every((d) => d), h = a.every((d) => !d);
555
- b.checked = u, b.indeterminate = !u && !h;
675
+ C.textContent = "Select All", p.appendChild(b), p.appendChild(C), o.appendChild(p);
676
+ const T = () => {
677
+ const s = [...y.values()], u = s.every((c) => c), w = s.every((c) => !c);
678
+ b.checked = u, b.indeterminate = !u && !w;
556
679
  };
557
680
  b.addEventListener("change", () => {
558
- const a = b.checked;
559
- for (const u of m.keys())
560
- m.set(u, a);
561
- k(), F();
562
- }), e.appendChild(s);
681
+ const s = b.checked;
682
+ for (const u of y.keys())
683
+ y.set(u, s);
684
+ T(), A();
685
+ }), e.appendChild(o);
563
686
  const v = document.createElement("div");
564
687
  v.className = "tbw-filter-values";
565
- const S = document.createElement("div");
566
- S.className = "tbw-filter-values-spacer", v.appendChild(S);
567
- const g = document.createElement("div");
568
- g.className = "tbw-filter-values-content", v.appendChild(g);
569
- const m = /* @__PURE__ */ new Map();
570
- r.forEach((a) => {
571
- const u = a == null ? "__null__" : String(a);
572
- m.set(u, !l.has(a));
573
- }), k();
574
- let y = [];
575
- const I = (a, u) => {
576
- const h = a == null ? "(Blank)" : String(a), d = a == null ? "__null__" : String(a), c = document.createElement("label");
577
- 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";
578
- const w = document.createElement("input");
579
- w.type = "checkbox", w.className = "tbw-filter-checkbox", w.checked = m.get(d) ?? !0, w.dataset.value = d, w.addEventListener("change", () => {
580
- m.set(d, w.checked), k();
688
+ const E = document.createElement("div");
689
+ E.className = "tbw-filter-values-spacer", v.appendChild(E);
690
+ const m = document.createElement("div");
691
+ m.className = "tbw-filter-values-content", v.appendChild(m);
692
+ const y = /* @__PURE__ */ new Map();
693
+ r.forEach((s) => {
694
+ const u = s == null ? "__null__" : String(s);
695
+ y.set(u, !n.has(s));
696
+ }), T();
697
+ let x = [];
698
+ const F = (s, u) => {
699
+ const w = s == null ? "(Blank)" : String(s), c = s == null ? "__null__" : String(s), d = document.createElement("label");
700
+ d.className = "tbw-filter-value-item", d.style.position = "absolute", d.style.top = `${u * g.LIST_ITEM_HEIGHT}px`, d.style.left = "0", d.style.right = "0", d.style.height = `${g.LIST_ITEM_HEIGHT}px`, d.style.boxSizing = "border-box";
701
+ const f = document.createElement("input");
702
+ f.type = "checkbox", f.className = "tbw-filter-checkbox", f.checked = y.get(c) ?? !0, f.dataset.value = c, f.addEventListener("change", () => {
703
+ y.set(c, f.checked), T();
581
704
  });
582
- const H = document.createElement("span");
583
- return H.textContent = h, c.appendChild(w), c.appendChild(H), c;
584
- }, F = () => {
585
- const a = y.length, u = v.clientHeight, h = v.scrollTop;
586
- if (S.style.height = `${a * x.LIST_ITEM_HEIGHT}px`, M(a, x.LIST_BYPASS_THRESHOLD / 3)) {
587
- g.innerHTML = "", g.style.transform = "translateY(0px)", y.forEach((c, w) => {
588
- g.appendChild(I(c, w));
705
+ const N = document.createElement("span");
706
+ return N.textContent = w, d.appendChild(f), d.appendChild(N), d;
707
+ }, A = () => {
708
+ const s = x.length, u = v.clientHeight, w = v.scrollTop;
709
+ if (E.style.height = `${s * g.LIST_ITEM_HEIGHT}px`, V(s, g.LIST_BYPASS_THRESHOLD / 3)) {
710
+ m.innerHTML = "", m.style.transform = "translateY(0px)", x.forEach((d, f) => {
711
+ m.appendChild(F(d, f));
589
712
  });
590
713
  return;
591
714
  }
592
- const d = P({
593
- totalRows: a,
715
+ const c = P({
716
+ totalRows: s,
594
717
  viewportHeight: u,
595
- scrollTop: h,
596
- rowHeight: x.LIST_ITEM_HEIGHT,
597
- overscan: x.LIST_OVERSCAN
718
+ scrollTop: w,
719
+ rowHeight: g.LIST_ITEM_HEIGHT,
720
+ overscan: g.LIST_OVERSCAN
598
721
  });
599
- g.style.transform = `translateY(${d.offsetY}px)`, g.innerHTML = "";
600
- for (let c = d.start; c < d.end; c++)
601
- g.appendChild(I(y[c], c - d.start));
602
- }, L = (a) => {
603
- const u = a.toLowerCase();
604
- if (y = r.filter((h) => {
605
- const d = h == null ? "(Blank)" : String(h);
606
- return !a || d.toLowerCase().includes(u);
607
- }), y.length === 0) {
608
- S.style.height = "0px", g.innerHTML = "";
609
- const h = document.createElement("div");
610
- h.className = "tbw-filter-no-match", h.textContent = "No matching values", g.appendChild(h);
722
+ m.style.transform = `translateY(${c.offsetY}px)`, m.innerHTML = "";
723
+ for (let d = c.start; d < c.end; d++)
724
+ m.appendChild(F(x[d], d - c.start));
725
+ }, I = (s) => {
726
+ const u = this.config.caseSensitive ?? !1, w = u ? s : s.toLowerCase();
727
+ if (x = r.filter((c) => {
728
+ const d = c == null ? "(Blank)" : String(c), f = u ? d : d.toLowerCase();
729
+ return !s || f.includes(w);
730
+ }), x.length === 0) {
731
+ E.style.height = "0px", m.innerHTML = "";
732
+ const c = document.createElement("div");
733
+ c.className = "tbw-filter-no-match", c.textContent = "No matching values", m.appendChild(c);
611
734
  return;
612
735
  }
613
- F();
736
+ A();
614
737
  };
615
738
  v.addEventListener(
616
739
  "scroll",
617
740
  () => {
618
- y.length > 0 && F();
741
+ x.length > 0 && A();
619
742
  },
620
743
  { passive: !0 }
621
- ), L(i.value), e.appendChild(v);
622
- let A;
623
- i.addEventListener("input", () => {
624
- clearTimeout(A), A = setTimeout(() => {
625
- this.searchText.set(n, i.value), L(i.value);
744
+ ), I(a.value), e.appendChild(v);
745
+ let L;
746
+ a.addEventListener("input", () => {
747
+ clearTimeout(L), L = setTimeout(() => {
748
+ this.searchText.set(i, a.value), I(a.value);
626
749
  }, this.config.debounceMs ?? 150);
627
750
  });
628
- const T = document.createElement("div");
629
- T.className = "tbw-filter-buttons";
630
- const E = document.createElement("button");
631
- E.className = "tbw-filter-apply-btn", E.textContent = "Apply", E.addEventListener("click", () => {
632
- const a = [];
633
- for (const [u, h] of m)
634
- if (!h)
751
+ const S = document.createElement("div");
752
+ S.className = "tbw-filter-buttons";
753
+ const R = document.createElement("button");
754
+ R.className = "tbw-filter-apply-btn", R.textContent = "Apply", R.addEventListener("click", () => {
755
+ const s = [];
756
+ for (const [u, w] of y)
757
+ if (!w)
635
758
  if (u === "__null__")
636
- a.push(null);
759
+ s.push(null);
637
760
  else {
638
- const d = r.find((c) => String(c) === u);
639
- a.push(d !== void 0 ? d : u);
761
+ const c = r.find((d) => String(d) === u);
762
+ s.push(c !== void 0 ? c : u);
640
763
  }
641
- t.applySetFilter(a);
642
- }), T.appendChild(E);
643
- const R = document.createElement("button");
644
- R.className = "tbw-filter-clear-btn", R.textContent = "Clear Filter", R.addEventListener("click", () => {
764
+ t.applySetFilter(s);
765
+ }), S.appendChild(R);
766
+ const k = document.createElement("button");
767
+ k.className = "tbw-filter-clear-btn", k.textContent = "Clear Filter", k.addEventListener("click", () => {
645
768
  t.clearFilter();
646
- }), T.appendChild(R), e.appendChild(T);
769
+ }), S.appendChild(k), e.appendChild(S);
647
770
  }
648
771
  /**
649
772
  * Apply a set filter (exclude values)
@@ -659,13 +782,13 @@ class x extends _ {
659
782
  /**
660
783
  * Apply a text filter
661
784
  */
662
- applyTextFilter(e, t, r, l) {
785
+ applyTextFilter(e, t, r, n) {
663
786
  this.filters.set(e, {
664
787
  field: e,
665
788
  type: "text",
666
789
  operator: t,
667
790
  value: r,
668
- valueTo: l
791
+ valueTo: n
669
792
  }), this.applyFiltersInternal();
670
793
  }
671
794
  /**
@@ -677,13 +800,13 @@ class x extends _ {
677
800
  if (this.config.filterHandler) {
678
801
  const t = this.grid;
679
802
  t.setAttribute("aria-busy", "true");
680
- const r = this.config.filterHandler(e, this.sourceRows), l = (n) => {
681
- t.removeAttribute("aria-busy"), this.cachedResult = n, this.grid.rows = n, this.emit("filter-change", {
803
+ const r = this.config.filterHandler(e, this.sourceRows), n = (i) => {
804
+ t.removeAttribute("aria-busy"), this.cachedResult = i, this.grid.rows = i, this.emit("filter-change", {
682
805
  filters: e,
683
- filteredRowCount: n.length
806
+ filteredRowCount: i.length
684
807
  }), this.requestRender();
685
808
  };
686
- r && typeof r.then == "function" ? r.then(l) : l(r);
809
+ r && typeof r.then == "function" ? r.then(n) : n(r);
687
810
  return;
688
811
  }
689
812
  this.emit("filter-change", {
@@ -726,11 +849,8 @@ class x extends _ {
726
849
  this.filters.set(e, r), this.cachedResult = null, this.cacheKey = null;
727
850
  }
728
851
  // #endregion
729
- // #region Styles
730
- styles = $;
731
- // #endregion
732
852
  }
733
853
  export {
734
- x as FilteringPlugin
854
+ g as FilteringPlugin
735
855
  };
736
856
  //# sourceMappingURL=index.js.map