@toolbox-web/grid 0.4.0 → 0.4.2

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 (161) hide show
  1. package/README.md +10 -13
  2. package/all.js +1124 -1047
  3. package/all.js.map +1 -1
  4. package/index.js +688 -515
  5. package/index.js.map +1 -1
  6. package/lib/core/grid.d.ts +10 -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/keyboard.d.ts.map +1 -1
  11. package/lib/core/internal/utils.d.ts +1 -0
  12. package/lib/core/internal/utils.d.ts.map +1 -1
  13. package/lib/core/internal/validate-config.d.ts +14 -0
  14. package/lib/core/internal/validate-config.d.ts.map +1 -1
  15. package/lib/core/plugin/base-plugin.d.ts +105 -1
  16. package/lib/core/plugin/base-plugin.d.ts.map +1 -1
  17. package/lib/core/plugin/expander-column.d.ts +51 -0
  18. package/lib/core/plugin/expander-column.d.ts.map +1 -0
  19. package/lib/core/plugin/index.d.ts +1 -0
  20. package/lib/core/plugin/index.d.ts.map +1 -1
  21. package/lib/core/plugin/plugin-manager.d.ts +1 -1
  22. package/lib/core/plugin/plugin-manager.d.ts.map +1 -1
  23. package/lib/core/plugin/types.d.ts +117 -1
  24. package/lib/core/plugin/types.d.ts.map +1 -1
  25. package/lib/core/types.d.ts +4 -2
  26. package/lib/core/types.d.ts.map +1 -1
  27. package/lib/plugins/clipboard/ClipboardPlugin.d.ts +9 -2
  28. package/lib/plugins/clipboard/ClipboardPlugin.d.ts.map +1 -1
  29. package/lib/plugins/clipboard/index.d.ts +1 -1
  30. package/lib/plugins/clipboard/index.d.ts.map +1 -1
  31. package/lib/plugins/clipboard/index.js +303 -185
  32. package/lib/plugins/clipboard/index.js.map +1 -1
  33. package/lib/plugins/clipboard/types.d.ts +72 -2
  34. package/lib/plugins/clipboard/types.d.ts.map +1 -1
  35. package/lib/plugins/column-virtualization/ColumnVirtualizationPlugin.d.ts +0 -1
  36. package/lib/plugins/column-virtualization/ColumnVirtualizationPlugin.d.ts.map +1 -1
  37. package/lib/plugins/column-virtualization/index.js +116 -24
  38. package/lib/plugins/column-virtualization/index.js.map +1 -1
  39. package/lib/plugins/context-menu/ContextMenuPlugin.d.ts +0 -1
  40. package/lib/plugins/context-menu/ContextMenuPlugin.d.ts.map +1 -1
  41. package/lib/plugins/context-menu/index.js +164 -72
  42. package/lib/plugins/context-menu/index.js.map +1 -1
  43. package/lib/plugins/editing/EditingPlugin.d.ts +1 -7
  44. package/lib/plugins/editing/EditingPlugin.d.ts.map +1 -1
  45. package/lib/plugins/editing/index.js +213 -133
  46. package/lib/plugins/editing/index.js.map +1 -1
  47. package/lib/plugins/export/ExportPlugin.d.ts +0 -1
  48. package/lib/plugins/export/ExportPlugin.d.ts.map +1 -1
  49. package/lib/plugins/export/index.js +195 -103
  50. package/lib/plugins/export/index.js.map +1 -1
  51. package/lib/plugins/filtering/FilteringPlugin.d.ts +5 -2
  52. package/lib/plugins/filtering/FilteringPlugin.d.ts.map +1 -1
  53. package/lib/plugins/filtering/index.js +145 -43
  54. package/lib/plugins/filtering/index.js.map +1 -1
  55. package/lib/plugins/grouping-columns/GroupingColumnsPlugin.d.ts +1 -2
  56. package/lib/plugins/grouping-columns/GroupingColumnsPlugin.d.ts.map +1 -1
  57. package/lib/plugins/grouping-columns/grouping-columns.d.ts +1 -1
  58. package/lib/plugins/grouping-columns/grouping-columns.d.ts.map +1 -1
  59. package/lib/plugins/grouping-columns/index.js +162 -68
  60. package/lib/plugins/grouping-columns/index.js.map +1 -1
  61. package/lib/plugins/grouping-rows/GroupingRowsPlugin.d.ts +14 -2
  62. package/lib/plugins/grouping-rows/GroupingRowsPlugin.d.ts.map +1 -1
  63. package/lib/plugins/grouping-rows/index.js +246 -138
  64. package/lib/plugins/grouping-rows/index.js.map +1 -1
  65. package/lib/plugins/master-detail/MasterDetailPlugin.d.ts +13 -11
  66. package/lib/plugins/master-detail/MasterDetailPlugin.d.ts.map +1 -1
  67. package/lib/plugins/master-detail/index.js +281 -196
  68. package/lib/plugins/master-detail/index.js.map +1 -1
  69. package/lib/plugins/master-detail/types.d.ts +0 -10
  70. package/lib/plugins/master-detail/types.d.ts.map +1 -1
  71. package/lib/plugins/multi-sort/MultiSortPlugin.d.ts +1 -2
  72. package/lib/plugins/multi-sort/MultiSortPlugin.d.ts.map +1 -1
  73. package/lib/plugins/multi-sort/index.js +121 -31
  74. package/lib/plugins/multi-sort/index.js.map +1 -1
  75. package/lib/plugins/pinned-columns/PinnedColumnsPlugin.d.ts +0 -1
  76. package/lib/plugins/pinned-columns/PinnedColumnsPlugin.d.ts.map +1 -1
  77. package/lib/plugins/pinned-columns/index.js +144 -52
  78. package/lib/plugins/pinned-columns/index.js.map +1 -1
  79. package/lib/plugins/pinned-rows/PinnedRowsPlugin.d.ts +1 -2
  80. package/lib/plugins/pinned-rows/PinnedRowsPlugin.d.ts.map +1 -1
  81. package/lib/plugins/pinned-rows/index.js +178 -88
  82. package/lib/plugins/pinned-rows/index.js.map +1 -1
  83. package/lib/plugins/pivot/PivotPlugin.d.ts +26 -4
  84. package/lib/plugins/pivot/PivotPlugin.d.ts.map +1 -1
  85. package/lib/plugins/pivot/index.js +414 -310
  86. package/lib/plugins/pivot/index.js.map +1 -1
  87. package/lib/plugins/pivot/pivot-rows.d.ts +2 -1
  88. package/lib/plugins/pivot/pivot-rows.d.ts.map +1 -1
  89. package/lib/plugins/reorder/ReorderPlugin.d.ts +13 -10
  90. package/lib/plugins/reorder/ReorderPlugin.d.ts.map +1 -1
  91. package/lib/plugins/reorder/index.js +304 -226
  92. package/lib/plugins/reorder/index.js.map +1 -1
  93. package/lib/plugins/selection/SelectionPlugin.d.ts +21 -3
  94. package/lib/plugins/selection/SelectionPlugin.d.ts.map +1 -1
  95. package/lib/plugins/selection/index.d.ts +2 -2
  96. package/lib/plugins/selection/index.d.ts.map +1 -1
  97. package/lib/plugins/selection/index.js +292 -145
  98. package/lib/plugins/selection/index.js.map +1 -1
  99. package/lib/plugins/selection/types.d.ts +24 -0
  100. package/lib/plugins/selection/types.d.ts.map +1 -1
  101. package/lib/plugins/server-side/ServerSidePlugin.d.ts +0 -1
  102. package/lib/plugins/server-side/ServerSidePlugin.d.ts.map +1 -1
  103. package/lib/plugins/server-side/index.js +95 -3
  104. package/lib/plugins/server-side/index.js.map +1 -1
  105. package/lib/plugins/tree/TreePlugin.d.ts +5 -1
  106. package/lib/plugins/tree/TreePlugin.d.ts.map +1 -1
  107. package/lib/plugins/tree/index.js +213 -112
  108. package/lib/plugins/tree/index.js.map +1 -1
  109. package/lib/plugins/tree/types.d.ts +0 -10
  110. package/lib/plugins/tree/types.d.ts.map +1 -1
  111. package/lib/plugins/undo-redo/UndoRedoPlugin.d.ts +7 -2
  112. package/lib/plugins/undo-redo/UndoRedoPlugin.d.ts.map +1 -1
  113. package/lib/plugins/undo-redo/index.js +112 -12
  114. package/lib/plugins/undo-redo/index.js.map +1 -1
  115. package/lib/plugins/visibility/VisibilityPlugin.d.ts +14 -5
  116. package/lib/plugins/visibility/VisibilityPlugin.d.ts.map +1 -1
  117. package/lib/plugins/visibility/index.js +168 -65
  118. package/lib/plugins/visibility/index.js.map +1 -1
  119. package/package.json +1 -1
  120. package/umd/grid.all.umd.js +21 -17
  121. package/umd/grid.all.umd.js.map +1 -1
  122. package/umd/grid.umd.js +14 -8
  123. package/umd/grid.umd.js.map +1 -1
  124. package/umd/plugins/clipboard.umd.js +5 -7
  125. package/umd/plugins/clipboard.umd.js.map +1 -1
  126. package/umd/plugins/column-virtualization.umd.js +1 -1
  127. package/umd/plugins/column-virtualization.umd.js.map +1 -1
  128. package/umd/plugins/context-menu.umd.js +1 -1
  129. package/umd/plugins/context-menu.umd.js.map +1 -1
  130. package/umd/plugins/editing.umd.js +1 -1
  131. package/umd/plugins/editing.umd.js.map +1 -1
  132. package/umd/plugins/export.umd.js +1 -1
  133. package/umd/plugins/export.umd.js.map +1 -1
  134. package/umd/plugins/filtering.umd.js +1 -1
  135. package/umd/plugins/filtering.umd.js.map +1 -1
  136. package/umd/plugins/grouping-columns.umd.js +1 -1
  137. package/umd/plugins/grouping-columns.umd.js.map +1 -1
  138. package/umd/plugins/grouping-rows.umd.js +1 -1
  139. package/umd/plugins/grouping-rows.umd.js.map +1 -1
  140. package/umd/plugins/master-detail.umd.js +1 -1
  141. package/umd/plugins/master-detail.umd.js.map +1 -1
  142. package/umd/plugins/multi-sort.umd.js +1 -1
  143. package/umd/plugins/multi-sort.umd.js.map +1 -1
  144. package/umd/plugins/pinned-columns.umd.js +1 -1
  145. package/umd/plugins/pinned-columns.umd.js.map +1 -1
  146. package/umd/plugins/pinned-rows.umd.js +1 -1
  147. package/umd/plugins/pinned-rows.umd.js.map +1 -1
  148. package/umd/plugins/pivot.umd.js +1 -1
  149. package/umd/plugins/pivot.umd.js.map +1 -1
  150. package/umd/plugins/reorder.umd.js +1 -1
  151. package/umd/plugins/reorder.umd.js.map +1 -1
  152. package/umd/plugins/selection.umd.js +1 -1
  153. package/umd/plugins/selection.umd.js.map +1 -1
  154. package/umd/plugins/server-side.umd.js +1 -1
  155. package/umd/plugins/server-side.umd.js.map +1 -1
  156. package/umd/plugins/tree.umd.js +1 -1
  157. package/umd/plugins/tree.umd.js.map +1 -1
  158. package/umd/plugins/undo-redo.umd.js +1 -1
  159. package/umd/plugins/undo-redo.umd.js.map +1 -1
  160. package/umd/plugins/visibility.umd.js +1 -1
  161. package/umd/plugins/visibility.umd.js.map +1 -1
