@toolbox-web/grid 1.6.2 → 1.8.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 (114) hide show
  1. package/README.md +51 -15
  2. package/all.js +267 -158
  3. package/all.js.map +1 -1
  4. package/index.js +866 -722
  5. package/index.js.map +1 -1
  6. package/lib/core/grid.d.ts +68 -1
  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/plugin/base-plugin.d.ts +182 -1
  10. package/lib/core/plugin/base-plugin.d.ts.map +1 -1
  11. package/lib/core/plugin/index.d.ts +1 -1
  12. package/lib/core/plugin/index.d.ts.map +1 -1
  13. package/lib/core/plugin/plugin-manager.d.ts +56 -1
  14. package/lib/core/plugin/plugin-manager.d.ts.map +1 -1
  15. package/lib/core/plugin/types.d.ts +36 -0
  16. package/lib/core/plugin/types.d.ts.map +1 -1
  17. package/lib/core/types.d.ts +1349 -31
  18. package/lib/core/types.d.ts.map +1 -1
  19. package/lib/plugins/clipboard/ClipboardPlugin.d.ts.map +1 -1
  20. package/lib/plugins/clipboard/index.js +140 -87
  21. package/lib/plugins/clipboard/index.js.map +1 -1
  22. package/lib/plugins/column-virtualization/index.js +64 -7
  23. package/lib/plugins/column-virtualization/index.js.map +1 -1
  24. package/lib/plugins/context-menu/ContextMenuPlugin.d.ts.map +1 -1
  25. package/lib/plugins/context-menu/index.js +123 -65
  26. package/lib/plugins/context-menu/index.js.map +1 -1
  27. package/lib/plugins/editing/EditingPlugin.d.ts +6 -1
  28. package/lib/plugins/editing/EditingPlugin.d.ts.map +1 -1
  29. package/lib/plugins/editing/index.js +95 -13
  30. package/lib/plugins/editing/index.js.map +1 -1
  31. package/lib/plugins/export/index.js +91 -34
  32. package/lib/plugins/export/index.js.map +1 -1
  33. package/lib/plugins/filtering/FilteringPlugin.d.ts +6 -1
  34. package/lib/plugins/filtering/FilteringPlugin.d.ts.map +1 -1
  35. package/lib/plugins/filtering/index.js +192 -123
  36. package/lib/plugins/filtering/index.js.map +1 -1
  37. package/lib/plugins/grouping-columns/index.js +57 -0
  38. package/lib/plugins/grouping-columns/index.js.map +1 -1
  39. package/lib/plugins/grouping-rows/GroupingRowsPlugin.d.ts +7 -2
  40. package/lib/plugins/grouping-rows/GroupingRowsPlugin.d.ts.map +1 -1
  41. package/lib/plugins/grouping-rows/index.js +142 -60
  42. package/lib/plugins/grouping-rows/index.js.map +1 -1
  43. package/lib/plugins/master-detail/index.js +69 -12
  44. package/lib/plugins/master-detail/index.js.map +1 -1
  45. package/lib/plugins/multi-sort/index.js +70 -13
  46. package/lib/plugins/multi-sort/index.js.map +1 -1
  47. package/lib/plugins/pinned-columns/PinnedColumnsPlugin.d.ts +3 -3
  48. package/lib/plugins/pinned-columns/PinnedColumnsPlugin.d.ts.map +1 -1
  49. package/lib/plugins/pinned-columns/index.js +106 -36
  50. package/lib/plugins/pinned-columns/index.js.map +1 -1
  51. package/lib/plugins/pinned-rows/index.js +57 -0
  52. package/lib/plugins/pinned-rows/index.js.map +1 -1
  53. package/lib/plugins/pivot/index.js +57 -0
  54. package/lib/plugins/pivot/index.js.map +1 -1
  55. package/lib/plugins/print/PrintPlugin.d.ts.map +1 -1
  56. package/lib/plugins/print/index.js +58 -1
  57. package/lib/plugins/print/index.js.map +1 -1
  58. package/lib/plugins/reorder/ReorderPlugin.d.ts.map +1 -1
  59. package/lib/plugins/reorder/column-drag.d.ts +2 -2
  60. package/lib/plugins/reorder/index.js +68 -17
  61. package/lib/plugins/reorder/index.js.map +1 -1
  62. package/lib/plugins/responsive/ResponsivePlugin.d.ts +6 -1
  63. package/lib/plugins/responsive/ResponsivePlugin.d.ts.map +1 -1
  64. package/lib/plugins/responsive/index.js +125 -54
  65. package/lib/plugins/responsive/index.js.map +1 -1
  66. package/lib/plugins/row-reorder/index.js +169 -112
  67. package/lib/plugins/row-reorder/index.js.map +1 -1
  68. package/lib/plugins/selection/SelectionPlugin.d.ts +14 -2
  69. package/lib/plugins/selection/SelectionPlugin.d.ts.map +1 -1
  70. package/lib/plugins/selection/index.js +84 -7
  71. package/lib/plugins/selection/index.js.map +1 -1
  72. package/lib/plugins/server-side/index.js +79 -22
  73. package/lib/plugins/server-side/index.js.map +1 -1
  74. package/lib/plugins/tree/TreePlugin.d.ts +7 -1
  75. package/lib/plugins/tree/TreePlugin.d.ts.map +1 -1
  76. package/lib/plugins/tree/index.js +140 -58
  77. package/lib/plugins/tree/index.js.map +1 -1
  78. package/lib/plugins/undo-redo/UndoRedoPlugin.d.ts +6 -1
  79. package/lib/plugins/undo-redo/UndoRedoPlugin.d.ts.map +1 -1
  80. package/lib/plugins/undo-redo/index.js +79 -10
  81. package/lib/plugins/undo-redo/index.js.map +1 -1
  82. package/lib/plugins/visibility/index.js +57 -0
  83. package/lib/plugins/visibility/index.js.map +1 -1
  84. package/package.json +1 -1
  85. package/public.d.ts +80 -2
  86. package/public.d.ts.map +1 -1
  87. package/umd/grid.all.umd.js +25 -25
  88. package/umd/grid.all.umd.js.map +1 -1
  89. package/umd/grid.umd.js +15 -15
  90. package/umd/grid.umd.js.map +1 -1
  91. package/umd/plugins/clipboard.umd.js +5 -5
  92. package/umd/plugins/clipboard.umd.js.map +1 -1
  93. package/umd/plugins/context-menu.umd.js +1 -1
  94. package/umd/plugins/context-menu.umd.js.map +1 -1
  95. package/umd/plugins/editing.umd.js +1 -1
  96. package/umd/plugins/editing.umd.js.map +1 -1
  97. package/umd/plugins/filtering.umd.js +1 -1
  98. package/umd/plugins/filtering.umd.js.map +1 -1
  99. package/umd/plugins/grouping-rows.umd.js +2 -2
  100. package/umd/plugins/grouping-rows.umd.js.map +1 -1
  101. package/umd/plugins/pinned-columns.umd.js +1 -1
  102. package/umd/plugins/pinned-columns.umd.js.map +1 -1
  103. package/umd/plugins/print.umd.js +1 -1
  104. package/umd/plugins/print.umd.js.map +1 -1
  105. package/umd/plugins/reorder.umd.js +1 -1
  106. package/umd/plugins/reorder.umd.js.map +1 -1
  107. package/umd/plugins/responsive.umd.js +1 -1
  108. package/umd/plugins/responsive.umd.js.map +1 -1
  109. package/umd/plugins/selection.umd.js +2 -2
  110. package/umd/plugins/selection.umd.js.map +1 -1
  111. package/umd/plugins/tree.umd.js +1 -1
  112. package/umd/plugins/tree.umd.js.map +1 -1
  113. package/umd/plugins/undo-redo.umd.js +1 -1
  114. package/umd/plugins/undo-redo.umd.js.map +1 -1
