@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,53 +1,54 @@
1
- function E(n) {
2
- n && n.querySelectorAll(".cell-focus").forEach((t) => t.classList.remove("cell-focus"));
1
+ function b(a) {
2
+ a && a.querySelectorAll(".cell-focus").forEach((e) => e.classList.remove("cell-focus"));
3
3
  }
4
- const R = 'input,select,textarea,[contenteditable="true"],[contenteditable=""],[tabindex]:not([tabindex="-1"])', y = document.createElement("template");
5
- y.innerHTML = '<div class="cell" role="gridcell" part="cell"></div>';
6
- const L = document.createElement("template");
7
- L.innerHTML = '<div class="data-grid-row" role="row" part="row"></div>';
8
- function A(n, t) {
9
- if (n._virtualization?.enabled) {
10
- const { rowHeight: a, container: o, viewportEl: l } = n._virtualization, s = o, d = l?.clientHeight ?? s?.clientHeight ?? 0;
11
- if (s && d > 0) {
12
- const f = n._focusRow * a;
13
- f < s.scrollTop ? s.scrollTop = f : f + a > s.scrollTop + d && (s.scrollTop = f - d + a);
4
+ const v = 'input,select,textarea,[contenteditable="true"],[contenteditable=""],[tabindex]:not([tabindex="-1"])', w = document.createElement("template");
5
+ w.innerHTML = '<div class="cell" role="gridcell" part="cell"></div>';
6
+ const C = document.createElement("template");
7
+ C.innerHTML = '<div class="data-grid-row" role="row" part="row"></div>';
8
+ function E(a, e) {
9
+ if (a._virtualization?.enabled) {
10
+ const { rowHeight: l, container: r, viewportEl: s } = a._virtualization, o = r, c = s?.clientHeight ?? o?.clientHeight ?? 0;
11
+ if (o && c > 0) {
12
+ const u = a._focusRow * l;
13
+ u < o.scrollTop ? o.scrollTop = u : u + l > o.scrollTop + c && (o.scrollTop = u - c + l);
14
14
  }
15
15
  }
16
- const e = n._activeEditRows !== void 0 && n._activeEditRows !== -1;
17
- e || n.refreshVirtualWindow(!1), E(n._bodyEl), Array.from(n._bodyEl.querySelectorAll('[aria-selected="true"]')).forEach((a) => {
18
- a.setAttribute("aria-selected", "false");
16
+ const t = a._activeEditRows !== void 0 && a._activeEditRows !== -1;
17
+ t || a.refreshVirtualWindow(!1), b(a._bodyEl), Array.from(a._bodyEl.querySelectorAll('[aria-selected="true"]')).forEach((l) => {
18
+ l.setAttribute("aria-selected", "false");
19
19
  });
20
- const i = n._focusRow, r = n._virtualization.start ?? 0, c = n._virtualization.end ?? n._rows.length;
21
- if (i >= r && i < c) {
22
- const a = n._bodyEl.querySelectorAll(".data-grid-row")[i - r], o = a?.children[n._focusCol];
23
- if (o) {
24
- o.classList.add("cell-focus"), o.setAttribute("aria-selected", "true");
25
- const l = n.shadowRoot?.querySelector(".tbw-scroll-area");
26
- if (l && o && !e) {
27
- const s = n._getHorizontalScrollOffsets?.(a ?? void 0, o) ?? { left: 0, right: 0 };
28
- if (!s.skipScroll) {
29
- const d = o.getBoundingClientRect(), f = l.getBoundingClientRect(), u = d.left - f.left + l.scrollLeft, g = u + d.width, h = l.scrollLeft + s.left, m = l.scrollLeft + l.clientWidth - s.right;
30
- u < h ? l.scrollLeft = u - s.left : g > m && (l.scrollLeft = g - l.clientWidth + s.right);
20
+ const i = a._focusRow, n = a._virtualization.start ?? 0, d = a._virtualization.end ?? a._rows.length;
21
+ if (i >= n && i < d) {
22
+ const l = a._bodyEl.querySelectorAll(".data-grid-row")[i - n];
23
+ let r = l?.children[a._focusCol];
24
+ if ((!r || !r.classList?.contains("cell")) && (r = l?.querySelector(`.cell[data-col="${a._focusCol}"]`) ?? l?.querySelector(".cell[data-col]")), r) {
25
+ r.classList.add("cell-focus"), r.setAttribute("aria-selected", "true");
26
+ const s = a.shadowRoot?.querySelector(".tbw-scroll-area");
27
+ if (s && r && !t) {
28
+ const o = a._getHorizontalScrollOffsets?.(l ?? void 0, r) ?? { left: 0, right: 0 };
29
+ if (!o.skipScroll) {
30
+ const c = r.getBoundingClientRect(), u = s.getBoundingClientRect(), f = c.left - u.left + s.scrollLeft, g = f + c.width, h = s.scrollLeft + o.left, m = s.scrollLeft + s.clientWidth - o.right;
31
+ f < h ? s.scrollLeft = f - o.left : g > m && (s.scrollLeft = g - s.clientWidth + o.right);
31
32
  }
32
33
  }
33
- if (n._activeEditRows !== void 0 && n._activeEditRows !== -1 && o.classList.contains("editing")) {
34
- const s = o.querySelector(R);
35
- if (s && document.activeElement !== s)
34
+ if (a._activeEditRows !== void 0 && a._activeEditRows !== -1 && r.classList.contains("editing")) {
35
+ const o = r.querySelector(v);
36
+ if (o && document.activeElement !== o)
36
37
  try {
37
- s.focus({ preventScroll: !0 });
38
+ o.focus({ preventScroll: !0 });
38
39
  } catch {
39
40
  }
40
- } else if (!o.contains(document.activeElement)) {
41
- o.hasAttribute("tabindex") || o.setAttribute("tabindex", "-1");
41
+ } else if (!r.contains(document.activeElement)) {
42
+ r.hasAttribute("tabindex") || r.setAttribute("tabindex", "-1");
42
43
  try {
43
- o.focus({ preventScroll: !0 });
44
+ r.focus({ preventScroll: !0 });
44
45
  } catch {
45
46
  }
46
47
  }
47
48
  }
48
49
  }
49
50
  }
50
- const O = {
51
+ const R = {
51
52
  expand: "▶",
52
53
  collapse: "▼",
53
54
  sortAsc: "▲",
@@ -56,13 +57,32 @@ const O = {
56
57
  submenuArrow: "▶",
57
58
  dragHandle: "⋮⋮",
58
59
  toolPanel: "☰"
59
- }, p = {
60
+ }, y = {
60
61
  /** Ask if a column can be moved. Context: ColumnConfig. Response: boolean | undefined */
61
62
  CAN_MOVE_COLUMN: "canMoveColumn"
62
63
  };
63
- class x {
64
- /** Plugin version - override in subclass if needed */
65
- version = "1.0.0";
64
+ class L {
65
+ /**
66
+ * Plugin dependencies - declare other plugins this one requires.
67
+ *
68
+ * Dependencies are validated when the plugin is attached.
69
+ * Required dependencies throw an error if missing.
70
+ * Optional dependencies log an info message if missing.
71
+ *
72
+ * @example
73
+ * ```typescript
74
+ * static readonly dependencies: PluginDependency[] = [
75
+ * { name: 'editing', required: true, reason: 'Tracks cell edits for undo/redo' },
76
+ * { name: 'selection', required: false, reason: 'Enables selection-based undo' },
77
+ * ];
78
+ * ```
79
+ */
80
+ static dependencies;
81
+ /**
82
+ * Plugin version - defaults to grid version for built-in plugins.
83
+ * Third-party plugins can override with their own semver.
84
+ */
85
+ version = typeof __GRID_VERSION__ < "u" ? __GRID_VERSION__ : "dev";
66
86
  /** CSS styles to inject into the grid's shadow DOM */
67
87
  styles;
68
88
  /** Custom cell renderers keyed by type name */
@@ -82,7 +102,7 @@ class x {
82
102
  * Created fresh in attach(), aborted in detach().
83
103
  * This ensures event listeners are properly cleaned up when plugins are re-attached.
84
104
  */
85
- #t;
105
+ #e;
86
106
  /**
87
107
  * Default configuration - subclasses should override this getter.
88
108
  * Note: This must be a getter (not property initializer) for proper inheritance
@@ -91,8 +111,8 @@ class x {
91
111
  get defaultConfig() {
92
112
  return {};
93
113
  }
94
- constructor(t = {}) {
95
- this.userConfig = t;
114
+ constructor(e = {}) {
115
+ this.userConfig = e;
96
116
  }
97
117
  /**
98
118
  * Called when the plugin is attached to a grid.
@@ -109,8 +129,8 @@ class x {
109
129
  * }
110
130
  * ```
111
131
  */
112
- attach(t) {
113
- this.#t?.abort(), this.#t = new AbortController(), this.grid = t, this.config = { ...this.defaultConfig, ...this.userConfig };
132
+ attach(e) {
133
+ this.#e?.abort(), this.#e = new AbortController(), this.grid = e, this.config = { ...this.defaultConfig, ...this.userConfig };
114
134
  }
115
135
  /**
116
136
  * Called when the plugin is detached from a grid.
@@ -126,7 +146,7 @@ class x {
126
146
  * ```
127
147
  */
128
148
  detach() {
129
- this.#t?.abort(), this.#t = void 0;
149
+ this.#e?.abort(), this.#e = void 0;
130
150
  }
131
151
  /**
132
152
  * Get another plugin instance from the same grid.
@@ -140,14 +160,22 @@ class x {
140
160
  * }
141
161
  * ```
142
162
  */
143
- getPlugin(t) {
144
- return this.grid?.getPlugin(t);
163
+ getPlugin(e) {
164
+ return this.grid?.getPlugin(e);
145
165
  }
146
166
  /**
147
167
  * Emit a custom event from the grid.
148
168
  */
149
- emit(t, e) {
150
- this.grid?.dispatchEvent?.(new CustomEvent(t, { detail: e, bubbles: !0 }));
169
+ emit(e, t) {
170
+ this.grid?.dispatchEvent?.(new CustomEvent(e, { detail: t, bubbles: !0 }));
171
+ }
172
+ /**
173
+ * Emit a cancelable custom event from the grid.
174
+ * @returns `true` if the event was cancelled (preventDefault called), `false` otherwise
175
+ */
176
+ emitCancelable(e, t) {
177
+ const i = new CustomEvent(e, { detail: t, bubbles: !0, cancelable: !0 });
178
+ return this.grid?.dispatchEvent?.(i), i.defaultPrevented;
151
179
  }
152
180
  /**
153
181
  * Request a re-render of the grid.
@@ -155,6 +183,14 @@ class x {
155
183
  requestRender() {
156
184
  this.grid?.requestRender?.();
157
185
  }
186
+ /**
187
+ * Request a re-render and restore focus styling afterward.
188
+ * Use this when a plugin action (like expand/collapse) triggers a render
189
+ * but needs to maintain keyboard navigation focus.
190
+ */
191
+ requestRenderWithFocus() {
192
+ this.grid?.requestRenderWithFocus?.();
193
+ }
158
194
  /**
159
195
  * Request a lightweight style update without rebuilding DOM.
160
196
  * Use this instead of requestRender() when only CSS classes need updating.
@@ -188,6 +224,19 @@ class x {
188
224
  get visibleColumns() {
189
225
  return this.grid?._visibleColumns ?? [];
190
226
  }
227
+ /**
228
+ * Get the grid as an HTMLElement for direct DOM operations.
229
+ * Use sparingly - prefer the typed GridElementRef API when possible.
230
+ *
231
+ * @example
232
+ * ```ts
233
+ * const width = this.gridElement.clientWidth;
234
+ * this.gridElement.classList.add('my-plugin-active');
235
+ * ```
236
+ */
237
+ get gridElement() {
238
+ return this.grid;
239
+ }
191
240
  /**
192
241
  * Get the shadow root of the grid.
193
242
  */
@@ -212,16 +261,61 @@ class x {
212
261
  * document.addEventListener('keydown', handler, { signal: this.disconnectSignal });
213
262
  */
214
263
  get disconnectSignal() {
215
- return this.#t?.signal ?? this.grid?.disconnectSignal;
264
+ return this.#e?.signal ?? this.grid?.disconnectSignal;
216
265
  }
217
266
  /**
218
267
  * Get the grid-level icons configuration.
219
268
  * Returns merged icons (user config + defaults).
220
269
  */
221
270
  get gridIcons() {
222
- const t = this.grid?.gridConfig?.icons ?? {};
223
- return { ...O, ...t };
271
+ const e = this.grid?.gridConfig?.icons ?? {};
272
+ return { ...R, ...e };
273
+ }
274
+ // #region Animation Helpers
275
+ /**
276
+ * Check if animations are enabled at the grid level.
277
+ * Respects gridConfig.animation.mode and the CSS variable set by the grid.
278
+ *
279
+ * Plugins should use this to skip animations when:
280
+ * - Animation mode is 'off' or `false`
281
+ * - User prefers reduced motion and mode is 'reduced-motion' (default)
282
+ *
283
+ * @example
284
+ * ```ts
285
+ * private get animationStyle(): 'slide' | 'fade' | false {
286
+ * if (!this.isAnimationEnabled) return false;
287
+ * return this.config.animation ?? 'slide';
288
+ * }
289
+ * ```
290
+ */
291
+ get isAnimationEnabled() {
292
+ const e = this.grid?.effectiveConfig?.animation?.mode ?? "reduced-motion";
293
+ if (e === !1 || e === "off") return !1;
294
+ if (e === !0 || e === "on") return !0;
295
+ const t = this.shadowRoot?.host;
296
+ return t ? getComputedStyle(t).getPropertyValue("--tbw-animation-enabled").trim() !== "0" : !0;
224
297
  }
298
+ /**
299
+ * Get the animation duration in milliseconds from CSS variable.
300
+ * Falls back to 200ms if not set.
301
+ *
302
+ * Plugins can use this for their animation timing to stay consistent
303
+ * with the grid-level animation.duration setting.
304
+ *
305
+ * @example
306
+ * ```ts
307
+ * element.animate(keyframes, { duration: this.animationDuration });
308
+ * ```
309
+ */
310
+ get animationDuration() {
311
+ const e = this.shadowRoot?.host;
312
+ if (e) {
313
+ const t = getComputedStyle(e).getPropertyValue("--tbw-animation-duration").trim(), i = parseInt(t, 10);
314
+ if (!isNaN(i)) return i;
315
+ }
316
+ return 200;
317
+ }
318
+ // #endregion
225
319
  /**
226
320
  * Resolve an icon value to string or HTMLElement.
227
321
  * Checks plugin config first, then grid-level icons, then defaults.
@@ -230,8 +324,8 @@ class x {
230
324
  * @param pluginOverride - Optional plugin-level override
231
325
  * @returns The resolved icon value
232
326
  */
233
- resolveIcon(t, e) {
234
- return e !== void 0 ? e : this.gridIcons[t];
327
+ resolveIcon(e, t) {
328
+ return t !== void 0 ? t : this.gridIcons[e];
235
329
  }
236
330
  /**
237
331
  * Set an icon value on an element.
@@ -240,30 +334,30 @@ class x {
240
334
  * @param element - The element to set the icon on
241
335
  * @param icon - The icon value (string or HTMLElement)
242
336
  */
243
- setIcon(t, e) {
244
- typeof e == "string" ? t.innerHTML = e : e instanceof HTMLElement && (t.innerHTML = "", t.appendChild(e.cloneNode(!0)));
337
+ setIcon(e, t) {
338
+ typeof t == "string" ? e.innerHTML = t : t instanceof HTMLElement && (e.innerHTML = "", e.appendChild(t.cloneNode(!0)));
245
339
  }
246
340
  /**
247
341
  * Log a warning message.
248
342
  */
249
- warn(t) {
250
- console.warn(`[tbw-grid:${this.name}] ${t}`);
343
+ warn(e) {
344
+ console.warn(`[tbw-grid:${this.name}] ${e}`);
251
345
  }
252
346
  // #endregion
253
347
  }
254
- function b(n) {
255
- const t = n.meta ?? {};
256
- return t.lockPosition !== !0 && t.suppressMovable !== !0;
348
+ function O(a) {
349
+ const e = a.meta ?? {};
350
+ return e.lockPosition !== !0 && e.suppressMovable !== !0;
257
351
  }
258
- function v(n, t, e) {
259
- if (t === e || t < 0 || t >= n.length || e < 0 || e > n.length) return n;
260
- const i = [...n], [r] = i.splice(t, 1);
261
- return i.splice(e, 0, r), i;
352
+ function p(a, e, t) {
353
+ if (e === t || e < 0 || e >= a.length || t < 0 || t > a.length) return a;
354
+ const i = [...a], [n] = i.splice(e, 1);
355
+ return i.splice(t, 0, n), i;
262
356
  }
263
- const _ = '.header-row>.cell[draggable=true]{cursor:grab;position:relative}.header-row>.cell.dragging{opacity:.5;cursor:grabbing}.header-row>.cell.drop-before:before{content:"";position:absolute;left:0;top:0;bottom:0;width:2px;background:var(--tbw-reorder-indicator, var(--tbw-color-accent));z-index:1}.header-row>.cell.drop-after:after{content:"";position:absolute;right:0;top:0;bottom:0;width:2px;background:var(--tbw-reorder-indicator, var(--tbw-color-accent));z-index:1}.cell.flip-animating{transition:transform var(--tbw-animation-duration, .2s) ease-out;will-change:transform;z-index:1}@keyframes reorder-fade-in{0%{opacity:0}to{opacity:1}}.cell.fade-animating{animation:reorder-fade-in var(--tbw-animation-duration, .2s) ease-out backwards}';
264
- class I extends x {
357
+ const A = '.header-row>.cell[draggable=true]{cursor:grab;position:relative}.header-row>.cell.dragging{opacity:.5;cursor:grabbing}.header-row>.cell.drop-before:before{content:"";position:absolute;left:0;top:0;bottom:0;width:2px;background:var(--tbw-reorder-indicator, var(--tbw-color-accent));z-index:1}.header-row>.cell.drop-after:after{content:"";position:absolute;right:0;top:0;bottom:0;width:2px;background:var(--tbw-reorder-indicator, var(--tbw-color-accent));z-index:1}.cell.flip-animating{transition:transform var(--tbw-animation-duration, .2s) ease-out;will-change:transform;z-index:1}@keyframes reorder-fade-in{0%{opacity:0}to{opacity:1}}.cell.fade-animating{animation:reorder-fade-in var(--tbw-animation-duration, .2s) ease-out backwards}';
358
+ class _ extends L {
265
359
  name = "reorder";
266
- version = "1.0.0";
360
+ styles = A;
267
361
  get defaultConfig() {
268
362
  return {
269
363
  animation: "flip"
@@ -272,47 +366,47 @@ class I extends x {
272
366
  }
273
367
  /**
274
368
  * Resolve animation type from plugin config.
275
- * Respects grid-level animation.mode (disabled = no animation).
369
+ * Uses base class isAnimationEnabled to respect grid-level settings.
276
370
  */
277
371
  get animationType() {
278
372
  return this.isAnimationEnabled ? this.config.animation !== void 0 ? this.config.animation : this.config.viewTransition === !1 ? !1 : (this.config.viewTransition === !0, "flip") : !1;
279
373
  }
280
374
  /**
281
- * Check if animations are enabled at the grid level.
282
- * Respects gridConfig.animation.mode and CSS variable.
283
- */
284
- get isAnimationEnabled() {
285
- const e = this.grid.effectiveConfig?.animation?.mode ?? "reduced-motion";
286
- if (e === !1 || e === "off") return !1;
287
- if (e === !0 || e === "on") return !0;
288
- const i = this.shadowRoot?.host;
289
- return i ? getComputedStyle(i).getPropertyValue("--tbw-animation-enabled").trim() !== "0" : !0;
290
- }
291
- /**
292
- * Get animation duration from CSS variable (set by grid config).
375
+ * Get animation duration, allowing plugin config override.
376
+ * Uses base class animationDuration for default.
293
377
  */
294
378
  get animationDuration() {
295
- if (this.config.animationDuration !== void 0)
296
- return this.config.animationDuration;
297
- const t = this.shadowRoot?.host;
298
- if (t) {
299
- const e = getComputedStyle(t).getPropertyValue("--tbw-animation-duration").trim(), i = parseInt(e, 10);
300
- if (!isNaN(i)) return i;
301
- }
302
- return 200;
379
+ return this.config.animationDuration !== void 0 ? this.config.animationDuration : super.animationDuration;
303
380
  }
304
381
  // #region Internal State
305
382
  isDragging = !1;
306
383
  draggedField = null;
307
384
  draggedIndex = null;
308
385
  dropIndex = null;
386
+ /**
387
+ * Check if a column can be moved, considering both column config and plugin queries.
388
+ */
389
+ canMoveColumnWithPlugins(e) {
390
+ return !e || !O(e) ? !1 : !this.grid.queryPlugins({
391
+ type: y.CAN_MOVE_COLUMN,
392
+ context: e
393
+ }).includes(!1);
394
+ }
395
+ /**
396
+ * Clear all drag-related classes from header cells.
397
+ */
398
+ clearDragClasses() {
399
+ this.shadowRoot?.querySelectorAll(".header-row > .cell").forEach((e) => {
400
+ e.classList.remove("dragging", "drop-target", "drop-before", "drop-after");
401
+ });
402
+ }
309
403
  // #endregion
310
404
  // #region Lifecycle
311
- attach(t) {
312
- super.attach(t), t.addEventListener(
405
+ attach(e) {
406
+ super.attach(e), e.addEventListener(
313
407
  "column-reorder-request",
314
- (e) => {
315
- const i = e.detail;
408
+ (t) => {
409
+ const i = t.detail;
316
410
  i?.field && typeof i.toIndex == "number" && this.moveColumn(i.field, i.toIndex);
317
411
  },
318
412
  { signal: this.disconnectSignal }
@@ -324,72 +418,59 @@ class I extends x {
324
418
  // #endregion
325
419
  // #region Hooks
326
420
  afterRender() {
327
- const t = this.shadowRoot;
328
- if (!t) return;
329
- t.querySelectorAll(".header-row > .cell").forEach((i) => {
330
- const r = i, c = r.getAttribute("data-field");
331
- if (!c) return;
332
- const a = this.columns.find((d) => d.field === c), s = !this.grid.queryPlugins({
333
- type: p.CAN_MOVE_COLUMN,
334
- context: a
335
- }).includes(!1);
336
- if (!a || !b(a) || !s) {
337
- r.draggable = !1;
421
+ const e = this.shadowRoot;
422
+ if (!e) return;
423
+ e.querySelectorAll(".header-row > .cell").forEach((i) => {
424
+ const n = i, d = n.getAttribute("data-field");
425
+ if (!d) return;
426
+ const l = this.columns.find((r) => r.field === d);
427
+ if (!this.canMoveColumnWithPlugins(l)) {
428
+ n.draggable = !1;
338
429
  return;
339
430
  }
340
- r.draggable = !0, !r.getAttribute("data-dragstart-bound") && (r.setAttribute("data-dragstart-bound", "true"), r.addEventListener("dragstart", (d) => {
341
- const u = this.getColumnOrder().indexOf(c);
342
- this.isDragging = !0, this.draggedField = c, this.draggedIndex = u, d.dataTransfer && (d.dataTransfer.effectAllowed = "move", d.dataTransfer.setData("text/plain", c)), r.classList.add("dragging");
343
- }), r.addEventListener("dragend", () => {
344
- this.isDragging = !1, this.draggedField = null, this.draggedIndex = null, this.dropIndex = null, t.querySelectorAll(".header-row > .cell").forEach((d) => {
345
- d.classList.remove("dragging", "drop-target", "drop-before", "drop-after");
346
- });
347
- }), r.addEventListener("dragover", (d) => {
348
- if (d.preventDefault(), !this.isDragging || this.draggedField === c) return;
349
- const f = r.getBoundingClientRect(), u = f.left + f.width / 2, h = this.getColumnOrder().indexOf(c);
350
- this.dropIndex = d.clientX < u ? h : h + 1, r.classList.add("drop-target"), r.classList.toggle("drop-before", d.clientX < u), r.classList.toggle("drop-after", d.clientX >= u);
351
- }), r.addEventListener("dragleave", () => {
352
- r.classList.remove("drop-target", "drop-before", "drop-after");
353
- }), r.addEventListener("drop", (d) => {
354
- d.preventDefault();
355
- const f = this.draggedField, u = this.draggedIndex, g = this.dropIndex;
356
- if (!this.isDragging || f === null || u === null || g === null)
431
+ n.draggable = !0, !n.getAttribute("data-dragstart-bound") && (n.setAttribute("data-dragstart-bound", "true"), n.addEventListener("dragstart", (r) => {
432
+ const o = this.getColumnOrder().indexOf(d);
433
+ this.isDragging = !0, this.draggedField = d, this.draggedIndex = o, r.dataTransfer && (r.dataTransfer.effectAllowed = "move", r.dataTransfer.setData("text/plain", d)), n.classList.add("dragging");
434
+ }), n.addEventListener("dragend", () => {
435
+ this.isDragging = !1, this.draggedField = null, this.draggedIndex = null, this.dropIndex = null, this.clearDragClasses();
436
+ }), n.addEventListener("dragover", (r) => {
437
+ if (r.preventDefault(), !this.isDragging || this.draggedField === d) return;
438
+ const s = n.getBoundingClientRect(), o = s.left + s.width / 2, u = this.getColumnOrder().indexOf(d);
439
+ this.dropIndex = r.clientX < o ? u : u + 1, n.classList.add("drop-target"), n.classList.toggle("drop-before", r.clientX < o), n.classList.toggle("drop-after", r.clientX >= o);
440
+ }), n.addEventListener("dragleave", () => {
441
+ n.classList.remove("drop-target", "drop-before", "drop-after");
442
+ }), n.addEventListener("drop", (r) => {
443
+ r.preventDefault();
444
+ const s = this.draggedField, o = this.draggedIndex, c = this.dropIndex;
445
+ if (!this.isDragging || s === null || o === null || c === null)
357
446
  return;
358
- const h = g > u ? g - 1 : g, m = this.getColumnOrder(), w = v(m, u, h), C = {
359
- field: f,
360
- fromIndex: u,
361
- toIndex: h,
362
- columnOrder: w
447
+ const u = c > o ? c - 1 : c, f = this.getColumnOrder(), g = p(f, o, u), h = {
448
+ field: s,
449
+ fromIndex: o,
450
+ toIndex: u,
451
+ columnOrder: g
363
452
  };
364
- this.updateColumnOrder(w), this.emit("column-move", C);
453
+ this.updateColumnOrder(g), this.emitCancelable("column-move", h) && this.updateColumnOrder(f);
365
454
  }));
366
455
  });
367
456
  }
368
457
  /**
369
458
  * Handle Alt+Arrow keyboard shortcuts for column reordering.
370
459
  */
371
- onKeyDown(t) {
372
- if (!t.altKey || t.key !== "ArrowLeft" && t.key !== "ArrowRight")
460
+ onKeyDown(e) {
461
+ if (!e.altKey || e.key !== "ArrowLeft" && e.key !== "ArrowRight")
373
462
  return;
374
- const e = this.grid, i = e._focusCol, r = e._visibleColumns;
375
- if (i < 0 || i >= r.length) return;
376
- const c = r[i];
377
- if (!c || !b(c)) return;
378
- const a = this.grid;
379
- if (a.queryPlugins({
380
- type: p.CAN_MOVE_COLUMN,
381
- context: c
382
- }).includes(!1)) return;
383
- const l = this.getColumnOrder(), s = l.indexOf(c.field);
384
- if (s === -1) return;
385
- const d = t.key === "ArrowLeft" ? s - 1 : s + 1;
386
- if (d < 0 || d >= l.length) return;
387
- const f = r.find((u) => u.field === l[d]);
388
- if (!(f && a.queryPlugins({
389
- type: p.CAN_MOVE_COLUMN,
390
- context: f
391
- }).includes(!1)))
392
- return this.moveColumn(c.field, d), e._focusCol = d, A(this.grid), t.preventDefault(), t.stopPropagation(), !0;
463
+ const t = this.grid, i = t._focusCol, n = t._visibleColumns;
464
+ if (i < 0 || i >= n.length) return;
465
+ const d = n[i];
466
+ if (!this.canMoveColumnWithPlugins(d)) return;
467
+ const l = this.getColumnOrder(), r = l.indexOf(d.field);
468
+ if (r === -1) return;
469
+ const s = e.key === "ArrowLeft" ? r - 1 : r + 1;
470
+ if (s < 0 || s >= l.length) return;
471
+ const o = n.find((c) => c.field === l[s]);
472
+ if (this.canMoveColumnWithPlugins(o))
473
+ return this.moveColumn(d.field, s), t._focusCol = s, E(this.grid), e.preventDefault(), e.stopPropagation(), !0;
393
474
  }
394
475
  // #endregion
395
476
  // #region Public API
@@ -405,30 +486,30 @@ class I extends x {
405
486
  * @param field - The field name of the column to move
406
487
  * @param toIndex - The target index
407
488
  */
408
- moveColumn(t, e) {
409
- const i = this.getColumnOrder(), r = i.indexOf(t);
410
- if (r === -1) return;
411
- const c = v(i, r, e);
412
- this.updateColumnOrder(c), this.emit("column-move", {
413
- field: t,
414
- fromIndex: r,
415
- toIndex: e,
416
- columnOrder: c
417
- });
489
+ moveColumn(e, t) {
490
+ const i = this.getColumnOrder(), n = i.indexOf(e);
491
+ if (n === -1) return;
492
+ const d = p(i, n, t);
493
+ this.updateColumnOrder(d), this.emitCancelable("column-move", {
494
+ field: e,
495
+ fromIndex: n,
496
+ toIndex: t,
497
+ columnOrder: d
498
+ }) && this.updateColumnOrder(i);
418
499
  }
419
500
  /**
420
501
  * Set a specific column order.
421
502
  * @param order - Array of field names in desired order
422
503
  */
423
- setColumnOrder(t) {
424
- this.updateColumnOrder(t);
504
+ setColumnOrder(e) {
505
+ this.updateColumnOrder(e);
425
506
  }
426
507
  /**
427
508
  * Reset column order to the original configuration order.
428
509
  */
429
510
  resetColumnOrder() {
430
- const t = this.columns.map((e) => e.field);
431
- this.updateColumnOrder(t);
511
+ const e = this.columns.map((t) => t.field);
512
+ this.updateColumnOrder(e);
432
513
  }
433
514
  // #endregion
434
515
  // #region View Transition
@@ -436,102 +517,99 @@ class I extends x {
436
517
  * Capture header cell positions before reorder.
437
518
  */
438
519
  captureHeaderPositions() {
439
- const t = /* @__PURE__ */ new Map();
440
- return this.shadowRoot?.querySelectorAll(".header-row > .cell[data-field]").forEach((e) => {
441
- const i = e.getAttribute("data-field");
442
- i && t.set(i, e.getBoundingClientRect().left);
443
- }), t;
520
+ const e = /* @__PURE__ */ new Map();
521
+ return this.shadowRoot?.querySelectorAll(".header-row > .cell[data-field]").forEach((t) => {
522
+ const i = t.getAttribute("data-field");
523
+ i && e.set(i, t.getBoundingClientRect().left);
524
+ }), e;
444
525
  }
445
526
  /**
446
527
  * Apply FLIP animation for column reorder.
447
528
  * Uses CSS transitions - JS sets initial transform and toggles class.
448
529
  * @param oldPositions - Header positions captured before DOM change
449
530
  */
450
- animateFLIP(t) {
451
- const e = this.shadowRoot;
452
- if (!e || t.size === 0) return;
531
+ animateFLIP(e) {
532
+ const t = this.shadowRoot;
533
+ if (!t || e.size === 0) return;
453
534
  const i = /* @__PURE__ */ new Map();
454
- if (e.querySelectorAll(".header-row > .cell[data-field]").forEach((a) => {
455
- const o = a.getAttribute("data-field");
456
- if (!o) return;
457
- const l = t.get(o);
458
- if (l === void 0) return;
459
- const s = l - a.getBoundingClientRect().left;
460
- Math.abs(s) > 1 && i.set(o, s);
535
+ if (t.querySelectorAll(".header-row > .cell[data-field]").forEach((l) => {
536
+ const r = l.getAttribute("data-field");
537
+ if (!r) return;
538
+ const s = e.get(r);
539
+ if (s === void 0) return;
540
+ const o = s - l.getBoundingClientRect().left;
541
+ Math.abs(o) > 1 && i.set(r, o);
461
542
  }), i.size === 0) return;
462
- const r = [];
463
- if (e.querySelectorAll(".cell[data-field]").forEach((a) => {
464
- const o = i.get(a.getAttribute("data-field") ?? "");
465
- if (o !== void 0) {
466
- const l = a;
467
- l.style.transform = `translateX(${o}px)`, r.push(l);
543
+ const n = [];
544
+ if (t.querySelectorAll(".cell[data-field]").forEach((l) => {
545
+ const r = i.get(l.getAttribute("data-field") ?? "");
546
+ if (r !== void 0) {
547
+ const s = l;
548
+ s.style.transform = `translateX(${r}px)`, n.push(s);
468
549
  }
469
- }), r.length === 0) return;
470
- e.host.offsetHeight;
471
- const c = this.animationDuration;
550
+ }), n.length === 0) return;
551
+ t.host.offsetHeight;
552
+ const d = this.animationDuration;
472
553
  requestAnimationFrame(() => {
473
- r.forEach((a) => {
474
- a.classList.add("flip-animating"), a.style.transform = "";
554
+ n.forEach((l) => {
555
+ l.classList.add("flip-animating"), l.style.transform = "";
475
556
  }), setTimeout(() => {
476
- r.forEach((a) => {
477
- a.style.transform = "", a.classList.remove("flip-animating");
557
+ n.forEach((l) => {
558
+ l.style.transform = "", l.classList.remove("flip-animating");
478
559
  });
479
- }, c + 50);
560
+ }, d + 50);
480
561
  });
481
562
  }
482
563
  /**
483
564
  * Apply crossfade animation for moved columns.
484
565
  * Uses CSS keyframes - JS just toggles classes.
485
566
  */
486
- animateFade(t) {
487
- const e = this.shadowRoot;
488
- if (!e) {
489
- t();
567
+ animateFade(e) {
568
+ const t = this.shadowRoot;
569
+ if (!t) {
570
+ e();
490
571
  return;
491
572
  }
492
573
  const i = this.captureHeaderPositions();
493
- t();
494
- const r = /* @__PURE__ */ new Set();
495
- if (e.querySelectorAll(".header-row > .cell[data-field]").forEach((o) => {
496
- const l = o.getAttribute("data-field");
497
- if (!l) return;
498
- const s = i.get(l);
499
- if (s === void 0) return;
500
- const d = o.getBoundingClientRect().left;
501
- Math.abs(s - d) > 1 && r.add(l);
502
- }), r.size === 0) return;
503
- const c = [];
504
- if (e.querySelectorAll(".cell[data-field]").forEach((o) => {
505
- const l = o.getAttribute("data-field");
506
- if (l && r.has(l)) {
507
- const s = o;
508
- s.classList.add("fade-animating"), c.push(s);
574
+ e();
575
+ const n = /* @__PURE__ */ new Set();
576
+ if (t.querySelectorAll(".header-row > .cell[data-field]").forEach((r) => {
577
+ const s = r.getAttribute("data-field");
578
+ if (!s) return;
579
+ const o = i.get(s);
580
+ if (o === void 0) return;
581
+ const c = r.getBoundingClientRect().left;
582
+ Math.abs(o - c) > 1 && n.add(s);
583
+ }), n.size === 0) return;
584
+ const d = [];
585
+ if (t.querySelectorAll(".cell[data-field]").forEach((r) => {
586
+ const s = r.getAttribute("data-field");
587
+ if (s && n.has(s)) {
588
+ const o = r;
589
+ o.classList.add("fade-animating"), d.push(o);
509
590
  }
510
- }), c.length === 0) return;
511
- const a = this.animationDuration;
591
+ }), d.length === 0) return;
592
+ const l = this.animationDuration;
512
593
  setTimeout(() => {
513
- c.forEach((o) => o.classList.remove("fade-animating"));
514
- }, a + 50);
594
+ d.forEach((r) => r.classList.remove("fade-animating"));
595
+ }, l + 50);
515
596
  }
516
597
  /**
517
598
  * Update column order with configured animation.
518
599
  */
519
- updateColumnOrder(t) {
520
- const e = this.grid, i = this.animationType;
521
- if (i === "flip" && this.shadowRoot) {
522
- const r = this.captureHeaderPositions();
523
- e.setColumnOrder(t), requestAnimationFrame(() => {
524
- this.shadowRoot?.host?.offsetHeight, this.animateFLIP(r);
600
+ updateColumnOrder(e) {
601
+ const t = this.animationType;
602
+ if (t === "flip" && this.shadowRoot) {
603
+ const i = this.captureHeaderPositions();
604
+ this.grid.setColumnOrder(e), requestAnimationFrame(() => {
605
+ this.shadowRoot?.host?.offsetHeight, this.animateFLIP(i);
525
606
  });
526
- } else i === "fade" ? this.animateFade(() => e.setColumnOrder(t)) : e.setColumnOrder(t);
527
- e.requestStateChange?.();
607
+ } else t === "fade" ? this.animateFade(() => this.grid.setColumnOrder(e)) : this.grid.setColumnOrder(e);
608
+ this.grid.requestStateChange?.();
528
609
  }
529
610
  // #endregion
530
- // #region Styles
531
- styles = _;
532
- // #endregion
533
611
  }
534
612
  export {
535
- I as ReorderPlugin
613
+ _ as ReorderPlugin
536
614
  };
537
615
  //# sourceMappingURL=index.js.map