@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 m = /{{\s*([^}]+)\s*}}/g, f = "__DG_EMPTY__", R = /^[\w$. '?+\-*/%:()!<>=,&|]+$/, x = /__(proto|defineGetter|defineSetter)|constructor|window|globalThis|global|process|Function|import|eval|Reflect|Proxy|Error|arguments|document|location|cookie|localStorage|sessionStorage|indexedDB|fetch|XMLHttpRequest|WebSocket|Worker|SharedWorker|ServiceWorker|opener|parent|top|frames|self|this\b/, b = /* @__PURE__ */ new Set([
1
+ const R = /{{\s*([^}]+)\s*}}/g, u = "__DG_EMPTY__", b = /^[\w$. '?+\-*/%:()!<>=,&|]+$/, E = /__(proto|defineGetter|defineSetter)|constructor|window|globalThis|global|process|Function|import|eval|Reflect|Proxy|Error|arguments|document|location|cookie|localStorage|sessionStorage|indexedDB|fetch|XMLHttpRequest|WebSocket|Worker|SharedWorker|ServiceWorker|opener|parent|top|frames|self|this\b/, v = /* @__PURE__ */ new Set([
2
2
  "script",
3
3
  "iframe",
4
4
  "object",
@@ -23,76 +23,76 @@ const m = /{{\s*([^}]+)\s*}}/g, f = "__DG_EMPTY__", R = /^[\w$. '?+\-*/%:()!<>=,
23
23
  "plaintext",
24
24
  "xmp",
25
25
  "listing"
26
- ]), w = /^on\w+$/i, E = /* @__PURE__ */ new Set(["href", "src", "action", "formaction", "data", "srcdoc", "xlink:href", "poster", "srcset"]), v = /^\s*(javascript|vbscript|data|blob):/i;
27
- function y(i) {
28
- if (!i || typeof i != "string") return "";
29
- if (i.indexOf("<") === -1) return i;
26
+ ]), h = /^on\w+$/i, y = /* @__PURE__ */ new Set(["href", "src", "action", "formaction", "data", "srcdoc", "xlink:href", "poster", "srcset"]), C = /^\s*(javascript|vbscript|data|blob):/i;
27
+ function _(n) {
28
+ if (!n || typeof n != "string") return "";
29
+ if (n.indexOf("<") === -1) return n;
30
30
  const e = document.createElement("template");
31
- return e.innerHTML = i, C(e.content), e.innerHTML;
31
+ return e.innerHTML = n, A(e.content), e.innerHTML;
32
32
  }