@@ -1,13 +1,13 @@
1
1
  function Y(N) {
2
- const { totalRows: e, viewportHeight: t, scrollTop: r, rowHeight: a, overscan: l } = N, c = Math.ceil(t / a);
3
- let d = Math.floor(r / a) - l;
2
+ const { totalRows: e, viewportHeight: t, scrollTop: r, rowHeight: i, overscan: l } = N, c = Math.ceil(t / i);
3
+ let d = Math.floor(r / i) - l;
4
4
  d < 0 && (d = 0);
5
- let i = d + c + l * 2;
6
- return i > e && (i = e), i === e && d > 0 && (d = Math.max(0, i - c - l * 2)), {
5
+ let a = d + c + l * 2;
6
+ return a > e && (a = e), a === e && d > 0 && (d = Math.max(0, a - c - l * 2)), {
7
7
  start: d,
8
- end: i,
9
- offsetY: d * a,
10
- totalHeight: e * a
8
+ end: a,
9
+ offsetY: d * i,
10
+ totalHeight: e * i
11
11
  };
12
12
  }
13
13
  function G(N, e) {
@@ -164,6 +164,63 @@ class K {
164
164
  const r = new CustomEvent(e, { detail: t, bubbles: !0, cancelable: !0 });
165
165
  return this.grid?.dispatchEvent?.(r), r.defaultPrevented;
166
166
  }
167
+ // =========================================================================
168
+ // Event Bus - Plugin-to-Plugin Communication
169
+ // =========================================================================
170
+ /**
171
+ * Subscribe to an event from another plugin.
172
+ * The subscription is automatically cleaned up when this plugin is detached.
173
+ *
174
+ * @category Plugin Development
175
+ * @param eventType - The event type to listen for (e.g., 'filter-change')
176
+ * @param callback - The callback to invoke when the event is emitted
177
+ *
178
+ * @example
179
+ * ```typescript
180
+ * // In attach() or other initialization
181
+ * this.on('filter-change', (detail) => {
182
+ * console.log('Filter changed:', detail);
183
+ * });
184
+ * ```
185
+ */
186
+ on(e, t) {
187
+ this.grid?._pluginManager?.subscribe(this, e, t);
188
+ }
189
+ /**
190
+ * Unsubscribe from a plugin event.
191
+ *
192
+ * @category Plugin Development
193
+ * @param eventType - The event type to stop listening for
194
+ *
195
+ * @example
196
+ * ```typescript
197
+ * this.off('filter-change');
198
+ * ```
199
+ */
200
+ off(e) {
201
+ this.grid?._pluginManager?.unsubscribe(this, e);
202
+ }
203
+ /**
204
+ * Emit an event to other plugins via the Event Bus.
205
+ * This is for inter-plugin communication only; it does NOT dispatch DOM events.
206
+ * Use `emit()` to dispatch DOM events that external consumers can listen to.
207
+ *
208
+ * @category Plugin Development
209
+ * @param eventType - The event type to emit (should be declared in manifest.events)
210
+ * @param detail - The event payload
211
+ *
212
+ * @example
213
+ * ```typescript
214
+ * // Emit to other plugins (not DOM)
215
+ * this.emitPluginEvent('filter-change', { field: 'name', value: 'Alice' });
216
+ *
217
+ * // For DOM events that consumers can addEventListener to:
218
+ * this.emit('filter-change', { field: 'name', value: 'Alice' });
219
+ * ```
220
+ */
221
+ emitPluginEvent(e, t) {
222
+ this.grid?._pluginManager?.emitPluginEvent(e, t);
223
+ }
167
224
  /**
168
225
  * Request a re-render of the grid.
169
226
  */
@@ -336,7 +393,7 @@ function j(N, e, t = !1) {
336
393
  if (e.operator === "notBlank")
337
394
  return r != null && r !== "";
338
395
  if (r == null) return !1;
339
- const a = String(r), l = t ? a : a.toLowerCase(), c = t ? String(e.value) : String(e.value).toLowerCase();
396
+ const i = String(r), l = t ? i : i.toLowerCase(), c = t ? String(e.value) : String(e.value).toLowerCase();
340
397
  switch (e.operator) {
341
398
  // Text operators
342
399
  case "contains":
@@ -372,7 +429,7 @@ function j(N, e, t = !1) {
372
429
  }
373
430
  }
374
431
  function U(N, e, t = !1) {
375
- return e.length ? N.filter((r) => e.every((a) => j(r, a, t))) : N;
432
+ return e.length ? N.filter((r) => e.every((i) => j(r, i, t))) : N;
376
433
  }
377
434
  function J(N) {
378
435
  return JSON.stringify(
@@ -387,13 +444,25 @@ function J(N) {
387
444
  function B(N, e) {
388
445
  const t = /* @__PURE__ */ new Set();
389
446
  for (const r of N) {
390
- const a = r[e];
391
- a != null && t.add(a);
447
+ const i = r[e];
448
+ i != null && t.add(i);
392
449
  }
393
- return [...t].sort((r, a) => typeof r == "number" && typeof a == "number" ? r - a : String(r).localeCompare(String(a)));
450
+ return [...t].sort((r, i) => typeof r == "number" && typeof i == "number" ? r - i : String(r).localeCompare(String(i)));
394
451
  }
395
452
  const Q = '@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}}', X = "@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-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;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-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)}}";
396
453
  class I extends K {
454
+ /**
455
+ * Plugin manifest - declares events emitted by this plugin.
456
+ * @internal
457
+ */
458
+ static manifest = {
459
+ events: [
460
+ {
461
+ type: "filter-applied",
462
+ description: "Emitted when filter criteria change. Subscribers can react to row visibility changes."
463
+ }
464
+ ]
465
+ };
397
466
  /** @internal */
398
467
  name = "filtering";
399
468
  /** @internal */
@@ -482,37 +551,37 @@ class I extends K {
482
551
  const r = J(t);
483
552
  if (this.cacheKey === r && this.cachedResult)
484
553
  return this.cachedResult;
485
- const a = U([...e], t, this.config.caseSensitive);
486
- return this.cachedResult = a, this.cacheKey = r, a;
554
+ const i = U([...e], t, this.config.caseSensitive);
555
+ return this.cachedResult = i, this.cacheKey = r, i;
487
556
  }
488
557
  /** @internal */
489
558
  afterRender() {
490
559
  const e = this.gridElement;
491
560
  if (!e) return;
492
561
  e.querySelectorAll('[part~="header-cell"]').forEach((r) => {
493
- const a = r.getAttribute("data-col");
494
- if (a === null) return;
495
- const l = this.visibleColumns[parseInt(a, 10)];
562
+ const i = r.getAttribute("data-col");
563
+ if (i === null) return;
564
+ const l = this.visibleColumns[parseInt(i, 10)];
496
565
  if (!l || !this.isColumnFilterable(l) || W(l)) return;
497
566
  const c = l.field;
498
567
  if (!c) return;
499
568
  const d = this.filters.has(c);
500
- let i = r.querySelector(".tbw-filter-btn");
501
- if (i) {
502
- const w = i.classList.contains("active");
503
- if (i.classList.toggle("active", d), r.classList.toggle("filtered", d), w !== d) {
569
+ let a = r.querySelector(".tbw-filter-btn");
570
+ if (a) {
571
+ const w = a.classList.contains("active");
572
+ if (a.classList.toggle("active", d), r.classList.toggle("filtered", d), w !== d) {
504
573
  const u = d ? "filterActive" : "filter";
505
- this.setIcon(i, this.resolveIcon(u));
574
+ this.setIcon(a, this.resolveIcon(u));
506
575
  }
507
576
  return;
508
577
  }
509
- i = document.createElement("button"), i.className = "tbw-filter-btn", i.setAttribute("aria-label", `Filter ${l.header ?? c}`);
578
+ a = document.createElement("button"), a.className = "tbw-filter-btn", a.setAttribute("aria-label", `Filter ${l.header ?? c}`);
510
579
  const m = d ? "filterActive" : "filter";
511
- this.setIcon(i, this.resolveIcon(m)), d && (i.classList.add("active"), r.classList.add("filtered")), i.addEventListener("click", (w) => {
512
- w.stopPropagation(), this.toggleFilterPanel(c, l, i);
580
+ this.setIcon(a, this.resolveIcon(m)), d && (a.classList.add("active"), r.classList.add("filtered")), a.addEventListener("click", (w) => {
581
+ w.stopPropagation(), this.toggleFilterPanel(c, l, a);
513
582
  });
514
583
  const k = r.querySelector(".resize-handle");
515
- k ? r.insertBefore(i, k) : r.appendChild(i);
584
+ k ? r.insertBefore(a, k) : r.appendChild(a);
516
585
  });
517
586
  }
518
587
  // #endregion
@@ -532,7 +601,7 @@ class I extends K {
532
601
  filters: [...this.filters.values()],
533
602
  filteredRowCount: 0
534
603
  // Will be accurate after processRows
535
- }), this.requestRender();
604
+ }), this.emitPluginEvent("filter-applied", { filters: [...this.filters.values()] }), this.requestRender();
536
605
  }
537
606
  /**
538
607
  * Get the current filter for a field.
@@ -562,7 +631,7 @@ class I extends K {
562
631
  this.cachedResult = null, this.cacheKey = null, this.emit("filter-change", {
563
632
  filters: [...this.filters.values()],
564
633
  filteredRowCount: 0
565
- }), this.requestRender();
634
+ }), this.emitPluginEvent("filter-applied", { filters: [...this.filters.values()] }), this.requestRender();
566
635
  }
567
636
  /**
568
637
  * Clear all filters.
@@ -610,8 +679,8 @@ class I extends K {
610
679
  copyGridThemeContext(e) {
611
680
  const t = this.gridElement;
612
681
  if (!t) return;
613
- for (const a of t.classList)
614
- a.startsWith("tbw-") || a === "selecting" || e.classList.add(a);
682
+ for (const i of t.classList)
683
+ i.startsWith("tbw-") || i === "selecting" || e.classList.add(i);
615
684
  const r = t.dataset.theme;
616
685
  r && (e.dataset.theme = r);
617
686
  }
@@ -636,26 +705,26 @@ class I extends K {
636
705
  return;
637
706
  }
638
707
  this.closeFilterPanel();
639
- const a = document.createElement("div");
640
- if (a.className = "tbw-filter-panel", this.copyGridThemeContext(a), this.isAnimationEnabled && a.classList.add("tbw-filter-panel-animated"), this.panelElement = a, this.openPanelField = e, this.config.valuesHandler) {
641
- a.innerHTML = '<div class="tbw-filter-loading">Loading...</div>', document.body.appendChild(a), this.positionPanel(a, r), this.setupPanelCloseHandler(a, r), this.config.valuesHandler(e, t).then((c) => {
642
- this.openPanelField !== e || !this.panelElement || (a.innerHTML = "", this.renderPanelContent(e, t, a, c));
708
+ const i = document.createElement("div");
709
+ if (i.className = "tbw-filter-panel", this.copyGridThemeContext(i), this.isAnimationEnabled && i.classList.add("tbw-filter-panel-animated"), this.panelElement = i, this.openPanelField = e, this.config.valuesHandler) {
710
+ i.innerHTML = '<div class="tbw-filter-loading">Loading...</div>', document.body.appendChild(i), this.positionPanel(i, r), this.setupPanelCloseHandler(i, r), this.config.valuesHandler(e, t).then((c) => {
711
+ this.openPanelField !== e || !this.panelElement || (i.innerHTML = "", this.renderPanelContent(e, t, i, c));
643
712
  });
644
713
  return;
645
714
  }
646
715
  const l = B(this.sourceRows, e);
647
- document.body.appendChild(a), this.positionPanel(a, r), this.renderPanelContent(e, t, a, l), this.setupPanelCloseHandler(a, r);
716
+ document.body.appendChild(i), this.positionPanel(i, r), this.renderPanelContent(e, t, i, l), this.setupPanelCloseHandler(i, r);
648
717
  }
649
718
  /**
650
719
  * Render filter panel content with given values
651
720
  */
652
- renderPanelContent(e, t, r, a) {
721
+ renderPanelContent(e, t, r, i) {
653
722
  let l = this.excludedValues.get(e);
654
723
  l || (l = /* @__PURE__ */ new Set(), this.excludedValues.set(e, l));
655
724
  const c = this.searchText.get(e) ?? "", d = {
656
725
  field: e,
657
726
  column: t,
658
- uniqueValues: a,
727
+ uniqueValues: i,
659
728
  excludedValues: l,
660
729
  searchText: c,
661
730
  applySetFilter: (m) => {
@@ -669,14 +738,14 @@ class I extends K {
669
738
  },
670
739
  closePanel: () => this.closeFilterPanel()
671
740
  };
672
- let i = !1;
673
- if (this.config.filterPanelRenderer && (this.config.filterPanelRenderer(r, d), i = r.children.length > 0), !i && t.type) {
741
+ let a = !1;
742
+ if (this.config.filterPanelRenderer && (this.config.filterPanelRenderer(r, d), a = r.children.length > 0), !a && t.type) {
674
743
  const m = this.grid.effectiveConfig.typeDefaults?.[t.type];
675
- m?.filterPanelRenderer && (m.filterPanelRenderer(r, d), i = r.children.length > 0);
744
+ m?.filterPanelRenderer && (m.filterPanelRenderer(r, d), a = r.children.length > 0);
676
745
  }
677
- if (!i) {
746
+ if (!a) {
678
747
  const m = t.type;
679
- m === "number" ? this.renderNumberFilterPanel(r, d, a) : m === "date" ? this.renderDateFilterPanel(r, d, a) : this.renderDefaultFilterPanel(r, d, a, l);
748
+ m === "number" ? this.renderNumberFilterPanel(r, d, i) : m === "date" ? this.renderDateFilterPanel(r, d, i) : this.renderDefaultFilterPanel(r, d, i, l);
680
749
  }
681
750
  }
682
751
  /**
@@ -713,15 +782,15 @@ class I extends K {
713
782
  * Uses CSS Anchor Positioning if supported, falls back to JS positioning
714
783
  */
715
784
  positionPanel(e, t) {
716
- const a = t.closest(".cell") ?? t;
717
- if (a.style.anchorName = "--tbw-filter-anchor", this.panelAnchorElement = a, I.checkAnchorPositioningSupport()) {
785
+ const i = t.closest(".cell") ?? t;
786
+ if (i.style.anchorName = "--tbw-filter-anchor", this.panelAnchorElement = i, I.checkAnchorPositioningSupport()) {
718
787
  requestAnimationFrame(() => {
719
- const c = e.getBoundingClientRect(), d = a.getBoundingClientRect();
788
+ const c = e.getBoundingClientRect(), d = i.getBoundingClientRect();
720
789
  c.top < d.top && e.classList.add("tbw-filter-panel-above");
721
790
  });
722
791
  return;
723
792
  }
724
- const l = a.getBoundingClientRect();
793
+ const l = i.getBoundingClientRect();
725
794
  e.style.position = "fixed", e.style.top = `${l.bottom + 4}px`, e.style.left = `${l.left}px`, requestAnimationFrame(() => {
726
795
  const c = e.getBoundingClientRect();
727
796
  c.right > window.innerWidth - 8 && (e.style.left = `${l.right - c.width}px`), c.bottom > window.innerHeight - 8 && (e.style.top = `${l.top - c.height - 4}px`, e.classList.add("tbw-filter-panel-above"));
@@ -730,11 +799,11 @@ class I extends K {
730
799
  /**
731
800
  * Render the default filter panel content
732
801
  */
733
- renderDefaultFilterPanel(e, t, r, a) {
802
+ renderDefaultFilterPanel(e, t, r, i) {
734
803
  const { field: l } = t, c = this.getListItemHeight(), d = document.createElement("div");
735
804
  d.className = "tbw-filter-search";
736
- const i = document.createElement("input");
737
- i.type = "text", i.placeholder = "Search...", i.className = "tbw-filter-search-input", i.value = this.searchText.get(l) ?? "", d.appendChild(i), e.appendChild(d);
805
+ const a = document.createElement("input");
806
+ a.type = "text", a.placeholder = "Search...", a.className = "tbw-filter-search-input", a.value = this.searchText.get(l) ?? "", d.appendChild(a), e.appendChild(d);
738
807
  const m = document.createElement("div");
739
808
  m.className = "tbw-filter-actions";
740
809
  const k = document.createElement("label");
@@ -744,14 +813,14 @@ class I extends K {
744
813
  const u = document.createElement("span");
745
814
  u.textContent = "Select All", k.appendChild(w), k.appendChild(u), m.appendChild(k);
746
815
  const v = () => {
747
- const o = [...S.values()], b = o.every((n) => n), y = o.every((n) => !n);
748
- w.checked = b, w.indeterminate = !b && !y;
816
+ const o = [...S.values()], h = o.every((n) => n), y = o.every((n) => !n);
817
+ w.checked = h, w.indeterminate = !h && !y;
749
818
  };
750
819
  w.addEventListener("change", () => {
751
820
  const o = w.checked;
752
- for (const b of S.keys())
753
- S.set(b, o);
754
- v(), L();
821
+ for (const h of S.keys())
822
+ S.set(h, o);
823
+ v(), A();
755
824
  }), e.appendChild(m);
756
825
  const E = document.createElement("div");
757
826
  E.className = "tbw-filter-values";
@@ -761,21 +830,21 @@ class I extends K {
761
830
  x.className = "tbw-filter-values-content", E.appendChild(x);
762
831
  const S = /* @__PURE__ */ new Map();
763
832
  r.forEach((o) => {
764
- const b = o == null ? "__null__" : String(o);
765
- S.set(b, !a.has(o));
833
+ const h = o == null ? "__null__" : String(o);
834
+ S.set(h, !i.has(o));
766
835
  }), v();
767
836
  let C = [];
768
- const R = (o, b) => {
837
+ const R = (o, h) => {
769
838
  const y = o == null ? "(Blank)" : String(o), n = o == null ? "__null__" : String(o), s = document.createElement("label");
770
- s.className = "tbw-filter-value-item", s.style.position = "absolute", s.style.top = `calc(var(--tbw-filter-item-height, 28px) * ${b})`, s.style.left = "0", s.style.right = "0", s.style.boxSizing = "border-box";
839
+ s.className = "tbw-filter-value-item", s.style.position = "absolute", s.style.top = `calc(var(--tbw-filter-item-height, 28px) * ${h})`, s.style.left = "0", s.style.right = "0", s.style.boxSizing = "border-box";
771
840
  const F = document.createElement("input");
772
841
  F.type = "checkbox", F.className = "tbw-filter-checkbox", F.checked = S.get(n) ?? !0, F.dataset.value = n, F.addEventListener("change", () => {
773
842
  S.set(n, F.checked), v();
774
843
  });
775
844
  const M = document.createElement("span");
776
845
  return M.textContent = y, s.appendChild(F), s.appendChild(M), s;
777
- }, L = () => {
778
- const o = C.length, b = E.clientHeight, y = E.scrollTop;
846
+ }, A = () => {
847
+ const o = C.length, h = E.clientHeight, y = E.scrollTop;
779
848
  if (g.style.height = `${o * c}px`, G(o, I.LIST_BYPASS_THRESHOLD / 3)) {
780
849
  x.innerHTML = "", x.style.transform = "translateY(0px)", C.forEach((s, F) => {
781
850
  x.appendChild(R(s, F));
@@ -784,7 +853,7 @@ class I extends K {
784
853
  }
785
854
  const n = Y({
786
855
  totalRows: o,
787
- viewportHeight: b,
856
+ viewportHeight: h,
788
857
  scrollTop: y,
789
858
  rowHeight: c,
790
859
  overscan: I.LIST_OVERSCAN
@@ -793,9 +862,9 @@ class I extends K {
793
862
  for (let s = n.start; s < n.end; s++)
794
863
  x.appendChild(R(C[s], s - n.start));
795
864
  }, f = (o) => {
796
- const b = this.config.caseSensitive ?? !1, y = b ? o : o.toLowerCase();
865
+ const h = this.config.caseSensitive ?? !1, y = h ? o : o.toLowerCase();
797
866
  if (C = r.filter((n) => {
798
- const s = n == null ? "(Blank)" : String(n), F = b ? s : s.toLowerCase();
867
+ const s = n == null ? "(Blank)" : String(n), F = h ? s : s.toLowerCase();
799
868
  return !o || F.includes(y);
800
869
  }), C.length === 0) {
801
870
  g.style.height = "0px", x.innerHTML = "";
@@ -803,36 +872,36 @@ class I extends K {
803
872
  n.className = "tbw-filter-no-match", n.textContent = "No matching values", x.appendChild(n);
804
873
  return;
805
874
  }
806
- L();
875
+ A();
807
876
  };
808
877
  E.addEventListener(
809
878
  "scroll",
810
879
  () => {
811
- C.length > 0 && L();
880
+ C.length > 0 && A();
812
881
  },
813
882
  { passive: !0 }
814
- ), f(i.value), e.appendChild(E);
815
- let P;
816
- i.addEventListener("input", () => {
817
- clearTimeout(P), P = setTimeout(() => {
818
- this.searchText.set(l, i.value), f(i.value);
883
+ ), f(a.value), e.appendChild(E);
884
+ let L;
885
+ a.addEventListener("input", () => {
886
+ clearTimeout(L), L = setTimeout(() => {
887
+ this.searchText.set(l, a.value), f(a.value);
819
888
  }, this.config.debounceMs ?? 150);
820
889
  });
821
890
  const T = document.createElement("div");
822
891
  T.className = "tbw-filter-buttons";
823
- const A = document.createElement("button");
824
- A.className = "tbw-filter-apply-btn", A.textContent = "Apply", A.addEventListener("click", () => {
892
+ const P = document.createElement("button");
893
+ P.className = "tbw-filter-apply-btn", P.textContent = "Apply", P.addEventListener("click", () => {
825
894
  const o = [];
826
- for (const [b, y] of S)
895
+ for (const [h, y] of S)
827
896
  if (!y)
828
- if (b === "__null__")
897
+ if (h === "__null__")
829
898
  o.push(null);
830
899
  else {
831
- const n = r.find((s) => String(s) === b);
832
- o.push(n !== void 0 ? n : b);
900
+ const n = r.find((s) => String(s) === h);
901
+ o.push(n !== void 0 ? n : h);
833
902
  }
834
903
  t.applySetFilter(o);
835
- }), T.appendChild(A);
904
+ }), T.appendChild(P);
836
905
  const p = document.createElement("button");
837
906
  p.className = "tbw-filter-clear-btn", p.textContent = "Clear Filter", p.addEventListener("click", () => {
838
907
  t.clearFilter();
@@ -842,65 +911,65 @@ class I extends K {
842
911
  * Render a number range filter panel with min/max inputs and slider
843
912
  */
844
913
  renderNumberFilterPanel(e, t, r) {
845
- const { field: a, column: l } = t, c = l.filterParams, d = l.editorParams, i = (h, V) => {
846
- if (typeof h == "number") return h;
847
- if (typeof h == "string") {
848
- const H = parseFloat(h);
914
+ const { field: i, column: l } = t, c = l.filterParams, d = l.editorParams, a = (b, V) => {
915
+ if (typeof b == "number") return b;
916
+ if (typeof b == "string") {
917
+ const H = parseFloat(b);
849
918
  return isNaN(H) ? V : H;
850
919
  }
851
920
  return V;
852
- }, m = r.filter((h) => typeof h == "number" && !isNaN(h)), k = m.length > 0 ? Math.min(...m) : 0, w = m.length > 0 ? Math.max(...m) : 100, u = i(c?.min ?? d?.min, k), v = i(c?.max ?? d?.max, w), E = c?.step ?? d?.step ?? 1, g = this.filters.get(a);
921
+ }, m = r.filter((b) => typeof b == "number" && !isNaN(b)), k = m.length > 0 ? Math.min(...m) : 0, w = m.length > 0 ? Math.max(...m) : 100, u = a(c?.min ?? d?.min, k), v = a(c?.max ?? d?.max, w), E = c?.step ?? d?.step ?? 1, g = this.filters.get(i);
853
922
  let x = u, S = v;
854
- g?.operator === "between" ? (x = i(g.value, u), S = i(g.valueTo, v)) : g?.operator === "greaterThanOrEqual" ? x = i(g.value, u) : g?.operator === "lessThanOrEqual" && (S = i(g.value, v));
923
+ g?.operator === "between" ? (x = a(g.value, u), S = a(g.valueTo, v)) : g?.operator === "greaterThanOrEqual" ? x = a(g.value, u) : g?.operator === "lessThanOrEqual" && (S = a(g.value, v));
855
924
  const C = document.createElement("div");
856
925
  C.className = "tbw-filter-range-inputs";
857
926
  const R = document.createElement("div");
858
927
  R.className = "tbw-filter-range-group";
859
- const L = document.createElement("label");
860
- L.textContent = "Min", L.className = "tbw-filter-range-label";
928
+ const A = document.createElement("label");
929
+ A.textContent = "Min", A.className = "tbw-filter-range-label";
861
930
  const f = document.createElement("input");
862
- f.type = "number", f.className = "tbw-filter-range-input", f.min = String(u), f.max = String(v), f.step = String(E), f.value = String(x), R.appendChild(L), R.appendChild(f), C.appendChild(R);
863
- const P = document.createElement("span");
864
- P.className = "tbw-filter-range-separator", P.textContent = "–", C.appendChild(P);
931
+ f.type = "number", f.className = "tbw-filter-range-input", f.min = String(u), f.max = String(v), f.step = String(E), f.value = String(x), R.appendChild(A), R.appendChild(f), C.appendChild(R);
932
+ const L = document.createElement("span");
933
+ L.className = "tbw-filter-range-separator", L.textContent = "–", C.appendChild(L);
865
934
  const T = document.createElement("div");
866
935
  T.className = "tbw-filter-range-group";
867
- const A = document.createElement("label");
868
- A.textContent = "Max", A.className = "tbw-filter-range-label";
936
+ const P = document.createElement("label");
937
+ P.textContent = "Max", P.className = "tbw-filter-range-label";
869
938
  const p = document.createElement("input");
870
- p.type = "number", p.className = "tbw-filter-range-input", p.min = String(u), p.max = String(v), p.step = String(E), p.value = String(S), T.appendChild(A), T.appendChild(p), C.appendChild(T), e.appendChild(C);
939
+ p.type = "number", p.className = "tbw-filter-range-input", p.min = String(u), p.max = String(v), p.step = String(E), p.value = String(S), T.appendChild(P), T.appendChild(p), C.appendChild(T), e.appendChild(C);
871
940
  const o = document.createElement("div");
872
941
  o.className = "tbw-filter-range-slider";
873
- const b = document.createElement("div");
874
- b.className = "tbw-filter-range-track";
942
+ const h = document.createElement("div");
943
+ h.className = "tbw-filter-range-track";
875
944
  const y = document.createElement("div");
876
945
  y.className = "tbw-filter-range-fill";
877
946
  const n = document.createElement("input");
878
947
  n.type = "range", n.className = "tbw-filter-range-thumb tbw-filter-range-thumb-min", n.min = String(u), n.max = String(v), n.step = String(E), n.value = String(x);
879
948
  const s = document.createElement("input");
880
- s.type = "range", s.className = "tbw-filter-range-thumb tbw-filter-range-thumb-max", s.min = String(u), s.max = String(v), s.step = String(E), s.value = String(S), o.appendChild(b), o.appendChild(y), o.appendChild(n), o.appendChild(s), e.appendChild(o);
949
+ s.type = "range", s.className = "tbw-filter-range-thumb tbw-filter-range-thumb-max", s.min = String(u), s.max = String(v), s.step = String(E), s.value = String(S), o.appendChild(h), o.appendChild(y), o.appendChild(n), o.appendChild(s), e.appendChild(o);
881
950
  const F = () => {
882
- const h = parseFloat(n.value), V = parseFloat(s.value), H = v - u, D = (h - u) / H * 100, O = (V - u) / H * 100;
951
+ const b = parseFloat(n.value), V = parseFloat(s.value), H = v - u, D = (b - u) / H * 100, O = (V - u) / H * 100;
883
952
  y.style.left = `${D}%`, y.style.width = `${O - D}%`;
884
953
  };
885
954
  n.addEventListener("input", () => {
886
- const h = Math.min(parseFloat(n.value), parseFloat(s.value));
887
- n.value = String(h), f.value = String(h), F();
955
+ const b = Math.min(parseFloat(n.value), parseFloat(s.value));
956
+ n.value = String(b), f.value = String(b), F();
888
957
  }), s.addEventListener("input", () => {
889
- const h = Math.max(parseFloat(s.value), parseFloat(n.value));
890
- s.value = String(h), p.value = String(h), F();
958
+ const b = Math.max(parseFloat(s.value), parseFloat(n.value));
959
+ s.value = String(b), p.value = String(b), F();
891
960
  }), f.addEventListener("input", () => {
892
- let h = parseFloat(f.value) || u;
893
- h = Math.max(u, Math.min(h, parseFloat(p.value))), n.value = String(h), F();
961
+ let b = parseFloat(f.value) || u;
962
+ b = Math.max(u, Math.min(b, parseFloat(p.value))), n.value = String(b), F();
894
963
  }), p.addEventListener("input", () => {
895
- let h = parseFloat(p.value) || v;
896
- h = Math.min(v, Math.max(h, parseFloat(f.value))), s.value = String(h), F();
964
+ let b = parseFloat(p.value) || v;
965
+ b = Math.min(v, Math.max(b, parseFloat(f.value))), s.value = String(b), F();
897
966
  }), F();
898
967
  const M = document.createElement("div");
899
968
  M.className = "tbw-filter-buttons";
900
969
  const _ = document.createElement("button");
901
970
  _.className = "tbw-filter-apply-btn", _.textContent = "Apply", _.addEventListener("click", () => {
902
- const h = parseFloat(f.value), V = parseFloat(p.value);
903
- t.applyTextFilter("between", h, V);
971
+ const b = parseFloat(f.value), V = parseFloat(p.value);
972
+ t.applyTextFilter("between", b, V);
904
973
  }), M.appendChild(_);
905
974
  const z = document.createElement("button");
906
975
  z.className = "tbw-filter-clear-btn", z.textContent = "Clear Filter", z.addEventListener("click", () => {
@@ -911,32 +980,32 @@ class I extends K {
911
980
  * Render a date range filter panel with from/to date inputs
912
981
  */
913
982
  renderDateFilterPanel(e, t, r) {
914
- const { field: a, column: l } = t, c = l.filterParams, d = l.editorParams, i = r.filter((n) => n instanceof Date || typeof n == "string" && !isNaN(Date.parse(n))).map((n) => n instanceof Date ? n : new Date(n)).filter((n) => !isNaN(n.getTime())), m = i.length > 0 ? new Date(Math.min(...i.map((n) => n.getTime()))) : null, k = i.length > 0 ? new Date(Math.max(...i.map((n) => n.getTime()))) : null, w = (n) => n ? n.toISOString().split("T")[0] : "", u = (n) => n ? typeof n == "string" ? n : typeof n == "number" ? w(new Date(n)) : "" : "", v = u(c?.min) || u(d?.min) || w(m), E = u(c?.max) || u(d?.max) || w(k), g = this.filters.get(a);
983
+ const { field: i, column: l } = t, c = l.filterParams, d = l.editorParams, a = r.filter((n) => n instanceof Date || typeof n == "string" && !isNaN(Date.parse(n))).map((n) => n instanceof Date ? n : new Date(n)).filter((n) => !isNaN(n.getTime())), m = a.length > 0 ? new Date(Math.min(...a.map((n) => n.getTime()))) : null, k = a.length > 0 ? new Date(Math.max(...a.map((n) => n.getTime()))) : null, w = (n) => n ? n.toISOString().split("T")[0] : "", u = (n) => n ? typeof n == "string" ? n : typeof n == "number" ? w(new Date(n)) : "" : "", v = u(c?.min) || u(d?.min) || w(m), E = u(c?.max) || u(d?.max) || w(k), g = this.filters.get(i);
915
984
  let x = "", S = "";
916
985
  g?.operator === "between" ? (x = u(g.value) || "", S = u(g.valueTo) || "") : g?.operator === "greaterThanOrEqual" ? x = u(g.value) || "" : g?.operator === "lessThanOrEqual" && (S = u(g.value) || "");
917
986
  const C = document.createElement("div");
918
987
  C.className = "tbw-filter-date-range";
919
988
  const R = document.createElement("div");
920
989
  R.className = "tbw-filter-date-group";
921
- const L = document.createElement("label");
922
- L.textContent = "From", L.className = "tbw-filter-range-label";
990
+ const A = document.createElement("label");
991
+ A.textContent = "From", A.className = "tbw-filter-range-label";
923
992
  const f = document.createElement("input");
924
- f.type = "date", f.className = "tbw-filter-date-input", v && (f.min = v), E && (f.max = E), f.value = x, R.appendChild(L), R.appendChild(f), C.appendChild(R);
925
- const P = document.createElement("span");
926
- P.className = "tbw-filter-range-separator", P.textContent = "–", C.appendChild(P);
993
+ f.type = "date", f.className = "tbw-filter-date-input", v && (f.min = v), E && (f.max = E), f.value = x, R.appendChild(A), R.appendChild(f), C.appendChild(R);
994
+ const L = document.createElement("span");
995
+ L.className = "tbw-filter-range-separator", L.textContent = "–", C.appendChild(L);
927
996
  const T = document.createElement("div");
928
997
  T.className = "tbw-filter-date-group";
929
- const A = document.createElement("label");
930
- A.textContent = "To", A.className = "tbw-filter-range-label";
998
+ const P = document.createElement("label");
999
+ P.textContent = "To", P.className = "tbw-filter-range-label";
931
1000
  const p = document.createElement("input");
932
- p.type = "date", p.className = "tbw-filter-date-input", v && (p.min = v), E && (p.max = E), p.value = S, T.appendChild(A), T.appendChild(p), C.appendChild(T), e.appendChild(C);
1001
+ p.type = "date", p.className = "tbw-filter-date-input", v && (p.min = v), E && (p.max = E), p.value = S, T.appendChild(P), T.appendChild(p), C.appendChild(T), e.appendChild(C);
933
1002
  const o = document.createElement("div");
934
1003
  o.className = "tbw-filter-buttons";
935
- const b = document.createElement("button");
936
- b.className = "tbw-filter-apply-btn", b.textContent = "Apply", b.addEventListener("click", () => {
1004
+ const h = document.createElement("button");
1005
+ h.className = "tbw-filter-apply-btn", h.textContent = "Apply", h.addEventListener("click", () => {
937
1006
  const n = f.value, s = p.value;
938
1007
  n && s ? t.applyTextFilter("between", n, s) : n ? t.applyTextFilter("greaterThanOrEqual", n) : s ? t.applyTextFilter("lessThanOrEqual", s) : t.clearFilter();
939
- }), o.appendChild(b);
1008
+ }), o.appendChild(h);
940
1009
  const y = document.createElement("button");
941
1010
  y.className = "tbw-filter-clear-btn", y.textContent = "Clear Filter", y.addEventListener("click", () => {
942
1011
  t.clearFilter();
@@ -956,13 +1025,13 @@ class I extends K {
956
1025
  /**
957
1026
  * Apply a text/number/date filter
958
1027
  */
959
- applyTextFilter(e, t, r, a) {
1028
+ applyTextFilter(e, t, r, i) {
960
1029
  this.filters.set(e, {
961
1030
  field: e,
962
1031
  type: "text",
963
1032
  operator: t,
964
1033
  value: r,
965
- valueTo: a
1034
+ valueTo: i
966
1035
  }), this.applyFiltersInternal();
967
1036
  }
968
1037
  /**
@@ -974,19 +1043,19 @@ class I extends K {
974
1043
  if (this.config.filterHandler) {
975
1044
  const t = this.grid;
976
1045
  t.setAttribute("aria-busy", "true");
977
- const r = this.config.filterHandler(e, this.sourceRows), a = (l) => {
1046
+ const r = this.config.filterHandler(e, this.sourceRows), i = (l) => {
978
1047
  t.removeAttribute("aria-busy"), this.cachedResult = l, this.grid.rows = l, this.emit("filter-change", {
979
1048
  filters: e,
980
1049
  filteredRowCount: l.length
981
- }), this.requestRender();
1050
+ }), this.emitPluginEvent("filter-applied", { filters: e }), this.requestRender();
982
1051
  };
983
- r && typeof r.then == "function" ? r.then(a) : a(r);
1052
+ r && typeof r.then == "function" ? r.then(i) : i(r);
984
1053
  return;
985
1054
  }
986
1055
  this.emit("filter-change", {
987
1056
  filters: e,
988
1057
  filteredRowCount: 0
989
- }), this.requestRender();
1058
+ }), this.emitPluginEvent("filter-applied", { filters: e }), this.requestRender();
990
1059
  }
991
1060
  // #endregion
992
1061
  // #region Column State Hooks