@@ -10,7 +10,7 @@ function P(p) {
10
10
  totalHeight: e * l
11
11
  };
12
12
  }
13
- function M(p, e) {
13
+ function _(p, e) {
14
14
  return p <= e;
15
15
  }
16
16
  const V = {
@@ -23,9 +23,28 @@ const V = {
23
23
  dragHandle: "⋮⋮",
24
24
  toolPanel: "☰"
25
25
  };
26
- class _ {
27
- /** Plugin version - override in subclass if needed */
28
- version = "1.0.0";
26
+ class M {
27
+ /**
28
+ * Plugin dependencies - declare other plugins this one requires.
29
+ *
30
+ * Dependencies are validated when the plugin is attached.
31
+ * Required dependencies throw an error if missing.
32
+ * Optional dependencies log an info message if missing.
33
+ *
34
+ * @example
35
+ * ```typescript
36
+ * static readonly dependencies: PluginDependency[] = [
37
+ * { name: 'editing', required: true, reason: 'Tracks cell edits for undo/redo' },
38
+ * { name: 'selection', required: false, reason: 'Enables selection-based undo' },
39
+ * ];
40
+ * ```
41
+ */
42
+ static dependencies;
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";
29
48
  /** CSS styles to inject into the grid's shadow DOM */
30
49
  styles;
31
50
  /** Custom cell renderers keyed by type name */
@@ -112,12 +131,28 @@ class _ {
112
131
  emit(e, t) {
113
132
  this.grid?.dispatchEvent?.(new CustomEvent(e, { detail: t, bubbles: !0 }));
114
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
+ }
115
142
  /**
116
143
  * Request a re-render of the grid.
117
144
  */
118
145
  requestRender() {
119
146
  this.grid?.requestRender?.();
120
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
+ }
121
156
  /**
122
157
  * Request a lightweight style update without rebuilding DOM.
123
158
  * Use this instead of requestRender() when only CSS classes need updating.
@@ -151,6 +186,19 @@ class _ {
151
186
  get visibleColumns() {
152
187
  return this.grid?._visibleColumns ?? [];
153
188
  }
189
+ /**
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
+ }
154
202
  /**
155
203
  * Get the shadow root of the grid.
156
204
  */
@@ -185,6 +233,51 @@ class _ {
185
233
  const e = this.grid?.gridConfig?.icons ?? {};
186
234
  return { ...V, ...e };
187
235
  }
236
+ // #region Animation Helpers
237
+ /**
238
+ * Check if animations are enabled at the grid level.
239
+ * Respects gridConfig.animation.mode and the CSS variable set by the grid.
240
+ *
241
+ * Plugins should use this to skip animations when:
242
+ * - Animation mode is 'off' or `false`
243
+ * - User prefers reduced motion and mode is 'reduced-motion' (default)
244
+ *
245
+ * @example
246
+ * ```ts
247
+ * private get animationStyle(): 'slide' | 'fade' | false {
248
+ * if (!this.isAnimationEnabled) return false;
249
+ * return this.config.animation ?? 'slide';
250
+ * }
251
+ * ```
252
+ */
253
+ get isAnimationEnabled() {
254
+ const e = this.grid?.effectiveConfig?.animation?.mode ?? "reduced-motion";
255
+ if (e === !1 || e === "off") return !1;
256
+ if (e === !0 || e === "on") return !0;
257
+ const t = this.shadowRoot?.host;
258
+ return t ? getComputedStyle(t).getPropertyValue("--tbw-animation-enabled").trim() !== "0" : !0;
259
+ }
260
+ /**
261
+ * Get the animation duration in milliseconds from CSS variable.
262
+ * Falls back to 200ms if not set.
263
+ *
264
+ * Plugins can use this for their animation timing to stay consistent
265
+ * with the grid-level animation.duration setting.
266
+ *
267
+ * @example
268
+ * ```ts
269
+ * element.animate(keyframes, { duration: this.animationDuration });
270
+ * ```
271
+ */
272
+ get animationDuration() {
273
+ const e = this.shadowRoot?.host;
274
+ if (e) {
275
+ const t = getComputedStyle(e).getPropertyValue("--tbw-animation-duration").trim(), r = parseInt(t, 10);
276
+ if (!isNaN(r)) return r;
277
+ }
278
+ return 200;
279
+ }
280
+ // #endregion
188
281
  /**
189
282
  * Resolve an icon value to string or HTMLElement.
190
283
  * Checks plugin config first, then grid-level icons, then defaults.
@@ -277,10 +370,10 @@ function N(p, e) {
277
370
  }
278
371
  return [...t].sort((r, l) => typeof r == "number" && typeof l == "number" ? r - l : String(r).localeCompare(String(l)));
279
372
  }
280
- 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)))}";
281
- class x extends _ {
373
+ const G = ':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))}', $ = ".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)))}";
374
+ class x extends M {
282
375
  name = "filtering";
283
- version = "1.0.0";
376
+ styles = G;
284
377
  get defaultConfig() {
285
378
  return {
286
379
  debounceMs: 300,
@@ -305,6 +398,12 @@ class x extends _ {
305
398
  static LIST_OVERSCAN = 3;
306
399
  static LIST_BYPASS_THRESHOLD = 50;
307
400
  // Don't virtualize if < 50 items
401
+ /**
402
+ * Sync excludedValues map from a filter model (for set filters).
403
+ */
404
+ syncExcludedValues(e, t) {
405
+ 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);
406
+ }
308
407
  // #endregion
309
408
  // #region Lifecycle
310
409
  attach(e) {
@@ -356,7 +455,13 @@ class x extends _ {
356
455
  * Pass null to remove the filter.
357
456
  */
358
457
  setFilter(e, t) {
359
- 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", {
458
+ if (t === null)
459
+ this.filters.delete(e), this.syncExcludedValues(e, null);
460
+ else {
461
+ const r = { ...t, field: e };
462
+ this.filters.set(e, r), this.syncExcludedValues(e, r);
463
+ }
464
+ this.cachedResult = null, this.cacheKey = null, this.emit("filter-change", {
360
465
  filters: [...this.filters.values()],
361
466
  filteredRowCount: 0
362
467
  // Will be accurate after processRows
@@ -386,7 +491,7 @@ class x extends _ {
386
491
  setFilterModel(e) {
387
492
  this.filters.clear(), this.excludedValues.clear();
388
493
  for (const t of e)
389
- 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));
494
+ this.filters.set(t.field, t), this.syncExcludedValues(t.field, t);
390
495
  this.cachedResult = null, this.cacheKey = null, this.emit("filter-change", {
391
496
  filters: [...this.filters.values()],
392
497
  filteredRowCount: 0
@@ -441,7 +546,7 @@ class x extends _ {
441
546
  return;
442
547
  }
443
548
  const e = document.createElement("style");
444
- e.id = "tbw-filter-panel-styles", e.textContent = K, document.head.appendChild(e), this.globalStylesInjected = !0;
549
+ e.id = "tbw-filter-panel-styles", e.textContent = $, document.head.appendChild(e), this.globalStylesInjected = !0;
445
550
  }
446
551
  /**
447
552
  * Toggle the filter panel for a field
@@ -535,39 +640,39 @@ class x extends _ {
535
640
  const C = document.createElement("span");
536
641
  C.textContent = "Select All", f.appendChild(b), f.appendChild(C), s.appendChild(f);
537
642
  const k = () => {
538
- const a = [...m.values()], u = a.every((d) => d), h = a.every((d) => !d);
643
+ const a = [...v.values()], u = a.every((d) => d), h = a.every((d) => !d);
539
644
  b.checked = u, b.indeterminate = !u && !h;
540
645
  };
541
646
  b.addEventListener("change", () => {
542
647
  const a = b.checked;
543
- for (const u of m.keys())
544
- m.set(u, a);
648
+ for (const u of v.keys())
649
+ v.set(u, a);
545
650
  k(), F();
546
651
  }), e.appendChild(s);
547
- const v = document.createElement("div");
548
- v.className = "tbw-filter-values";
549
- const S = document.createElement("div");
550
- S.className = "tbw-filter-values-spacer", v.appendChild(S);
652
+ const m = document.createElement("div");
653
+ m.className = "tbw-filter-values";
654
+ const E = document.createElement("div");
655
+ E.className = "tbw-filter-values-spacer", m.appendChild(E);
551
656
  const g = document.createElement("div");
552
- g.className = "tbw-filter-values-content", v.appendChild(g);
553
- const m = /* @__PURE__ */ new Map();
657
+ g.className = "tbw-filter-values-content", m.appendChild(g);
658
+ const v = /* @__PURE__ */ new Map();
554
659
  r.forEach((a) => {
555
660
  const u = a == null ? "__null__" : String(a);
556
- m.set(u, !l.has(a));
661
+ v.set(u, !l.has(a));
557
662
  }), k();
558
663
  let y = [];
559
664
  const I = (a, u) => {
560
665
  const h = a == null ? "(Blank)" : String(a), d = a == null ? "__null__" : String(a), c = document.createElement("label");
561
666
  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";
562
667
  const w = document.createElement("input");
563
- w.type = "checkbox", w.className = "tbw-filter-checkbox", w.checked = m.get(d) ?? !0, w.dataset.value = d, w.addEventListener("change", () => {
564
- m.set(d, w.checked), k();
668
+ w.type = "checkbox", w.className = "tbw-filter-checkbox", w.checked = v.get(d) ?? !0, w.dataset.value = d, w.addEventListener("change", () => {
669
+ v.set(d, w.checked), k();
565
670
  });
566
- const H = document.createElement("span");
567
- return H.textContent = h, c.appendChild(w), c.appendChild(H), c;
671
+ const A = document.createElement("span");
672
+ return A.textContent = h, c.appendChild(w), c.appendChild(A), c;
568
673
  }, F = () => {
569
- const a = y.length, u = v.clientHeight, h = v.scrollTop;
570
- if (S.style.height = `${a * x.LIST_ITEM_HEIGHT}px`, M(a, x.LIST_BYPASS_THRESHOLD / 3)) {
674
+ const a = y.length, u = m.clientHeight, h = m.scrollTop;
675
+ if (E.style.height = `${a * x.LIST_ITEM_HEIGHT}px`, _(a, x.LIST_BYPASS_THRESHOLD / 3)) {
571
676
  g.innerHTML = "", g.style.transform = "translateY(0px)", y.forEach((c, w) => {
572
677
  g.appendChild(I(c, w));
573
678
  });
@@ -589,32 +694,32 @@ class x extends _ {
589
694
  const d = h == null ? "(Blank)" : String(h);
590
695
  return !a || d.toLowerCase().includes(u);
591
696
  }), y.length === 0) {
592
- S.style.height = "0px", g.innerHTML = "";
697
+ E.style.height = "0px", g.innerHTML = "";
593
698
  const h = document.createElement("div");
594
699
  h.className = "tbw-filter-no-match", h.textContent = "No matching values", g.appendChild(h);
595
700
  return;
596
701
  }
597
702
  F();
598
703
  };
599
- v.addEventListener(
704
+ m.addEventListener(
600
705
  "scroll",
601
706
  () => {
602
707
  y.length > 0 && F();
603
708
  },
604
709
  { passive: !0 }
605
- ), L(i.value), e.appendChild(v);
606
- let A;
710
+ ), L(i.value), e.appendChild(m);
711
+ let H;
607
712
  i.addEventListener("input", () => {
608
- clearTimeout(A), A = setTimeout(() => {
713
+ clearTimeout(H), H = setTimeout(() => {
609
714
  this.searchText.set(n, i.value), L(i.value);
610
715
  }, this.config.debounceMs ?? 150);
611
716
  });
612
- const T = document.createElement("div");
613
- T.className = "tbw-filter-buttons";
614
- const E = document.createElement("button");
615
- E.className = "tbw-filter-apply-btn", E.textContent = "Apply", E.addEventListener("click", () => {
717
+ const S = document.createElement("div");
718
+ S.className = "tbw-filter-buttons";
719
+ const R = document.createElement("button");
720
+ R.className = "tbw-filter-apply-btn", R.textContent = "Apply", R.addEventListener("click", () => {
616
721
  const a = [];
617
- for (const [u, h] of m)
722
+ for (const [u, h] of v)
618
723
  if (!h)
619
724
  if (u === "__null__")
620
725
  a.push(null);
@@ -623,11 +728,11 @@ class x extends _ {
623
728
  a.push(d !== void 0 ? d : u);
624
729
  }
625
730
  t.applySetFilter(a);
626
- }), T.appendChild(E);
627
- const R = document.createElement("button");
628
- R.className = "tbw-filter-clear-btn", R.textContent = "Clear Filter", R.addEventListener("click", () => {
731
+ }), S.appendChild(R);
732
+ const T = document.createElement("button");
733
+ T.className = "tbw-filter-clear-btn", T.textContent = "Clear Filter", T.addEventListener("click", () => {
629
734
  t.clearFilter();
630
- }), T.appendChild(R), e.appendChild(T);
735
+ }), S.appendChild(T), e.appendChild(S);
631
736
  }
632
737
  /**
633
738
  * Apply a set filter (exclude values)
@@ -710,9 +815,6 @@ class x extends _ {
710
815
  this.filters.set(e, r), this.cachedResult = null, this.cacheKey = null;
711
816
  }
712
817
  // #endregion
713
- // #region Styles
714
- styles = $;
715
- // #endregion
716
818
  }
717
819
  export {
718
820
  x as FilteringPlugin