33
- function C(i) {
34
- const e = [], t = i.querySelectorAll("*");
35
- for (const n of t) {
36
- const s = n.tagName.toLowerCase();
37
- if (b.has(s)) {
38
- e.push(n);
33
+ function A(n) {
34
+ const e = [], t = n.querySelectorAll("*");
35
+ for (const i of t) {
36
+ const r = i.tagName.toLowerCase();
37
+ if (v.has(r)) {
38
+ e.push(i);
39
39
  continue;
40
40
  }
41
- if ((s === "svg" || n.namespaceURI === "http://www.w3.org/2000/svg") && Array.from(n.attributes).some(
42
- (o) => w.test(o.name) || o.name === "href" || o.name === "xlink:href"
41
+ if ((r === "svg" || i.namespaceURI === "http://www.w3.org/2000/svg") && Array.from(i.attributes).some(
42
+ (s) => h.test(s.name) || s.name === "href" || s.name === "xlink:href"
43
43
  )) {
44
- e.push(n);
44
+ e.push(i);
45
45
  continue;
46
46
  }
47
- const a = [];
48
- for (const r of n.attributes) {
49
- const o = r.name.toLowerCase();
50
- if (w.test(o)) {
51
- a.push(r.name);
47
+ const l = [];
48
+ for (const o of i.attributes) {
49
+ const s = o.name.toLowerCase();
50
+ if (h.test(s)) {
51
+ l.push(o.name);
52
52
  continue;
53
53
  }
54
- if (E.has(o) && v.test(r.value)) {
55
- a.push(r.name);
54
+ if (y.has(s) && C.test(o.value)) {
55
+ l.push(o.name);
56
56
  continue;
57
57
  }
58
- if (o === "style" && /expression\s*\(|javascript:|behavior\s*:/i.test(r.value)) {
59
- a.push(r.name);
58
+ if (s === "style" && /expression\s*\(|javascript:|behavior\s*:/i.test(o.value)) {
59
+ l.push(o.name);
60
60
  continue;
61
61
  }
62
62
  }
63
- a.forEach((r) => n.removeAttribute(r));
63
+ l.forEach((o) => i.removeAttribute(o));
64
64
  }
65
- e.forEach((n) => n.remove());
65
+ e.forEach((i) => i.remove());
66
66
  }
67
- function S(i, e) {
68
- if (!i || i.indexOf("{{") === -1) return i;
69
- const t = [], n = i.replace(m, (o, l) => {
70
- const d = A(l, e);
71
- return t.push({ expr: l.trim(), result: d }), d;
72
- }), s = D(n), a = t.length && t.every((o) => o.result === "" || o.result === f);
73
- return /Reflect\.|\bProxy\b|ownKeys\(/.test(i) || a ? "" : s;
67
+ function D(n, e) {
68
+ if (!n || n.indexOf("{{") === -1) return n;
69
+ const t = [], i = n.replace(R, (s, a) => {
70
+ const d = S(a, e);
71
+ return t.push({ expr: a.trim(), result: d }), d;
72
+ }), r = L(i), l = t.length && t.every((s) => s.result === "" || s.result === u);
73
+ return /Reflect\.|\bProxy\b|ownKeys\(/.test(n) || l ? "" : r;
74
74
  }
75
- function A(i, e) {
76
- if (i = (i || "").trim(), !i || /\b(Reflect|Proxy|ownKeys)\b/.test(i)) return f;
77
- if (i === "value") return e.value == null ? f : String(e.value);
78
- if (i.startsWith("row.") && !/[()?]/.test(i) && !i.includes(":")) {
79
- const n = i.slice(4), s = e.row ? e.row[n] : void 0;
80
- return s == null ? f : String(s);
81
- }
82
- if (i.length > 80 || !R.test(i) || x.test(i)) return f;
83
- const t = i.match(/\./g);
84
- if (t && t.length > 1) return f;
75
+ function S(n, e) {
76
+ if (n = (n || "").trim(), !n || /\b(Reflect|Proxy|ownKeys)\b/.test(n)) return u;
77
+ if (n === "value") return e.value == null ? u : String(e.value);
78
+ if (n.startsWith("row.") && !/[()?]/.test(n) && !n.includes(":")) {
79
+ const i = n.slice(4), r = e.row ? e.row[i] : void 0;
80
+ return r == null ? u : String(r);
81
+ }
82
+ if (n.length > 80 || !b.test(n) || E.test(n)) return u;
83
+ const t = n.match(/\./g);
84
+ if (t && t.length > 1) return u;
85
85
  try {
86
- const s = new Function("value", "row", `return (${i});`)(e.value, e.row), a = s == null ? "" : String(s);
87
- return /Reflect|Proxy|ownKeys/.test(a) ? f : a || f;
86
+ const r = new Function("value", "row", `return (${n});`)(e.value, e.row), l = r == null ? "" : String(r);
87
+ return /Reflect|Proxy|ownKeys/.test(l) ? u : l || u;
88
88
  } catch {
89
- return f;
89
+ return u;
90
90
  }
91
91
  }
92
- function D(i) {
93
- return i && i.replace(new RegExp(f, "g"), "").replace(/Reflect\.[^<>{}\s]+/g, "").replace(/\bProxy\b/g, "").replace(/ownKeys\([^)]*\)/g, "");
92
+ function L(n) {
93
+ return n && n.replace(new RegExp(u, "g"), "").replace(/Reflect\.[^<>{}\s]+/g, "").replace(/\bProxy\b/g, "").replace(/ownKeys\([^)]*\)/g, "");
94
94
  }
95
- const H = {
95
+ const I = {
96
96
  expand: "▶",
97
97
  collapse: "▼",
98
98
  sortAsc: "▲",
@@ -102,9 +102,28 @@ const H = {
102
102
  dragHandle: "⋮⋮",
103
103
  toolPanel: "☰"
104
104
  };
105
- class _ {
106
- /** Plugin version - override in subclass if needed */
107
- version = "1.0.0";
105
+ class T {
106
+ /**
107
+ * Plugin dependencies - declare other plugins this one requires.
108
+ *
109
+ * Dependencies are validated when the plugin is attached.
110
+ * Required dependencies throw an error if missing.
111
+ * Optional dependencies log an info message if missing.
112
+ *
113
+ * @example
114
+ * ```typescript
115
+ * static readonly dependencies: PluginDependency[] = [
116
+ * { name: 'editing', required: true, reason: 'Tracks cell edits for undo/redo' },
117
+ * { name: 'selection', required: false, reason: 'Enables selection-based undo' },
118
+ * ];
119
+ * ```
120
+ */
121
+ static dependencies;
122
+ /**
123
+ * Plugin version - defaults to grid version for built-in plugins.
124
+ * Third-party plugins can override with their own semver.
125
+ */
126
+ version = typeof __GRID_VERSION__ < "u" ? __GRID_VERSION__ : "dev";
108
127
  /** CSS styles to inject into the grid's shadow DOM */
109
128
  styles;
110
129
  /** Custom cell renderers keyed by type name */
@@ -191,12 +210,28 @@ class _ {
191
210
  emit(e, t) {
192
211
  this.grid?.dispatchEvent?.(new CustomEvent(e, { detail: t, bubbles: !0 }));
193
212
  }
213
+ /**
214
+ * Emit a cancelable custom event from the grid.
215
+ * @returns `true` if the event was cancelled (preventDefault called), `false` otherwise
216
+ */
217
+ emitCancelable(e, t) {
218
+ const i = new CustomEvent(e, { detail: t, bubbles: !0, cancelable: !0 });
219
+ return this.grid?.dispatchEvent?.(i), i.defaultPrevented;
220
+ }
194
221
  /**
195
222
  * Request a re-render of the grid.
196
223
  */
197
224
  requestRender() {
198
225
  this.grid?.requestRender?.();
199
226
  }
227
+ /**
228
+ * Request a re-render and restore focus styling afterward.
229
+ * Use this when a plugin action (like expand/collapse) triggers a render
230
+ * but needs to maintain keyboard navigation focus.
231
+ */
232
+ requestRenderWithFocus() {
233
+ this.grid?.requestRenderWithFocus?.();
234
+ }
200
235
  /**
201
236
  * Request a lightweight style update without rebuilding DOM.
202
237
  * Use this instead of requestRender() when only CSS classes need updating.
@@ -230,6 +265,19 @@ class _ {
230
265
  get visibleColumns() {
231
266
  return this.grid?._visibleColumns ?? [];
232
267
  }
268
+ /**
269
+ * Get the grid as an HTMLElement for direct DOM operations.
270
+ * Use sparingly - prefer the typed GridElementRef API when possible.
271
+ *
272
+ * @example
273
+ * ```ts
274
+ * const width = this.gridElement.clientWidth;
275
+ * this.gridElement.classList.add('my-plugin-active');
276
+ * ```
277
+ */
278
+ get gridElement() {
279
+ return this.grid;
280
+ }
233
281
  /**
234
282
  * Get the shadow root of the grid.
235
283
  */
@@ -262,8 +310,53 @@ class _ {
262
310
  */
263
311
  get gridIcons() {
264
312
  const e = this.grid?.gridConfig?.icons ?? {};
265
- return { ...H, ...e };
313
+ return { ...I, ...e };
314
+ }
315
+ // #region Animation Helpers
316
+ /**
317
+ * Check if animations are enabled at the grid level.
318
+ * Respects gridConfig.animation.mode and the CSS variable set by the grid.
319
+ *
320
+ * Plugins should use this to skip animations when:
321
+ * - Animation mode is 'off' or `false`
322
+ * - User prefers reduced motion and mode is 'reduced-motion' (default)
323
+ *
324
+ * @example
325
+ * ```ts
326
+ * private get animationStyle(): 'slide' | 'fade' | false {
327
+ * if (!this.isAnimationEnabled) return false;
328
+ * return this.config.animation ?? 'slide';
329
+ * }
330
+ * ```
331
+ */
332
+ get isAnimationEnabled() {
333
+ const e = this.grid?.effectiveConfig?.animation?.mode ?? "reduced-motion";
334
+ if (e === !1 || e === "off") return !1;
335
+ if (e === !0 || e === "on") return !0;
336
+ const t = this.shadowRoot?.host;
337
+ return t ? getComputedStyle(t).getPropertyValue("--tbw-animation-enabled").trim() !== "0" : !0;
266
338
  }
339
+ /**
340
+ * Get the animation duration in milliseconds from CSS variable.
341
+ * Falls back to 200ms if not set.
342
+ *
343
+ * Plugins can use this for their animation timing to stay consistent
344
+ * with the grid-level animation.duration setting.
345
+ *
346
+ * @example
347
+ * ```ts
348
+ * element.animate(keyframes, { duration: this.animationDuration });
349
+ * ```
350
+ */
351
+ get animationDuration() {
352
+ const e = this.shadowRoot?.host;
353
+ if (e) {
354
+ const t = getComputedStyle(e).getPropertyValue("--tbw-animation-duration").trim(), i = parseInt(t, 10);
355
+ if (!isNaN(i)) return i;
356
+ }
357
+ return 200;
358
+ }
359
+ // #endregion
267
360
  /**
268
361
  * Resolve an icon value to string or HTMLElement.
269
362
  * Checks plugin config first, then grid-level icons, then defaults.
@@ -293,33 +386,58 @@ class _ {
293
386
  }
294
387
  // #endregion
295
388
  }
296
- function p(i, e) {
297
- const t = new Set(i);
389
+ const p = "__tbw_expander", H = 32;
390
+ function m(n) {
391
+ return n.field === p;
392
+ }
393
+ function k(n) {
394
+ return n.find(m);
395
+ }
396
+ function O(n) {
397
+ return {
398
+ field: p,
399
+ header: "",
400
+ // No header text - visually merges with next column
401
+ width: H,
402
+ resizable: !1,
403
+ sortable: !1,
404
+ meta: {
405
+ lockPosition: !0,
406
+ suppressMovable: !0,
407
+ expanderColumn: !0,
408
+ expanderPlugin: n,
409
+ utility: !0
410
+ // Marks this as a utility column (excluded from selection, clipboard, etc.)
411
+ }
412
+ };
413
+ }
414
+ function g(n, e) {
415
+ const t = new Set(n);
298
416
  return t.has(e) ? t.delete(e) : t.add(e), t;
299
417
  }
300
- function k(i, e) {
301
- const t = new Set(i);
418
+ function N(n, e) {
419
+ const t = new Set(n);
302
420
  return t.add(e), t;
303
421
  }
304
- function L(i, e) {
305
- const t = new Set(i);
422
+ function q(n, e) {
423
+ const t = new Set(n);
306
424
  return t.delete(e), t;
307
425
  }
308
- function T(i, e) {
309
- return i.has(e);
426
+ function P(n, e) {
427
+ return n.has(e);
310
428
  }
311
- function I(i, e, t, n) {
312
- const s = document.createElement("div");
313
- s.className = "master-detail-row", s.setAttribute("data-detail-for", String(e)), s.setAttribute("role", "row");
314
- const a = document.createElement("div");
315
- a.className = "master-detail-cell", a.setAttribute("role", "cell"), a.style.gridColumn = `1 / ${n + 1}`;
316
- const r = t(i, e);
317
- return typeof r == "string" ? a.innerHTML = r : r instanceof HTMLElement && a.appendChild(r), s.appendChild(a), s;
429
+ function M(n, e, t, i) {
430
+ const r = document.createElement("div");
431
+ r.className = "master-detail-row", r.setAttribute("data-detail-for", String(e)), r.setAttribute("role", "row");
432
+ const l = document.createElement("div");
433
+ l.className = "master-detail-cell", l.setAttribute("role", "cell"), l.style.gridColumn = `1 / ${i + 1}`;
434
+ const o = t(n, e);
435
+ return typeof o == "string" ? l.innerHTML = o : o instanceof HTMLElement && l.appendChild(o), r.appendChild(l), r;
318
436
  }
319
- const O = ".master-detail-cell-wrapper{display:flex;align-items:center;gap:4px}.master-detail-toggle{cursor:pointer;opacity:.7;-webkit-user-select:none;user-select:none;display:inline-flex;align-items:center;justify-content:center}.master-detail-toggle:hover{opacity:1}.master-detail-row{grid-column:1 / -1;display:grid;background:var(--tbw-master-detail-bg, var(--tbw-color-row-alt));border-bottom:1px solid var(--tbw-master-detail-border, var(--tbw-color-border));overflow:hidden}.master-detail-cell{padding:16px;overflow:auto}.master-detail-row.tbw-expanding{animation:tbw-detail-expand var(--tbw-animation-duration, .2s) var(--tbw-animation-easing, ease-out) forwards}.master-detail-row.tbw-collapsing{animation:tbw-detail-collapse var(--tbw-animation-duration, .2s) var(--tbw-animation-easing, ease-out) forwards}@keyframes tbw-detail-expand{0%{opacity:0;max-height:0;padding-top:0;padding-bottom:0}to{opacity:1;max-height:500px;padding-top:16px;padding-bottom:16px}}@keyframes tbw-detail-collapse{0%{opacity:1;max-height:500px}to{opacity:0;max-height:0}}";
320
- class q extends _ {
437
+ const F = ".cell[data-field=__tbw_expander]{border-right:none!important;padding:0;display:flex;align-items:center;justify-content:center}.header-row .cell[data-field=__tbw_expander]{visibility:hidden;border:none!important;padding:0;overflow:hidden}.header-row .cell[data-field=__tbw_expander]+.cell{margin-left:-32px;padding-left:calc(var(--tbw-cell-padding, 8px) + 32px)}.master-detail-expander{display:flex;align-items:center;justify-content:center;width:100%;height:100%}.master-detail-toggle{cursor:pointer;opacity:.7;-webkit-user-select:none;user-select:none;display:inline-flex;align-items:center;justify-content:center}.master-detail-toggle:hover{opacity:1}.master-detail-row{grid-column:1 / -1;display:grid;background:var(--tbw-master-detail-bg, var(--tbw-color-row-alt));border-bottom:1px solid var(--tbw-master-detail-border, var(--tbw-color-border));overflow:hidden}.master-detail-cell{padding:16px;overflow:auto}.master-detail-row.tbw-expanding{animation:tbw-detail-expand var(--tbw-animation-duration, .2s) var(--tbw-animation-easing, ease-out) forwards}.master-detail-row.tbw-collapsing{animation:tbw-detail-collapse var(--tbw-animation-duration, .2s) var(--tbw-animation-easing, ease-out) forwards}@keyframes tbw-detail-expand{0%{opacity:0;max-height:0;padding-top:0;padding-bottom:0}to{opacity:1;max-height:500px;padding-top:16px;padding-bottom:16px}}@keyframes tbw-detail-collapse{0%{opacity:1;max-height:500px}to{opacity:0;max-height:0}}";
438
+ class w extends T {
321
439
  name = "masterDetail";
322
- version = "1.0.0";
440
+ styles = F;
323
441
  get defaultConfig() {
324
442
  return {
325
443
  detailHeight: "auto",
@@ -365,52 +483,31 @@ class q extends _ {
365
483
  if (!e || typeof e.querySelector != "function") return;
366
484
  const t = e.querySelector("tbw-grid-detail");
367
485
  if (!t) return;
368
- const n = e;
369
- if (n.__frameworkAdapter?.parseDetailElement) {
370
- const c = n.__frameworkAdapter.parseDetailElement(t);
371
- if (c) {
372
- this.config = { ...this.config, detailRenderer: c };
486
+ const i = e;
487
+ if (i.__frameworkAdapter?.parseDetailElement) {
488
+ const f = i.__frameworkAdapter.parseDetailElement(t);
489
+ if (f) {
490
+ this.config = { ...this.config, detailRenderer: f };
373
491
  return;
374
492
  }
375
493
  }
376
- const s = t.getAttribute("animation"), a = t.getAttribute("show-expand-column"), r = t.getAttribute("expand-on-row-click"), o = t.getAttribute("collapse-on-click-outside"), l = t.getAttribute("height"), d = {};
377
- s !== null && (d.animation = s === "false" ? !1 : s), a !== null && (d.showExpandColumn = a !== "false"), r !== null && (d.expandOnRowClick = r === "true"), o !== null && (d.collapseOnClickOutside = o === "true"), l !== null && (d.detailHeight = l === "auto" ? "auto" : parseInt(l, 10));
378
- const u = t.innerHTML.trim();
379
- u && !this.config.detailRenderer && (d.detailRenderer = (c, h) => {
380
- const g = S(u, { value: c, row: c });
381
- return y(g);
494
+ const r = t.getAttribute("animation"), l = t.getAttribute("show-expand-column"), o = t.getAttribute("expand-on-row-click"), s = t.getAttribute("collapse-on-click-outside"), a = t.getAttribute("height"), d = {};
495
+ r !== null && (d.animation = r === "false" ? !1 : r), l !== null && (d.showExpandColumn = l !== "false"), o !== null && (d.expandOnRowClick = o === "true"), s !== null && (d.collapseOnClickOutside = s === "true"), a !== null && (d.detailHeight = a === "auto" ? "auto" : parseInt(a, 10));
496
+ const c = t.innerHTML.trim();
497
+ c && !this.config.detailRenderer && (d.detailRenderer = (f, G) => {
498
+ const x = D(c, { value: f, row: f });
499
+ return _(x);
382
500
  }), Object.keys(d).length > 0 && (this.config = { ...this.config, ...d });
383
501
  }
384
502
  // #endregion
385
503
  // #region Animation Helpers
386
- /**
387
- * Check if animations are enabled at the grid level.
388
- * Respects gridConfig.animation.mode and CSS variable.
389
- */
390
- get isAnimationEnabled() {
391
- const t = this.grid.effectiveConfig?.animation?.mode ?? "reduced-motion";
392
- if (t === !1 || t === "off") return !1;
393
- if (t === !0 || t === "on") return !0;
394
- const n = this.shadowRoot?.host;
395
- return n ? getComputedStyle(n).getPropertyValue("--tbw-animation-enabled").trim() !== "0" : !0;
396
- }
397
504
  /**
398
505
  * Get expand/collapse animation style from plugin config.
506
+ * Uses base class isAnimationEnabled to respect grid-level settings.
399
507
  */
400
508
  get animationStyle() {
401
509
  return this.isAnimationEnabled ? this.config.animation ?? "slide" : !1;
402
510
  }
403
- /**
404
- * Get animation duration from CSS variable (set by grid).
405
- */
406
- get animationDuration() {
407
- const e = this.shadowRoot?.host;
408
- if (e) {
409
- const t = getComputedStyle(e).getPropertyValue("--tbw-animation-duration").trim(), n = parseInt(t, 10);
410
- if (!isNaN(n)) return n;
411
- }
412
- return 200;
413
- }
414
511
  /**
415
512
  * Apply expand animation to a detail element.
416
513
  */
@@ -432,15 +529,34 @@ class q extends _ {
432
529
  return;
433
530
  }
434
531
  e.classList.add("tbw-collapsing");
435
- const n = () => {
532
+ const i = () => {
436
533
  e.classList.remove("tbw-collapsing"), t();
437
534
  };
438
- e.addEventListener("animationend", n, { once: !0 }), setTimeout(n, this.animationDuration + 50);
535
+ e.addEventListener("animationend", i, { once: !0 }), setTimeout(i, this.animationDuration + 50);
439
536
  }
440
537
  // #endregion
441
538
  // #region Internal State
442
539
  expandedRows = /* @__PURE__ */ new Set();
443
540
  detailElements = /* @__PURE__ */ new Map();
541
+ /** Default height for detail rows when not configured */
542
+ static DEFAULT_DETAIL_HEIGHT = 150;
543
+ /**
544
+ * Get the estimated height for a detail row.
545
+ */
546
+ getDetailHeight(e) {
547
+ const t = this.detailElements.get(e);
548
+ return t ? t.offsetHeight : typeof this.config?.detailHeight == "number" ? this.config.detailHeight : w.DEFAULT_DETAIL_HEIGHT;
549
+ }
550
+ /**
551
+ * Toggle a row's detail and emit event.
552
+ */
553
+ toggleAndEmit(e, t) {
554
+ this.expandedRows = g(this.expandedRows, e), this.emit("detail-expand", {
555
+ rowIndex: t,
556
+ row: e,
557
+ expanded: this.expandedRows.has(e)
558
+ }), this.requestRender();
559
+ }
444
560
  // #endregion
445
561
  // #region Lifecycle
446
562
  detach() {
@@ -449,49 +565,36 @@ class q extends _ {
449
565
  // #endregion
450
566
  // #region Hooks
451
567
  processColumns(e) {
452
- if (!this.config.detailRenderer)
568
+ if (!this.config.detailRenderer || this.config.showExpandColumn === !1)
453
569
  return [...e];
454
570
  const t = [...e];
455
- if (t.length > 0) {
456
- const n = { ...t[0] }, s = n.viewRenderer;
457
- if (s?.__masterDetailWrapped)
458
- return t;
459
- const a = (r) => {
460
- const { value: o, row: l } = r, d = this.expandedRows.has(l), u = document.createElement("span");
461
- u.className = "master-detail-cell-wrapper";
462
- const c = document.createElement("span");
463
- c.className = `master-detail-toggle${d ? " expanded" : ""}`, this.setIcon(c, this.resolveIcon(d ? "collapse" : "expand")), c.setAttribute("role", "button"), c.setAttribute("tabindex", "0"), c.setAttribute("aria-expanded", String(d)), c.setAttribute("aria-label", d ? "Collapse details" : "Expand details"), u.appendChild(c);
464
- const h = document.createElement("span");
465
- if (s) {
466
- const g = s(r);
467
- g instanceof Node ? h.appendChild(g) : h.textContent = String(g ?? o ?? "");
468
- } else
469
- h.textContent = String(o ?? "");
470
- return u.appendChild(h), u;
471
- };
472
- a.__masterDetailWrapped = !0, n.viewRenderer = a, t[0] = n;
473
- }
474
- return t;
571
+ if (k(t))
572
+ return t;
573
+ const r = O(this.name);
574
+ return r.viewRenderer = (l) => {
575
+ const { row: o } = l, s = this.expandedRows.has(o), a = document.createElement("span");
576
+ a.className = "master-detail-expander expander-cell";
577
+ const d = document.createElement("span");
578
+ return d.className = `master-detail-toggle${s ? " expanded" : ""}`, this.setIcon(d, this.resolveIcon(s ? "collapse" : "expand")), d.setAttribute("role", "button"), d.setAttribute("tabindex", "0"), d.setAttribute("aria-expanded", String(s)), d.setAttribute("aria-label", s ? "Collapse details" : "Expand details"), a.appendChild(d), a;
579
+ }, [r, ...t];
475
580
  }
476
581
  onRowClick(e) {
477
582
  if (!(!this.config.expandOnRowClick || !this.config.detailRenderer))
478
- return this.expandedRows = p(this.expandedRows, e.row), this.emit("detail-expand", {
479
- rowIndex: e.rowIndex,
480
- row: e.row,
481
- expanded: this.expandedRows.has(e.row)
482
- }), this.requestRender(), !1;
583
+ return this.toggleAndEmit(e.row, e.rowIndex), !1;
483
584
  }
484
585
  onCellClick(e) {
485
- if (e.originalEvent?.target?.classList.contains("master-detail-toggle")) {
486
- const n = e.row, s = e.rowIndex;
487
- return this.expandedRows = p(this.expandedRows, n), this.emit("detail-expand", {
488
- rowIndex: s,
489
- row: n,
490
- expanded: this.expandedRows.has(n)
491
- }), this.requestRender(), !0;
492
- }
586
+ if (e.originalEvent?.target?.classList.contains("master-detail-toggle"))
587
+ return this.toggleAndEmit(e.row, e.rowIndex), !0;
493
588
  this.expandedRows.size > 0 && queueMicrotask(() => this.#e());
494
589
  }
590
+ onKeyDown(e) {
591
+ if (e.key !== " ") return;
592
+ const t = this.grid._focusCol, i = this.grid._focusRow, r = this.columns[t];
593
+ if (!r || !m(r)) return;
594
+ const l = this.rows[i];
595
+ if (l)
596
+ return e.preventDefault(), this.toggleAndEmit(l, i), this.requestRenderWithFocus(), !0;
597
+ }
495
598
  afterRender() {
496
599
  this.#e();
497
600
  }
@@ -510,26 +613,26 @@ class q extends _ {
510
613
  if (!this.config.detailRenderer) return;
511
614
  const e = this.shadowRoot?.querySelector(".rows");
512
615
  if (!e) return;
513
- const t = /* @__PURE__ */ new Map(), n = e.querySelectorAll(".data-grid-row"), s = this.columns.length;
514
- for (const r of n) {
515
- const o = r.querySelector(".cell[data-row]"), l = o ? parseInt(o.getAttribute("data-row") ?? "-1", 10) : -1;
516
- l >= 0 && t.set(l, r);
616
+ const t = /* @__PURE__ */ new Map(), i = e.querySelectorAll(".data-grid-row"), r = this.columns.length;
617
+ for (const o of i) {
618
+ const s = o.querySelector(".cell[data-row]"), a = s ? parseInt(s.getAttribute("data-row") ?? "-1", 10) : -1;
619
+ a >= 0 && t.set(a, o);
517
620
  }
518
- const a = e.querySelectorAll(".master-detail-row");
519
- for (const r of a) {
520
- const o = parseInt(r.getAttribute("data-detail-for") ?? "-1", 10), l = o >= 0 ? this.rows[o] : void 0, d = l && this.expandedRows.has(l), u = t.has(o);
521
- (!d || !u) && (r.remove(), l && this.detailElements.delete(l));
621
+ const l = e.querySelectorAll(".master-detail-row");
622
+ for (const o of l) {
623
+ const s = parseInt(o.getAttribute("data-detail-for") ?? "-1", 10), a = s >= 0 ? this.rows[s] : void 0, d = a && this.expandedRows.has(a), c = t.has(s);
624
+ (!d || !c) && (o.remove(), a && this.detailElements.delete(a));
522
625
  }
523
- for (const [r, o] of t) {
524
- const l = this.rows[r];
525
- if (!l || !this.expandedRows.has(l)) continue;
526
- const d = this.detailElements.get(l);
626
+ for (const [o, s] of t) {
627
+ const a = this.rows[o];
628
+ if (!a || !this.expandedRows.has(a)) continue;
629
+ const d = this.detailElements.get(a);
527
630
  if (d) {
528
- d.previousElementSibling !== o && o.after(d);
631
+ d.previousElementSibling !== s && s.after(d);
529
632
  continue;
530
633
  }
531
- const u = I(l, r, this.config.detailRenderer, s);
532
- typeof this.config.detailHeight == "number" && (u.style.height = `${this.config.detailHeight}px`), o.after(u), this.detailElements.set(l, u), this.animateExpand(u);
634
+ const c = M(a, o, this.config.detailRenderer, r);
635
+ typeof this.config.detailHeight == "number" && (c.style.height = `${this.config.detailHeight}px`), s.after(c), this.detailElements.set(a, c), this.animateExpand(c);
533
636
  }
534
637
  }
535
638
  /**
@@ -538,15 +641,8 @@ class q extends _ {
538
641
  */
539
642
  getExtraHeight() {
540
643
  let e = 0;
541
- for (const t of this.expandedRows) {
542
- const n = this.detailElements.get(t);
543
- if (n)
544
- e += n.offsetHeight;
545
- else {
546
- const s = this.config?.detailHeight;
547
- e += typeof s == "number" ? s : 150;
548
- }
549
- }
644
+ for (const t of this.expandedRows)
645
+ e += this.getDetailHeight(t);
550
646
  return e;
551
647
  }
552
648
  /**
@@ -555,17 +651,9 @@ class q extends _ {
555
651
  */
556
652
  getExtraHeightBefore(e) {
557
653
  let t = 0;
558
- for (const n of this.expandedRows) {
559
- const s = this.rows.indexOf(n);
560
- if (s >= 0 && s < e) {
561
- const a = this.detailElements.get(n);
562
- if (a)
563
- t += a.offsetHeight;
564
- else {
565
- const r = this.config?.detailHeight;
566
- t += typeof r == "number" ? r : 150;
567
- }
568
- }
654
+ for (const i of this.expandedRows) {
655
+ const r = this.rows.indexOf(i);
656
+ r >= 0 && r < e && (t += this.getDetailHeight(i));
569
657
  }
570
658
  return t;
571
659
  }
@@ -573,20 +661,20 @@ class q extends _ {
573
661
  * Adjust the virtualization start index to keep expanded row visible while its detail is visible.
574
662
  * This ensures the detail scrolls smoothly out of view instead of disappearing abruptly.
575
663
  */
576
- adjustVirtualStart(e, t, n) {
664
+ adjustVirtualStart(e, t, i) {
577
665
  if (this.expandedRows.size === 0) return e;
578
- const s = [];
579
- for (const o of this.expandedRows) {
580
- const l = this.rows.indexOf(o);
581
- l >= 0 && s.push({ index: l, row: o });
666
+ const r = [];
667
+ for (const s of this.expandedRows) {
668
+ const a = this.rows.indexOf(s);
669
+ a >= 0 && r.push({ index: a, row: s });
582
670
  }
583
- s.sort((o, l) => o.index - l.index);
584
- let a = e, r = 0;
585
- for (const { index: o, row: l } of s) {
586
- const d = o * n + r, c = this.detailElements.get(l)?.offsetHeight ?? (typeof this.config?.detailHeight == "number" ? this.config.detailHeight : 150), h = d + n + c;
587
- r += c, !(o >= e) && h > t && o < a && (a = o);
671
+ r.sort((s, a) => s.index - a.index);
672
+ let l = e, o = 0;
673
+ for (const { index: s, row: a } of r) {
674
+ const d = s * i + o, c = this.getDetailHeight(a), f = d + i + c;
675
+ o += c, !(s >= e) && f > t && s < l && (l = s);
588
676
  }
589
- return a;
677
+ return l;
590
678
  }
591
679
  // #endregion
592
680
  // #region Public API
@@ -596,7 +684,7 @@ class q extends _ {
596
684
  */
597
685
  expand(e) {
598
686
  const t = this.rows[e];
599
- t && (this.expandedRows = k(this.expandedRows, t), this.requestRender());
687
+ t && (this.expandedRows = N(this.expandedRows, t), this.requestRender());
600
688
  }
601
689
  /**
602
690
  * Collapse the detail row at the given index.
@@ -604,7 +692,7 @@ class q extends _ {
604
692
  */
605
693
  collapse(e) {
606
694
  const t = this.rows[e];
607
- t && (this.expandedRows = L(this.expandedRows, t), this.requestRender());
695
+ t && (this.expandedRows = q(this.expandedRows, t), this.requestRender());
608
696
  }
609
697
  /**
610
698
  * Toggle the detail row at the given index.
@@ -612,7 +700,7 @@ class q extends _ {
612
700
  */
613
701
  toggle(e) {
614
702
  const t = this.rows[e];
615
- t && (this.expandedRows = p(this.expandedRows, t), this.requestRender());
703
+ t && (this.expandedRows = g(this.expandedRows, t), this.requestRender());
616
704
  }
617
705
  /**
618
706
  * Check if the detail row at the given index is expanded.
@@ -621,7 +709,7 @@ class q extends _ {
621
709
  */
622
710
  isExpanded(e) {
623
711
  const t = this.rows[e];
624
- return t ? T(this.expandedRows, t) : !1;
712
+ return t ? P(this.expandedRows, t) : !1;
625
713
  }
626
714
  /**
627
715
  * Expand all detail rows.
@@ -644,8 +732,8 @@ class q extends _ {
644
732
  getExpandedRows() {
645
733
  const e = [];
646
734
  for (const t of this.expandedRows) {
647
- const n = this.rows.indexOf(t);
648
- n >= 0 && e.push(n);
735
+ const i = this.rows.indexOf(t);
736
+ i >= 0 && e.push(i);
649
737
  }
650
738
  return e;
651
739
  }
@@ -673,11 +761,8 @@ class q extends _ {
673
761
  }
674
762
  }
675
763
  // #endregion
676
- // #region Styles
677
- styles = O;
678
- // #endregion
679
764
  }
680
765
  export {
681
- q as MasterDetailPlugin
766
+ w as MasterDetailPlugin
682
767
  };
683
768
  //# sourceMappingURL=index.js.map