@toolbox-web/grid 0.4.1 → 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 (157) hide show
  1. package/README.md +10 -13
  2. package/all.js +1101 -1048
  3. package/all.js.map +1 -1
  4. package/index.js +245 -137
  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/plugin/base-plugin.d.ts +57 -1
  14. package/lib/core/plugin/base-plugin.d.ts.map +1 -1
  15. package/lib/core/plugin/expander-column.d.ts +51 -0
  16. package/lib/core/plugin/expander-column.d.ts.map +1 -0
  17. package/lib/core/plugin/types.d.ts +117 -1
  18. package/lib/core/plugin/types.d.ts.map +1 -1
  19. package/lib/core/types.d.ts +4 -2
  20. package/lib/core/types.d.ts.map +1 -1
  21. package/lib/plugins/clipboard/ClipboardPlugin.d.ts +5 -4
  22. package/lib/plugins/clipboard/ClipboardPlugin.d.ts.map +1 -1
  23. package/lib/plugins/clipboard/index.d.ts +1 -1
  24. package/lib/plugins/clipboard/index.d.ts.map +1 -1
  25. package/lib/plugins/clipboard/index.js +282 -188
  26. package/lib/plugins/clipboard/index.js.map +1 -1
  27. package/lib/plugins/clipboard/types.d.ts +72 -2
  28. package/lib/plugins/clipboard/types.d.ts.map +1 -1
  29. package/lib/plugins/column-virtualization/ColumnVirtualizationPlugin.d.ts +0 -1
  30. package/lib/plugins/column-virtualization/ColumnVirtualizationPlugin.d.ts.map +1 -1
  31. package/lib/plugins/column-virtualization/index.js +102 -26
  32. package/lib/plugins/column-virtualization/index.js.map +1 -1
  33. package/lib/plugins/context-menu/ContextMenuPlugin.d.ts +0 -1
  34. package/lib/plugins/context-menu/ContextMenuPlugin.d.ts.map +1 -1
  35. package/lib/plugins/context-menu/index.js +154 -78
  36. package/lib/plugins/context-menu/index.js.map +1 -1
  37. package/lib/plugins/editing/EditingPlugin.d.ts +1 -7
  38. package/lib/plugins/editing/EditingPlugin.d.ts.map +1 -1
  39. package/lib/plugins/editing/index.js +200 -136
  40. package/lib/plugins/editing/index.js.map +1 -1
  41. package/lib/plugins/export/ExportPlugin.d.ts +0 -1
  42. package/lib/plugins/export/ExportPlugin.d.ts.map +1 -1
  43. package/lib/plugins/export/index.js +175 -99
  44. package/lib/plugins/export/index.js.map +1 -1
  45. package/lib/plugins/filtering/FilteringPlugin.d.ts +5 -2
  46. package/lib/plugins/filtering/FilteringPlugin.d.ts.map +1 -1
  47. package/lib/plugins/filtering/index.js +129 -43
  48. package/lib/plugins/filtering/index.js.map +1 -1
  49. package/lib/plugins/grouping-columns/GroupingColumnsPlugin.d.ts +1 -2
  50. package/lib/plugins/grouping-columns/GroupingColumnsPlugin.d.ts.map +1 -1
  51. package/lib/plugins/grouping-columns/grouping-columns.d.ts +1 -1
  52. package/lib/plugins/grouping-columns/grouping-columns.d.ts.map +1 -1
  53. package/lib/plugins/grouping-columns/index.js +144 -66
  54. package/lib/plugins/grouping-columns/index.js.map +1 -1
  55. package/lib/plugins/grouping-rows/GroupingRowsPlugin.d.ts +14 -2
  56. package/lib/plugins/grouping-rows/GroupingRowsPlugin.d.ts.map +1 -1
  57. package/lib/plugins/grouping-rows/index.js +230 -138
  58. package/lib/plugins/grouping-rows/index.js.map +1 -1
  59. package/lib/plugins/master-detail/MasterDetailPlugin.d.ts +13 -11
  60. package/lib/plugins/master-detail/MasterDetailPlugin.d.ts.map +1 -1
  61. package/lib/plugins/master-detail/index.js +265 -196
  62. package/lib/plugins/master-detail/index.js.map +1 -1
  63. package/lib/plugins/master-detail/types.d.ts +0 -10
  64. package/lib/plugins/master-detail/types.d.ts.map +1 -1
  65. package/lib/plugins/multi-sort/MultiSortPlugin.d.ts +1 -2
  66. package/lib/plugins/multi-sort/MultiSortPlugin.d.ts.map +1 -1
  67. package/lib/plugins/multi-sort/index.js +105 -31
  68. package/lib/plugins/multi-sort/index.js.map +1 -1
  69. package/lib/plugins/pinned-columns/PinnedColumnsPlugin.d.ts +0 -1
  70. package/lib/plugins/pinned-columns/PinnedColumnsPlugin.d.ts.map +1 -1
  71. package/lib/plugins/pinned-columns/index.js +128 -52
  72. package/lib/plugins/pinned-columns/index.js.map +1 -1
  73. package/lib/plugins/pinned-rows/PinnedRowsPlugin.d.ts +1 -2
  74. package/lib/plugins/pinned-rows/PinnedRowsPlugin.d.ts.map +1 -1
  75. package/lib/plugins/pinned-rows/index.js +162 -88
  76. package/lib/plugins/pinned-rows/index.js.map +1 -1
  77. package/lib/plugins/pivot/PivotPlugin.d.ts +26 -4
  78. package/lib/plugins/pivot/PivotPlugin.d.ts.map +1 -1
  79. package/lib/plugins/pivot/index.js +398 -310
  80. package/lib/plugins/pivot/index.js.map +1 -1
  81. package/lib/plugins/pivot/pivot-rows.d.ts +2 -1
  82. package/lib/plugins/pivot/pivot-rows.d.ts.map +1 -1
  83. package/lib/plugins/reorder/ReorderPlugin.d.ts +13 -10
  84. package/lib/plugins/reorder/ReorderPlugin.d.ts.map +1 -1
  85. package/lib/plugins/reorder/index.js +288 -226
  86. package/lib/plugins/reorder/index.js.map +1 -1
  87. package/lib/plugins/selection/SelectionPlugin.d.ts +21 -3
  88. package/lib/plugins/selection/SelectionPlugin.d.ts.map +1 -1
  89. package/lib/plugins/selection/index.d.ts +2 -2
  90. package/lib/plugins/selection/index.d.ts.map +1 -1
  91. package/lib/plugins/selection/index.js +276 -145
  92. package/lib/plugins/selection/index.js.map +1 -1
  93. package/lib/plugins/selection/types.d.ts +24 -0
  94. package/lib/plugins/selection/types.d.ts.map +1 -1
  95. package/lib/plugins/server-side/ServerSidePlugin.d.ts +0 -1
  96. package/lib/plugins/server-side/ServerSidePlugin.d.ts.map +1 -1
  97. package/lib/plugins/server-side/index.js +83 -7
  98. package/lib/plugins/server-side/index.js.map +1 -1
  99. package/lib/plugins/tree/TreePlugin.d.ts +5 -1
  100. package/lib/plugins/tree/TreePlugin.d.ts.map +1 -1
  101. package/lib/plugins/tree/index.js +197 -112
  102. package/lib/plugins/tree/index.js.map +1 -1
  103. package/lib/plugins/tree/types.d.ts +0 -10
  104. package/lib/plugins/tree/types.d.ts.map +1 -1
  105. package/lib/plugins/undo-redo/UndoRedoPlugin.d.ts +0 -1
  106. package/lib/plugins/undo-redo/UndoRedoPlugin.d.ts.map +1 -1
  107. package/lib/plugins/undo-redo/index.js +93 -17
  108. package/lib/plugins/undo-redo/index.js.map +1 -1
  109. package/lib/plugins/visibility/VisibilityPlugin.d.ts +7 -4
  110. package/lib/plugins/visibility/VisibilityPlugin.d.ts.map +1 -1
  111. package/lib/plugins/visibility/index.js +144 -65
  112. package/lib/plugins/visibility/index.js.map +1 -1
  113. package/package.json +1 -1
  114. package/umd/grid.all.umd.js +17 -19
  115. package/umd/grid.all.umd.js.map +1 -1
  116. package/umd/grid.umd.js +7 -7
  117. package/umd/grid.umd.js.map +1 -1
  118. package/umd/plugins/clipboard.umd.js +5 -7
  119. package/umd/plugins/clipboard.umd.js.map +1 -1
  120. package/umd/plugins/column-virtualization.umd.js +1 -1
  121. package/umd/plugins/column-virtualization.umd.js.map +1 -1
  122. package/umd/plugins/context-menu.umd.js +1 -1
  123. package/umd/plugins/context-menu.umd.js.map +1 -1
  124. package/umd/plugins/editing.umd.js +1 -1
  125. package/umd/plugins/editing.umd.js.map +1 -1
  126. package/umd/plugins/export.umd.js +1 -1
  127. package/umd/plugins/export.umd.js.map +1 -1
  128. package/umd/plugins/filtering.umd.js +1 -1
  129. package/umd/plugins/filtering.umd.js.map +1 -1
  130. package/umd/plugins/grouping-columns.umd.js +1 -1
  131. package/umd/plugins/grouping-columns.umd.js.map +1 -1
  132. package/umd/plugins/grouping-rows.umd.js +1 -1
  133. package/umd/plugins/grouping-rows.umd.js.map +1 -1
  134. package/umd/plugins/master-detail.umd.js +1 -1
  135. package/umd/plugins/master-detail.umd.js.map +1 -1
  136. package/umd/plugins/multi-sort.umd.js +1 -1
  137. package/umd/plugins/multi-sort.umd.js.map +1 -1
  138. package/umd/plugins/pinned-columns.umd.js +1 -1
  139. package/umd/plugins/pinned-columns.umd.js.map +1 -1
  140. package/umd/plugins/pinned-rows.umd.js +1 -1
  141. package/umd/plugins/pinned-rows.umd.js.map +1 -1
  142. package/umd/plugins/pivot.umd.js +1 -1
  143. package/umd/plugins/pivot.umd.js.map +1 -1
  144. package/umd/plugins/reorder.umd.js +1 -1
  145. package/umd/plugins/reorder.umd.js.map +1 -1
  146. package/umd/plugins/selection.umd.js +1 -1
  147. package/umd/plugins/selection.umd.js.map +1 -1
  148. package/umd/plugins/server-side.umd.js +1 -1
  149. package/umd/plugins/server-side.umd.js.map +1 -1
  150. package/umd/plugins/tree.umd.js +1 -1
  151. package/umd/plugins/tree.umd.js.map +1 -1
  152. package/umd/plugins/undo-redo.umd.js +1 -1
  153. package/umd/plugins/undo-redo.umd.js.map +1 -1
  154. package/umd/plugins/visibility.umd.js +1 -1
  155. package/umd/plugins/visibility.umd.js.map +1 -1
  156. package/lib/core/internal/editing.d.ts +0 -76
  157. package/lib/core/internal/editing.d.ts.map +0 -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,11 +57,11 @@ 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
