@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
@@ -1,4 +1,4 @@
1
- const x = {
1
+ const R = {
2
2
  expand: "▶",
3
3
  collapse: "▼",
4
4
  sortAsc: "▲",
@@ -8,9 +8,28 @@ const x = {
8
8
  dragHandle: "⋮⋮",
9
9
  toolPanel: "☰"
10
10
  };
11
- class R {
12
- /** Plugin version - override in subclass if needed */
13
- version = "1.0.0";
11
+ class x {
12
+ /**
13
+ * Plugin dependencies - declare other plugins this one requires.
14
+ *
15
+ * Dependencies are validated when the plugin is attached.
16
+ * Required dependencies throw an error if missing.
17
+ * Optional dependencies log an info message if missing.
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * static readonly dependencies: PluginDependency[] = [
22
+ * { name: 'editing', required: true, reason: 'Tracks cell edits for undo/redo' },
23
+ * { name: 'selection', required: false, reason: 'Enables selection-based undo' },
24
+ * ];
25
+ * ```
26
+ */
27
+ static dependencies;
28
+ /**
29
+ * Plugin version - defaults to grid version for built-in plugins.
30
+ * Third-party plugins can override with their own semver.
31
+ */
32
+ version = typeof __GRID_VERSION__ < "u" ? __GRID_VERSION__ : "dev";
14
33
  /** CSS styles to inject into the grid's shadow DOM */
15
34
  styles;
16
35
  /** Custom cell renderers keyed by type name */
@@ -97,12 +116,28 @@ class R {
97
116
  emit(e, t) {
98
117
  this.grid?.dispatchEvent?.(new CustomEvent(e, { detail: t, bubbles: !0 }));
99
118
  }
119
+ /**
120
+ * Emit a cancelable custom event from the grid.
121
+ * @returns `true` if the event was cancelled (preventDefault called), `false` otherwise
122
+ */
123
+ emitCancelable(e, t) {
124
+ const r = new CustomEvent(e, { detail: t, bubbles: !0, cancelable: !0 });
125
+ return this.grid?.dispatchEvent?.(r), r.defaultPrevented;
126
+ }
100
127
  /**
101
128
  * Request a re-render of the grid.
102
129
  */
103
130
  requestRender() {
104
131
  this.grid?.requestRender?.();
105
132
  }
133
+ /**
134
+ * Request a re-render and restore focus styling afterward.
135
+ * Use this when a plugin action (like expand/collapse) triggers a render
136
+ * but needs to maintain keyboard navigation focus.
137
+ */
138
+ requestRenderWithFocus() {
139
+ this.grid?.requestRenderWithFocus?.();
140
+ }
106
141
  /**
107
142
  * Request a lightweight style update without rebuilding DOM.
108
143
  * Use this instead of requestRender() when only CSS classes need updating.
@@ -136,6 +171,19 @@ class R {
136
171
  get visibleColumns() {
137
172
  return this.grid?._visibleColumns ?? [];
138
173
  }
174
+ /**
175
+ * Get the grid as an HTMLElement for direct DOM operations.
176
+ * Use sparingly - prefer the typed GridElementRef API when possible.
177
+ *
178
+ * @example
179
+ * ```ts
180
+ * const width = this.gridElement.clientWidth;
181
+ * this.gridElement.classList.add('my-plugin-active');
182
+ * ```
183
+ */
184
+ get gridElement() {
185
+ return this.grid;
186
+ }
139
187
  /**
140
188
  * Get the shadow root of the grid.
141
189
  */
@@ -168,8 +216,53 @@ class R {
168
216
  */
169
217
  get gridIcons() {
170
218
  const e = this.grid?.gridConfig?.icons ?? {};
171
- return { ...x, ...e };
219
+ return { ...R, ...e };
172
220
  }
221
+ // #region Animation Helpers
222
+ /**
223
+ * Check if animations are enabled at the grid level.
224
+ * Respects gridConfig.animation.mode and the CSS variable set by the grid.
225
+ *
226
+ * Plugins should use this to skip animations when:
227
+ * - Animation mode is 'off' or `false`
228
+ * - User prefers reduced motion and mode is 'reduced-motion' (default)
229
+ *
230
+ * @example
231
+ * ```ts
232
+ * private get animationStyle(): 'slide' | 'fade' | false {
233
+ * if (!this.isAnimationEnabled) return false;
234
+ * return this.config.animation ?? 'slide';
235
+ * }
236
+ * ```
237
+ */
238
+ get isAnimationEnabled() {
239
+ const e = this.grid?.effectiveConfig?.animation?.mode ?? "reduced-motion";
240
+ if (e === !1 || e === "off") return !1;
241
+ if (e === !0 || e === "on") return !0;
242
+ const t = this.shadowRoot?.host;
243
+ return t ? getComputedStyle(t).getPropertyValue("--tbw-animation-enabled").trim() !== "0" : !0;
244
+ }
245
+ /**
246
+ * Get the animation duration in milliseconds from CSS variable.
247
+ * Falls back to 200ms if not set.
248
+ *
249
+ * Plugins can use this for their animation timing to stay consistent
250
+ * with the grid-level animation.duration setting.
251
+ *
252
+ * @example
253
+ * ```ts
254
+ * element.animate(keyframes, { duration: this.animationDuration });
255
+ * ```
256
+ */
257
+ get animationDuration() {
258
+ const e = this.shadowRoot?.host;
259
+ if (e) {
260
+ const t = getComputedStyle(e).getPropertyValue("--tbw-animation-duration").trim(), r = parseInt(t, 10);
261
+ if (!isNaN(r)) return r;
262
+ }
263
+ return 200;
264
+ }
265
+ // #endregion
173
266
  /**
174
267
  * Resolve an icon value to string or HTMLElement.
175
268
  * Checks plugin config first, then grid-level icons, then defaults.
@@ -199,10 +292,14 @@ class R {
199
292
  }
200
293
  // #endregion
201
294
  }
295
+ const v = "__tbw_expander";
296
+ function C(n) {
297
+ return n.field === v;
298
+ }
202
299
  const m = {
203
- sum: (n, e) => n.reduce((t, i) => t + (Number(i[e]) || 0), 0),
300
+ sum: (n, e) => n.reduce((t, r) => t + (Number(r[e]) || 0), 0),
204
301
  avg: (n, e) => {
205
- const t = n.reduce((i, r) => i + (Number(r[e]) || 0), 0);
302
+ const t = n.reduce((r, o) => r + (Number(o[e]) || 0), 0);
206
303
  return n.length ? t / n.length : 0;
207
304
  },
208
305
  count: (n) => n.length,
@@ -210,104 +307,104 @@ const m = {
210
307
  max: (n, e) => Math.max(...n.map((t) => Number(t[e]) || -1 / 0)),
211
308
  first: (n, e) => n[0]?.[e],
212
309
  last: (n, e) => n[n.length - 1]?.[e]
213
- }, _ = /* @__PURE__ */ new Map(), f = {
310
+ }, w = /* @__PURE__ */ new Map(), c = {
214
311
  /**
215
312
  * Register a custom aggregator function.
216
313
  */
217
314
  register(n, e) {
218
- _.set(n, e);
315
+ w.set(n, e);
219
316
  },
220
317
  /**
221
318
  * Unregister a custom aggregator function.
222
319
  */
223
320
  unregister(n) {
224
- _.delete(n);
321
+ w.delete(n);
225
322
  },
226
323
  /**
227
324
  * Get an aggregator function by reference.
228
325
  */
229
326
  get(n) {
230
327
  if (n !== void 0)
231
- return typeof n == "function" ? n : _.get(n) ?? m[n];
328
+ return typeof n == "function" ? n : w.get(n) ?? m[n];
232
329
  },
233
330
  /**
234
331
  * Run an aggregator on a set of rows.
235
332
  */
236
- run(n, e, t, i) {
237
- const r = this.get(n);
238
- return r ? r(e, t, i) : void 0;
333
+ run(n, e, t, r) {
334
+ const o = this.get(n);
335
+ return o ? o(e, t, r) : void 0;
239
336
  },
240
337
  /**
241
338
  * Check if an aggregator exists.
242
339
  */
243
340
  has(n) {
244
- return _.has(n) || n in m;
341
+ return w.has(n) || n in m;
245
342
  },
246
343
  /**
247
344
  * List all available aggregator names.
248
345
  */
249
346
  list() {
250
- return [...Object.keys(m), ..._.keys()];
347
+ return [...Object.keys(m), ...w.keys()];
251
348
  }
252
349
  };
253
- f.register.bind(f);
254
- f.unregister.bind(f);
255
- f.get.bind(f);
256
- const w = f.run.bind(f);
257
- f.list.bind(f);
258
- function v({ rows: n, config: e, expanded: t }) {
259
- const i = e.groupOn;
260
- if (typeof i != "function")
350
+ c.register.bind(c);
351
+ c.unregister.bind(c);
352
+ c.get.bind(c);
353
+ const y = c.run.bind(c);
354
+ c.list.bind(c);
355
+ function A({ rows: n, config: e, expanded: t }) {
356
+ const r = e.groupOn;
357
+ if (typeof r != "function")
261
358
  return [];
262
- const r = { key: "__root__", value: null, depth: -1, rows: [], children: /* @__PURE__ */ new Map() };
263
- if (n.forEach((s) => {
264
- let d = i(s);
265
- d == null || d === !1 ? d = ["__ungrouped__"] : Array.isArray(d) || (d = [d]);
266
- let a = r;
267
- d.forEach((c, b) => {
268
- const l = c == null ? "∅" : String(c), g = a.key === "__root__" ? l : a.key + "||" + l;
269
- let p = a.children.get(l);
270
- p || (p = { key: g, value: c, depth: b, rows: [], children: /* @__PURE__ */ new Map(), parent: a }, a.children.set(l, p)), a = p;
271
- }), a.rows.push(s);
272
- }), r.children.size === 1 && r.children.has("__ungrouped__") && r.children.get("__ungrouped__").rows.length === n.length)
359
+ const o = { key: "__root__", value: null, depth: -1, rows: [], children: /* @__PURE__ */ new Map() };
360
+ if (n.forEach((i) => {
361
+ let a = r(i);
362
+ a == null || a === !1 ? a = ["__ungrouped__"] : Array.isArray(a) || (a = [a]);
363
+ let d = o;
364
+ a.forEach((h, p) => {
365
+ const f = h == null ? "∅" : String(h), l = d.key === "__root__" ? f : d.key + "||" + f;
366
+ let g = d.children.get(f);
367
+ g || (g = { key: l, value: h, depth: p, rows: [], children: /* @__PURE__ */ new Map(), parent: d }, d.children.set(f, g)), d = g;
368
+ }), d.rows.push(i);
369
+ }), o.children.size === 1 && o.children.has("__ungrouped__") && o.children.get("__ungrouped__").rows.length === n.length)
273
370
  return [];
274
- const o = [], u = (s) => {
275
- if (s === r) {
276
- s.children.forEach((a) => u(a));
371
+ const s = [], u = (i) => {
372
+ if (i === o) {
373
+ i.children.forEach((d) => u(d));
277
374
  return;
278
375
  }
279
- const d = t.has(s.key);
280
- o.push({
376
+ const a = t.has(i.key);
377
+ s.push({
281
378
  kind: "group",
282
- key: s.key,
283
- value: s.value,
284
- depth: s.depth,
285
- rows: s.rows,
286
- expanded: d
287
- }), d && (s.children.size ? s.children.forEach((a) => u(a)) : s.rows.forEach((a) => o.push({ kind: "data", row: a, rowIndex: n.indexOf(a) })));
379
+ key: i.key,
380
+ value: i.value,
381
+ depth: i.depth,
382
+ rows: i.rows,
383
+ expanded: a
384
+ }), a && (i.children.size ? i.children.forEach((d) => u(d)) : i.rows.forEach((d) => s.push({ kind: "data", row: d, rowIndex: n.indexOf(d) })));
288
385
  };
289
- return u(r), o;
386
+ return u(o), s;
290
387
  }
291
- function C(n, e) {
388
+ function k(n, e) {
292
389
  const t = new Set(n);
293
390
  return t.has(e) ? t.delete(e) : t.add(e), t;
294
391
  }
295
- function A(n) {
392
+ function K(n) {
296
393
  const e = /* @__PURE__ */ new Set();
297
394
  for (const t of n)
298
395
  t.kind === "group" && e.add(t.key);
299
396
  return e;
300
397
  }
301
- function k() {
398
+ function S() {
302
399
  return /* @__PURE__ */ new Set();
303
400
  }
304
- function K(n) {
401
+ function E(n) {
305
402
  return n.kind !== "group" ? 0 : n.rows.length;
306
403
  }
307
- const S = '.group-row{display:grid;grid-template-columns:var(--tbw-column-template);background:var(--tbw-grouping-rows-bg, var(--tbw-color-panel-bg));font-weight:500;border-bottom:var(--tbw-row-divider);min-height:var(--tbw-row-height)}.group-row .cell{display:flex;align-items:center;padding:var(--tbw-cell-padding, 2px 8px)}.group-row:hover{background:var(--tbw-grouping-rows-bg-hover, var(--tbw-color-row-hover))}.group-toggle{cursor:pointer;-webkit-user-select:none;user-select:none;display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;margin-right:4px;background:none;border:0;font:inherit}.group-toggle:hover{background:var(--tbw-grouping-rows-toggle-hover, var(--tbw-color-row-hover));border-radius:2px}.group-label{display:inline-flex;align-items:center;gap:8px}.group-count{color:var(--tbw-grouping-rows-count-color, var(--tbw-color-fg-muted));font-size:.85em;font-weight:400}[data-group-depth="0"] .group-label{padding-left:0}[data-group-depth="1"] .group-label{padding-left:20px}[data-group-depth="2"] .group-label{padding-left:40px}[data-group-depth="3"] .group-label{padding-left:60px}[data-group-depth="4"] .group-label{padding-left:80px}.data-grid-row.tbw-group-slide-in{animation:tbw-group-slide-in var(--tbw-animation-duration, .2s) var(--tbw-animation-easing, ease-out) forwards}@keyframes tbw-group-slide-in{0%{opacity:0;transform:translate(-8px)}to{opacity:1;transform:translate(0)}}.data-grid-row.tbw-group-fade-in{animation:tbw-group-fade-in var(--tbw-animation-duration, .2s) var(--tbw-animation-easing, ease-out) forwards}@keyframes tbw-group-fade-in{0%{opacity:0}to{opacity:1}}';
308
- class E extends R {
404
+ const G = '.group-row{display:grid;grid-template-columns:var(--tbw-column-template);background:var(--tbw-grouping-rows-bg, var(--tbw-color-panel-bg));font-weight:500;border-bottom:var(--tbw-row-divider);min-height:var(--tbw-row-height)}.group-row .cell{display:flex;align-items:center;padding:var(--tbw-cell-padding, 2px 8px)}.group-row:hover{background:var(--tbw-grouping-rows-bg-hover, var(--tbw-color-row-hover))}.group-toggle{cursor:pointer;-webkit-user-select:none;user-select:none;display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;margin-right:4px;background:none;border:0;font:inherit}.group-toggle:hover{background:var(--tbw-grouping-rows-toggle-hover, var(--tbw-color-row-hover));border-radius:2px}.group-label{display:inline-flex;align-items:center;gap:8px}.group-count{color:var(--tbw-grouping-rows-count-color, var(--tbw-color-fg-muted));font-size:.85em;font-weight:400}[data-group-depth="0"] .group-label{padding-left:0}[data-group-depth="1"] .group-label{padding-left:20px}[data-group-depth="2"] .group-label{padding-left:40px}[data-group-depth="3"] .group-label{padding-left:60px}[data-group-depth="4"] .group-label{padding-left:80px}.data-grid-row.tbw-group-slide-in{animation:tbw-group-slide-in var(--tbw-animation-duration, .2s) var(--tbw-animation-easing, ease-out) forwards}@keyframes tbw-group-slide-in{0%{opacity:0;transform:translate(-8px)}to{opacity:1;transform:translate(0)}}.data-grid-row.tbw-group-fade-in{animation:tbw-group-fade-in var(--tbw-animation-duration, .2s) var(--tbw-animation-easing, ease-out) forwards}@keyframes tbw-group-fade-in{0%{opacity:0}to{opacity:1}}';
405
+ class T extends x {
309
406
  name = "groupingRows";
310
- version = "1.0.0";
407
+ styles = G;
311
408
  get defaultConfig() {
312
409
  return {
313
410
  defaultExpanded: !1,
@@ -325,15 +422,12 @@ class E extends R {
325
422
  keysToAnimate = /* @__PURE__ */ new Set();
326
423
  // #endregion
327
424
  // #region Animation
425
+ /**
426
+ * Get expand/collapse animation style from plugin config.
427
+ * Uses base class isAnimationEnabled to respect grid-level settings.
428
+ */
328
429
  get animationStyle() {
329
- const t = this.grid.effectiveConfig?.animation?.mode ?? "reduced-motion";
330
- if (t === !1 || t === "off") return !1;
331
- if (t !== !0 && t !== "on") {
332
- const i = this.shadowRoot?.host;
333
- if (i && getComputedStyle(i).getPropertyValue("--tbw-animation-enabled").trim() === "0")
334
- return !1;
335
- }
336
- return this.config.animation ?? "slide";
430
+ return this.isAnimationEnabled ? this.config.animation ?? "slide" : !1;
337
431
  }
338
432
  // #endregion
339
433
  // #region Lifecycle
@@ -353,119 +447,136 @@ class E extends R {
353
447
  const t = this.config;
354
448
  if (typeof t.groupOn != "function")
355
449
  return this.isActive = !1, this.flattenedRows = [], [...e];
356
- const i = v({
450
+ const r = A({
357
451
  rows: [...e],
358
452
  config: t,
359
453
  expanded: this.expandedKeys
360
454
  });
361
- if (i.length === 0)
455
+ if (r.length === 0)
362
456
  return this.isActive = !1, this.flattenedRows = [], [...e];
363
- this.isActive = !0, this.flattenedRows = i, this.keysToAnimate.clear();
364
- const r = /* @__PURE__ */ new Set();
365
- return i.forEach((o, u) => {
366
- if (o.kind === "data") {
367
- const s = `data-${u}`;
368
- r.add(s), this.previousVisibleKeys.has(s) || this.keysToAnimate.add(s);
457
+ this.isActive = !0, this.flattenedRows = r, this.keysToAnimate.clear();
458
+ const o = /* @__PURE__ */ new Set();
459
+ return r.forEach((s, u) => {
460
+ if (s.kind === "data") {
461
+ const i = `data-${u}`;
462
+ o.add(i), this.previousVisibleKeys.has(i) || this.keysToAnimate.add(i);
369
463
  }
370
- }), this.previousVisibleKeys = r, i.map((o) => o.kind === "group" ? {
464
+ }), this.previousVisibleKeys = o, r.map((s) => s.kind === "group" ? {
371
465
  __isGroupRow: !0,
372
- __groupKey: o.key,
373
- __groupValue: o.value,
374
- __groupDepth: o.depth,
375
- __groupRows: o.rows,
376
- __groupExpanded: o.expanded,
377
- __groupRowCount: K(o)
378
- } : o.row);
466
+ __groupKey: s.key,
467
+ __groupValue: s.value,
468
+ __groupDepth: s.depth,
469
+ __groupRows: s.rows,
470
+ __groupExpanded: s.expanded,
471
+ __groupRowCount: E(s)
472
+ } : s.row);
379
473
  }
380
474
  onCellClick(e) {
381
475
  const t = e.row;
382
476
  if (t?.__isGroupRow && e.originalEvent.target?.closest(".group-toggle"))
383
477
  return this.toggle(t.__groupKey), !0;
384
478
  }
479
+ onKeyDown(e) {
480
+ if (e.key !== " ") return;
481
+ const t = this.grid._focusRow, r = this.rows[t];
482
+ if (r?.__isGroupRow)
483
+ return e.preventDefault(), this.toggle(r.__groupKey), this.requestRenderWithFocus(), !0;
484
+ }
385
485
  /**
386
486
  * Render a row. Returns true if we handled the row (group row), false otherwise.
387
487
  */
388
- renderRow(e, t, i) {
488
+ renderRow(e, t, r) {
389
489
  if (!e?.__isGroupRow)
390
490
  return !1;
391
- const r = this.config;
392
- if (r.groupRowRenderer) {
393
- const s = () => {
491
+ const o = this.config;
492
+ if (o.groupRowRenderer) {
493
+ const i = () => {
394
494
  this.toggle(e.__groupKey);
395
- }, d = r.groupRowRenderer({
495
+ }, a = o.groupRowRenderer({
396
496
  key: e.__groupKey,
397
497
  value: e.__groupValue,
398
498
  depth: e.__groupDepth,
399
499
  rows: e.__groupRows,
400
500
  expanded: e.__groupExpanded,
401
- toggleExpand: s
501
+ toggleExpand: i
402
502
  });
403
- if (d)
404
- return t.className = "group-row", t.__isCustomRow = !0, t.setAttribute("data-group-depth", String(e.__groupDepth)), typeof d == "string" ? t.innerHTML = d : (t.innerHTML = "", t.appendChild(d)), !0;
503
+ if (a)
504
+ return t.className = "data-grid-row group-row", t.__isCustomRow = !0, t.setAttribute("data-group-depth", String(e.__groupDepth)), typeof a == "string" ? t.innerHTML = a : (t.innerHTML = "", t.appendChild(a)), !0;
405
505
  }
406
- const o = () => {
506
+ const s = () => {
407
507
  this.toggle(e.__groupKey);
408
508
  };
409
- return t.className = "group-row", t.__isCustomRow = !0, t.setAttribute("data-group-depth", String(e.__groupDepth)), t.setAttribute("role", "row"), t.setAttribute("aria-expanded", String(e.__groupExpanded)), t.style.paddingLeft = `${(e.__groupDepth || 0) * (r.indentWidth ?? 20)}px`, t.innerHTML = "", r.fullWidth !== !1 ? this.renderFullWidthGroupRow(e, t, o) : this.renderPerColumnGroupRow(e, t, o), !0;
509
+ return t.className = "data-grid-row group-row", t.__isCustomRow = !0, t.setAttribute("data-group-depth", String(e.__groupDepth)), t.setAttribute("role", "row"), t.setAttribute("aria-expanded", String(e.__groupExpanded)), t.style.paddingLeft = `${(e.__groupDepth || 0) * (o.indentWidth ?? 20)}px`, t.innerHTML = "", o.fullWidth !== !1 ? this.renderFullWidthGroupRow(e, t, s) : this.renderPerColumnGroupRow(e, t, s), !0;
410
510
  }
411
511
  afterRender() {
412
512
  const e = this.animationStyle;
413
513
  if (e === !1 || this.keysToAnimate.size === 0) return;
414
514
  const t = this.shadowRoot?.querySelector(".rows");
415
515
  if (!t) return;
416
- const i = e === "fade" ? "tbw-group-fade-in" : "tbw-group-slide-in";
417
- for (const r of t.querySelectorAll(".data-grid-row:not(.group-row)")) {
418
- const o = r.querySelector(".cell[data-row]"), u = o ? parseInt(o.getAttribute("data-row") ?? "-1", 10) : -1, d = this.flattenedRows[u]?.kind === "data" ? `data-${u}` : void 0;
419
- d && this.keysToAnimate.has(d) && (r.classList.add(i), r.addEventListener("animationend", () => r.classList.remove(i), { once: !0 }));
516
+ const r = e === "fade" ? "tbw-group-fade-in" : "tbw-group-slide-in";
517
+ for (const o of t.querySelectorAll(".data-grid-row:not(.group-row)")) {
518
+ const s = o.querySelector(".cell[data-row]"), u = s ? parseInt(s.getAttribute("data-row") ?? "-1", 10) : -1, a = this.flattenedRows[u]?.kind === "data" ? `data-${u}` : void 0;
519
+ a && this.keysToAnimate.has(a) && (o.classList.add(r), o.addEventListener("animationend", () => o.classList.remove(r), { once: !0 }));
420
520
  }
421
521
  this.keysToAnimate.clear();
422
522
  }
423
523
  // #endregion
424
524
  // #region Private Rendering Helpers
425
- renderFullWidthGroupRow(e, t, i) {
426
- const r = this.config, o = document.createElement("div");
427
- o.className = "cell group-full", o.style.gridColumn = "1 / -1", o.setAttribute("role", "gridcell");
428
- const u = document.createElement("button");
429
- u.type = "button", u.className = `group-toggle${e.__groupExpanded ? " expanded" : ""}`, u.setAttribute("aria-label", e.__groupExpanded ? "Collapse group" : "Expand group"), this.setIcon(u, this.resolveIcon(e.__groupExpanded ? "collapse" : "expand")), u.addEventListener("click", (a) => {
430
- a.stopPropagation(), i();
431
- }), o.appendChild(u);
432
- const s = document.createElement("span");
433
- s.className = "group-label";
434
- const d = r.formatLabel ? r.formatLabel(e.__groupValue, e.__groupDepth || 0, e.__groupKey) : String(e.__groupValue);
435
- if (s.textContent = d, o.appendChild(s), r.showRowCount !== !1) {
436
- const a = document.createElement("span");
437
- a.className = "group-count", a.textContent = `(${e.__groupRowCount ?? e.__groupRows?.length ?? 0})`, o.appendChild(a);
525
+ /**
526
+ * Create a toggle button for expanding/collapsing a group.
527
+ */
528
+ createToggleButton(e, t) {
529
+ const r = document.createElement("button");
530
+ return r.type = "button", r.className = `group-toggle${e ? " expanded" : ""}`, r.setAttribute("aria-label", e ? "Collapse group" : "Expand group"), this.setIcon(r, this.resolveIcon(e ? "collapse" : "expand")), r.addEventListener("click", (o) => {
531
+ o.stopPropagation(), t();
532
+ }), r;
533
+ }
534
+ /**
535
+ * Get the formatted label text for a group.
536
+ */
537
+ getGroupLabelText(e, t, r) {
538
+ const o = this.config;
539
+ return o.formatLabel ? o.formatLabel(e, t, r) : String(e);
540
+ }
541
+ renderFullWidthGroupRow(e, t, r) {
542
+ const o = this.config, s = document.createElement("div");
543
+ s.className = "cell group-full", s.style.gridColumn = "1 / -1", s.setAttribute("role", "gridcell"), s.setAttribute("data-col", "0"), s.appendChild(this.createToggleButton(e.__groupExpanded, r));
544
+ const u = document.createElement("span");
545
+ if (u.className = "group-label", u.textContent = this.getGroupLabelText(e.__groupValue, e.__groupDepth || 0, e.__groupKey), s.appendChild(u), o.showRowCount !== !1) {
546
+ const i = document.createElement("span");
547
+ i.className = "group-count", i.textContent = `(${e.__groupRowCount ?? e.__groupRows?.length ?? 0})`, s.appendChild(i);
438
548
  }
439
- t.appendChild(o);
549
+ t.appendChild(s);
440
550
  }
441
- renderPerColumnGroupRow(e, t, i) {
442
- const r = this.config, o = r.aggregators ?? {}, u = this.columns, s = e.__groupRows ?? [], a = this.shadowRoot?.querySelector(".body")?.style.gridTemplateColumns || "";
443
- a && (t.style.display = "grid", t.style.gridTemplateColumns = a), u.forEach((c, b) => {
551
+ renderPerColumnGroupRow(e, t, r) {
552
+ const o = this.config, s = o.aggregators ?? {}, u = this.columns, i = e.__groupRows ?? [], d = this.shadowRoot?.querySelector(".body")?.style.gridTemplateColumns || "";
553
+ d && (t.style.display = "grid", t.style.gridTemplateColumns = d);
554
+ let h = !1;
555
+ u.forEach((p, f) => {
444
556
  const l = document.createElement("div");
445
- if (l.className = "cell group-cell", l.setAttribute("data-col", String(b)), l.setAttribute("role", "gridcell"), b === 0) {
446
- const g = document.createElement("button");
447
- g.type = "button", g.className = `group-toggle${e.__groupExpanded ? " expanded" : ""}`, g.setAttribute("aria-label", e.__groupExpanded ? "Collapse group" : "Expand group"), this.setIcon(g, this.resolveIcon(e.__groupExpanded ? "collapse" : "expand")), g.addEventListener("click", (h) => {
448
- h.stopPropagation(), i();
449
- }), l.appendChild(g);
450
- const p = document.createElement("span"), y = o[c.field];
451
- if (y) {
452
- const h = w(y, s, c.field, c);
453
- p.textContent = h != null ? String(h) : String(e.__groupValue);
454
- } else {
455
- const h = r.formatLabel ? r.formatLabel(e.__groupValue, e.__groupDepth || 0, e.__groupKey) : String(e.__groupValue);
456
- p.textContent = h;
457
- }
458
- if (l.appendChild(p), r.showRowCount !== !1) {
459
- const h = document.createElement("span");
460
- h.className = "group-count", h.textContent = ` (${s.length})`, l.appendChild(h);
461
- }
462
- } else {
463
- const g = o[c.field];
557
+ if (l.className = "cell group-cell", l.setAttribute("data-col", String(f)), l.setAttribute("role", "gridcell"), C(p)) {
558
+ l.setAttribute("data-field", p.field), t.appendChild(l);
559
+ return;
560
+ }
561
+ if (h) {
562
+ const g = s[p.field];
464
563
  if (g) {
465
- const p = w(g, s, c.field, c);
466
- l.textContent = p != null ? String(p) : "";
564
+ const b = y(g, i, p.field, p);
565
+ l.textContent = b != null ? String(b) : "";
467
566
  } else
468
567
  l.textContent = "";
568
+ } else {
569
+ h = !0, l.appendChild(this.createToggleButton(e.__groupExpanded, r));
570
+ const g = document.createElement("span"), b = s[p.field];
571
+ if (b) {
572
+ const _ = y(b, i, p.field, p);
573
+ g.textContent = _ != null ? String(_) : String(e.__groupValue);
574
+ } else
575
+ g.textContent = this.getGroupLabelText(e.__groupValue, e.__groupDepth || 0, e.__groupKey);
576
+ if (l.appendChild(g), o.showRowCount !== !1) {
577
+ const _ = document.createElement("span");
578
+ _.className = "group-count", _.textContent = ` (${i.length})`, l.appendChild(_);
579
+ }
469
580
  }
470
581
  t.appendChild(l);
471
582
  });
@@ -476,21 +587,21 @@ class E extends R {
476
587
  * Expand all groups.
477
588
  */
478
589
  expandAll() {
479
- this.expandedKeys = A(this.flattenedRows), this.requestRender();
590
+ this.expandedKeys = K(this.flattenedRows), this.requestRender();
480
591
  }
481
592
  /**
482
593
  * Collapse all groups.
483
594
  */
484
595
  collapseAll() {
485
- this.expandedKeys = k(), this.requestRender();
596
+ this.expandedKeys = S(), this.requestRender();
486
597
  }
487
598
  /**
488
599
  * Toggle expansion of a specific group.
489
600
  * @param key - The group key to toggle
490
601
  */
491
602
  toggle(e) {
492
- this.expandedKeys = C(this.expandedKeys, e);
493
- const t = this.flattenedRows.find((i) => i.kind === "group" && i.key === e);
603
+ this.expandedKeys = k(this.expandedKeys, e);
604
+ const t = this.flattenedRows.find((r) => r.kind === "group" && r.key === e);
494
605
  this.emit("group-toggle", {
495
606
  key: e,
496
607
  expanded: this.expandedKeys.has(e),
@@ -579,11 +690,8 @@ class E extends R {
579
690
  this.config.groupOn = e, this.requestRender();
580
691
  }
581
692
  // #endregion
582
- // #region Styles
583
- styles = S;
584
- // #endregion
585
693
  }
586
694
  export {
587
- E as GroupingRowsPlugin
695
+ T as GroupingRowsPlugin
588
696
  };
589
697
  //# sourceMappingURL=index.js.map