+ class L {
64
65
  /**
65
66
  * Plugin dependencies - declare other plugins this one requires.
66
67
  *
@@ -77,8 +78,11 @@ class x {
77
78
  * ```
78
79
  */
79
80
  static dependencies;
80
- /** Plugin version - override in subclass if needed */
81
- version = "1.0.0";
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";
82
86
  /** CSS styles to inject into the grid's shadow DOM */
83
87
  styles;
84
88
  /** Custom cell renderers keyed by type name */
@@ -98,7 +102,7 @@ class x {
98
102
  * Created fresh in attach(), aborted in detach().
99
103
  * This ensures event listeners are properly cleaned up when plugins are re-attached.
100
104
  */
101
- #t;
105
+ #e;
102
106
  /**
103
107
  * Default configuration - subclasses should override this getter.
104
108
  * Note: This must be a getter (not property initializer) for proper inheritance
@@ -107,8 +111,8 @@ class x {
107
111
  get defaultConfig() {
108
112
  return {};
109
113
  }
110
- constructor(t = {}) {
111
- this.userConfig = t;
114
+ constructor(e = {}) {
115
+ this.userConfig = e;
112
116
  }
113
117
  /**
114
118
  * Called when the plugin is attached to a grid.
@@ -125,8 +129,8 @@ class x {
125
129
  * }
126
130
  * ```
127
131
  */
128
- attach(t) {
129
- 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 };
130
134
  }
131
135
  /**
132
136
  * Called when the plugin is detached from a grid.
@@ -142,7 +146,7 @@ class x {
142
146
  * ```
143
147
  */
144
148
  detach() {
145
- this.#t?.abort(), this.#t = void 0;
149
+ this.#e?.abort(), this.#e = void 0;
146
150
  }
147
151
  /**
148
152
  * Get another plugin instance from the same grid.
@@ -156,14 +160,22 @@ class x {
156
160
  * }
157
161
  * ```
158
162
  */
159
- getPlugin(t) {
160
- return this.grid?.getPlugin(t);
163
+ getPlugin(e) {
164
+ return this.grid?.getPlugin(e);
161
165
  }
162
166
  /**
163
167
  * Emit a custom event from the grid.
164
168
  */
165
- emit(t, e) {
166
- 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;
167
179
  }
168
180
  /**
169
181
  * Request a re-render of the grid.
@@ -171,6 +183,14 @@ class x {
171
183
  requestRender() {
172
184
  this.grid?.requestRender?.();
173
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
+ }
174
194
  /**
175
195
  * Request a lightweight style update without rebuilding DOM.
176
196
  * Use this instead of requestRender() when only CSS classes need updating.
@@ -204,6 +224,19 @@ class x {
204
224
  get visibleColumns() {
205
225
  return this.grid?._visibleColumns ?? [];
206
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
+ }
207
240
  /**
208
241
  * Get the shadow root of the grid.
209
242
  */
@@ -228,16 +261,61 @@ class x {
228
261
  * document.addEventListener('keydown', handler, { signal: this.disconnectSignal });
229
262
  */
230
263
  get disconnectSignal() {
231
- return this.#t?.signal ?? this.grid?.disconnectSignal;
264
+ return this.#e?.signal ?? this.grid?.disconnectSignal;
232
265
  }
233
266
  /**
234
267
  * Get the grid-level icons configuration.
235
268
  * Returns merged icons (user config + defaults).
236
269
  */
237
270
  get gridIcons() {
238
- const t = this.grid?.gridConfig?.icons ?? {};
239
- return { ...O, ...t };
271
+ const e = this.grid?.gridConfig?.icons ?? {};
272
+ return { ...R, ...e };
240
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;
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
241
319
  /**
242
320
  * Resolve an icon value to string or HTMLElement.
243
321
  * Checks plugin config first, then grid-level icons, then defaults.
@@ -246,8 +324,8 @@ class x {
246
324
  * @param pluginOverride - Optional plugin-level override
247
325
  * @returns The resolved icon value
248
326
  */
249
- resolveIcon(t, e) {
250
- return e !== void 0 ? e : this.gridIcons[t];
327
+ resolveIcon(e, t) {
328
+ return t !== void 0 ? t : this.gridIcons[e];
251
329
  }
252
330
  /**
253
331
  * Set an icon value on an element.
@@ -256,30 +334,30 @@ class x {
256
334
  * @param element - The element to set the icon on
257
335
  * @param icon - The icon value (string or HTMLElement)
258
336
  */
259
- setIcon(t, e) {
260
- 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)));
261
339
  }
262
340
  /**
263
341
  * Log a warning message.
264
342
  */
265
- warn(t) {
266
- console.warn(`[tbw-grid:${this.name}] ${t}`);
343
+ warn(e) {
344
+ console.warn(`[tbw-grid:${this.name}] ${e}`);
267
345
  }
268
346
  // #endregion
269
347
  }
270
- function b(n) {
271
- const t = n.meta ?? {};
272
- return t.lockPosition !== !0 && t.suppressMovable !== !0;
348
+ function O(a) {
349
+ const e = a.meta ?? {};
350
+ return e.lockPosition !== !0 && e.suppressMovable !== !0;
273
351
  }
274
- function v(n, t, e) {
275
- if (t === e || t < 0 || t >= n.length || e < 0 || e > n.length) return n;
276
- const i = [...n], [r] = i.splice(t, 1);
277
- 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;
278
356
  }
279
- 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}';
280
- 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 {
281
359
  name = "reorder";
282
- version = "1.0.0";
360
+ styles = A;
283
361
  get defaultConfig() {
284
362
  return {
285
363
  animation: "flip"
@@ -288,47 +366,47 @@ class I extends x {
288
366
  }
289
367
  /**
290
368
  * Resolve animation type from plugin config.
291
- * Respects grid-level animation.mode (disabled = no animation).
369
+ * Uses base class isAnimationEnabled to respect grid-level settings.
292
370
  */
293
371
  get animationType() {
294
372
  return this.isAnimationEnabled ? this.config.animation !== void 0 ? this.config.animation : this.config.viewTransition === !1 ? !1 : (this.config.viewTransition === !0, "flip") : !1;
295
373
  }
296
374
  /**
297
- * Check if animations are enabled at the grid level.
298
- * Respects gridConfig.animation.mode and CSS variable.
299
- */
300
- get isAnimationEnabled() {
301
- const e = this.grid.effectiveConfig?.animation?.mode ?? "reduced-motion";
302
- if (e === !1 || e === "off") return !1;
303
- if (e === !0 || e === "on") return !0;
304
- const i = this.shadowRoot?.host;
305
- return i ? getComputedStyle(i).getPropertyValue("--tbw-animation-enabled").trim() !== "0" : !0;
306
- }
307
- /**
308
- * 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.
309
377
  */
310
378
  get animationDuration() {
311
- if (this.config.animationDuration !== void 0)
312
- return this.config.animationDuration;
313
- const t = this.shadowRoot?.host;
314
- if (t) {
315
- const e = getComputedStyle(t).getPropertyValue("--tbw-animation-duration").trim(), i = parseInt(e, 10);
316
- if (!isNaN(i)) return i;
317
- }
318
- return 200;
379
+ return this.config.animationDuration !== void 0 ? this.config.animationDuration : super.animationDuration;
319
380
  }
320
381
  // #region Internal State
321
382
  isDragging = !1;
322
383
  draggedField = null;
323
384
  draggedIndex = null;
324
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
+ }
325
403
  // #endregion
326
404
  // #region Lifecycle
327
- attach(t) {
328
- super.attach(t), t.addEventListener(
405
+ attach(e) {
406
+ super.attach(e), e.addEventListener(
329
407
  "column-reorder-request",
330
- (e) => {
331
- const i = e.detail;
408
+ (t) => {
409
+ const i = t.detail;
332
410
  i?.field && typeof i.toIndex == "number" && this.moveColumn(i.field, i.toIndex);
333
411
  },
334
412
  { signal: this.disconnectSignal }
@@ -340,72 +418,59 @@ class I extends x {
340
418
  // #endregion
341
419
  // #region Hooks
342
420
  afterRender() {
343
- const t = this.shadowRoot;
344
- if (!t) return;
345
- t.querySelectorAll(".header-row > .cell").forEach((i) => {
346
- const r = i, c = r.getAttribute("data-field");
347
- if (!c) return;
348
- const a = this.columns.find((d) => d.field === c), s = !this.grid.queryPlugins({
349
- type: p.CAN_MOVE_COLUMN,
350
- context: a
351
- }).includes(!1);
352
- if (!a || !b(a) || !s) {
353
- 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;
354
429
  return;
355
430
  }
356
- r.draggable = !0, !r.getAttribute("data-dragstart-bound") && (r.setAttribute("data-dragstart-bound", "true"), r.addEventListener("dragstart", (d) => {
357
- const u = this.getColumnOrder().indexOf(c);
358
- 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");
359
- }), r.addEventListener("dragend", () => {
360
- this.isDragging = !1, this.draggedField = null, this.draggedIndex = null, this.dropIndex = null, t.querySelectorAll(".header-row > .cell").forEach((d) => {
361
- d.classList.remove("dragging", "drop-target", "drop-before", "drop-after");
362
- });
363
- }), r.addEventListener("dragover", (d) => {
364
- if (d.preventDefault(), !this.isDragging || this.draggedField === c) return;
365
- const f = r.getBoundingClientRect(), u = f.left + f.width / 2, h = this.getColumnOrder().indexOf(c);
366
- 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);
367
- }), r.addEventListener("dragleave", () => {
368
- r.classList.remove("drop-target", "drop-before", "drop-after");
369
- }), r.addEventListener("drop", (d) => {
370
- d.preventDefault();
371
- const f = this.draggedField, u = this.draggedIndex, g = this.dropIndex;
372
- 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)
373
446
  return;
374
- const h = g > u ? g - 1 : g, m = this.getColumnOrder(), w = v(m, u, h), C = {
375
- field: f,
376
- fromIndex: u,
377
- toIndex: h,
378
- 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
379
452
  };
380
- this.updateColumnOrder(w), this.emit("column-move", C);
453
+ this.updateColumnOrder(g), this.emitCancelable("column-move", h) && this.updateColumnOrder(f);
381
454
  }));
382
455
  });
383
456
  }
384
457
  /**
385
458
  * Handle Alt+Arrow keyboard shortcuts for column reordering.
386
459
  */
387
- onKeyDown(t) {
388
- if (!t.altKey || t.key !== "ArrowLeft" && t.key !== "ArrowRight")
460
+ onKeyDown(e) {
461
+ if (!e.altKey || e.key !== "ArrowLeft" && e.key !== "ArrowRight")
389
462
  return;
390
- const e = this.grid, i = e._focusCol, r = e._visibleColumns;
391
- if (i < 0 || i >= r.length) return;
392
- const c = r[i];
393
- if (!c || !b(c)) return;
394
- const a = this.grid;
395
- if (a.queryPlugins({
396
- type: p.CAN_MOVE_COLUMN,
397
- context: c
398
- }).includes(!1)) return;
399
- const l = this.getColumnOrder(), s = l.indexOf(c.field);
400
- if (s === -1) return;
401
- const d = t.key === "ArrowLeft" ? s - 1 : s + 1;
402
- if (d < 0 || d >= l.length) return;
403
- const f = r.find((u) => u.field === l[d]);
404
- if (!(f && a.queryPlugins({
405
- type: p.CAN_MOVE_COLUMN,
406
- context: f
407
- }).includes(!1)))
408
- 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;
409
474
  }
410
475
  // #endregion
411
476
  // #region Public API
@@ -421,30 +486,30 @@ class I extends x {
421
486
  * @param field - The field name of the column to move
422
487
  * @param toIndex - The target index
423
488
  */
424
- moveColumn(t, e) {
425
- const i = this.getColumnOrder(), r = i.indexOf(t);
426
- if (r === -1) return;
427
- const c = v(i, r, e);
428
- this.updateColumnOrder(c), this.emit("column-move", {
429
- field: t,
430
- fromIndex: r,
431
- toIndex: e,
432
- columnOrder: c
433
- });
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);
434
499
  }
435
500
  /**
436
501
  * Set a specific column order.
437
502
  * @param order - Array of field names in desired order
438
503
  */
439
- setColumnOrder(t) {
440
- this.updateColumnOrder(t);
504
+ setColumnOrder(e) {
505
+ this.updateColumnOrder(e);
441
506
  }
442
507
  /**
443
508
  * Reset column order to the original configuration order.
444
509
  */
445
510
  resetColumnOrder() {
446
- const t = this.columns.map((e) => e.field);
447
- this.updateColumnOrder(t);
511
+ const e = this.columns.map((t) => t.field);
512
+ this.updateColumnOrder(e);
448
513
  }
449
514
  // #endregion
450
515
  // #region View Transition
@@ -452,102 +517,99 @@ class I extends x {
452
517
  * Capture header cell positions before reorder.
453
518
  */
454
519
  captureHeaderPositions() {
455
- const t = /* @__PURE__ */ new Map();
456
- return this.shadowRoot?.querySelectorAll(".header-row > .cell[data-field]").forEach((e) => {
457
- const i = e.getAttribute("data-field");
458
- i && t.set(i, e.getBoundingClientRect().left);
459
- }), 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;
460
525
  }
461
526
  /**
462
527
  * Apply FLIP animation for column reorder.
463
528
  * Uses CSS transitions - JS sets initial transform and toggles class.
464
529
  * @param oldPositions - Header positions captured before DOM change
465
530
  */
466
- animateFLIP(t) {
467
- const e = this.shadowRoot;
468
- if (!e || t.size === 0) return;
531
+ animateFLIP(e) {
532
+ const t = this.shadowRoot;
533
+ if (!t || e.size === 0) return;
469
534
  const i = /* @__PURE__ */ new Map();
470
- if (e.querySelectorAll(".header-row > .cell[data-field]").forEach((a) => {
471
- const o = a.getAttribute("data-field");
472
- if (!o) return;
473
- const l = t.get(o);
474
- if (l === void 0) return;
475
- const s = l - a.getBoundingClientRect().left;
476
- 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);
477
542
  }), i.size === 0) return;
478
- const r = [];
479
- if (e.querySelectorAll(".cell[data-field]").forEach((a) => {
480
- const o = i.get(a.getAttribute("data-field") ?? "");
481
- if (o !== void 0) {
482
- const l = a;
483
- 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);
484
549
  }
485
- }), r.length === 0) return;
486
- e.host.offsetHeight;
487
- const c = this.animationDuration;
550
+ }), n.length === 0) return;
551
+ t.host.offsetHeight;
552
+ const d = this.animationDuration;
488
553
  requestAnimationFrame(() => {
489
- r.forEach((a) => {
490
- a.classList.add("flip-animating"), a.style.transform = "";
554
+ n.forEach((l) => {
555
+ l.classList.add("flip-animating"), l.style.transform = "";
491
556
  }), setTimeout(() => {
492
- r.forEach((a) => {
493
- a.style.transform = "", a.classList.remove("flip-animating");
557
+ n.forEach((l) => {
558
+ l.style.transform = "", l.classList.remove("flip-animating");
494
559
  });
495
- }, c + 50);
560
+ }, d + 50);
496
561
  });
497
562
  }
498
563
  /**
499
564
  * Apply crossfade animation for moved columns.
500
565
  * Uses CSS keyframes - JS just toggles classes.
501
566
  */
502
- animateFade(t) {
503
- const e = this.shadowRoot;
504
- if (!e) {
505
- t();
567
+ animateFade(e) {
568
+ const t = this.shadowRoot;
569
+ if (!t) {
570
+ e();
506
571
  return;
507
572
  }
508
573
  const i = this.captureHeaderPositions();
509
- t();
510
- const r = /* @__PURE__ */ new Set();
511
- if (e.querySelectorAll(".header-row > .cell[data-field]").forEach((o) => {
512
- const l = o.getAttribute("data-field");
513
- if (!l) return;
514
- const s = i.get(l);
515
- if (s === void 0) return;
516
- const d = o.getBoundingClientRect().left;
517
- Math.abs(s - d) > 1 && r.add(l);
518
- }), r.size === 0) return;
519
- const c = [];
520
- if (e.querySelectorAll(".cell[data-field]").forEach((o) => {
521
- const l = o.getAttribute("data-field");
522
- if (l && r.has(l)) {
523
- const s = o;
524
- 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);
525
590
  }
526
- }), c.length === 0) return;
527
- const a = this.animationDuration;
591
+ }), d.length === 0) return;
592
+ const l = this.animationDuration;
528
593
  setTimeout(() => {
529
- c.forEach((o) => o.classList.remove("fade-animating"));
530
- }, a + 50);
594
+ d.forEach((r) => r.classList.remove("fade-animating"));
595
+ }, l + 50);
531
596
  }
532
597
  /**
533
598
  * Update column order with configured animation.
534
599
  */
535
- updateColumnOrder(t) {
536
- const e = this.grid, i = this.animationType;
537
- if (i === "flip" && this.shadowRoot) {
538
- const r = this.captureHeaderPositions();
539
- e.setColumnOrder(t), requestAnimationFrame(() => {
540
- 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);
541
606
  });
542
- } else i === "fade" ? this.animateFade(() => e.setColumnOrder(t)) : e.setColumnOrder(t);
543
- e.requestStateChange?.();
607
+ } else t === "fade" ? this.animateFade(() => this.grid.setColumnOrder(e)) : this.grid.setColumnOrder(e);
608
+ this.grid.requestStateChange?.();
544
609
  }
545
610
  // #endregion
546
- // #region Styles
547
- styles = _;
548
- // #endregion
549
611
  }
550
612
  export {
551
- I as ReorderPlugin
613
+ _ as ReorderPlugin
552
614
  };
553
615
  //# sourceMappingURL=index.js.map