@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 R = {
1
+ const _ = {
2
2
  expand: "▶",
3
3
  collapse: "▼",
4
4
  sortAsc: "▲",
@@ -8,9 +8,28 @@ const R = {
8
8
  dragHandle: "⋮⋮",
9
9
  toolPanel: "☰"
10
10
  };
11
- class _ {
12
- /** Plugin version - override in subclass if needed */
13
- version = "1.0.0";
11
+ class S {
12
+ /**
13
+ * Plugin dependencies - declare other plugins this one requires.
14
+ *
15
+ * Dependencies are validated when the plugin is attached.
16
+ * Required dependencies throw an error if missing.
17
+ * Optional dependencies log an info message if missing.
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * static readonly dependencies: PluginDependency[] = [
22
+ * { name: 'editing', required: true, reason: 'Tracks cell edits for undo/redo' },
23
+ * { name: 'selection', required: false, reason: 'Enables selection-based undo' },
24
+ * ];
25
+ * ```
26
+ */
27
+ static dependencies;
28
+ /**
29
+ * Plugin version - defaults to grid version for built-in plugins.
30
+ * Third-party plugins can override with their own semver.
31
+ */
32
+ version = typeof __GRID_VERSION__ < "u" ? __GRID_VERSION__ : "dev";
14
33
  /** CSS styles to inject into the grid's shadow DOM */
15
34
  styles;
16
35
  /** Custom cell renderers keyed by type name */
@@ -97,12 +116,28 @@ class _ {
97
116
  emit(e, t) {
98
117
  this.grid?.dispatchEvent?.(new CustomEvent(e, { detail: t, bubbles: !0 }));
99
118
  }
119
+ /**
120
+ * Emit a cancelable custom event from the grid.
121
+ * @returns `true` if the event was cancelled (preventDefault called), `false` otherwise
122
+ */
123
+ emitCancelable(e, t) {
124
+ const i = new CustomEvent(e, { detail: t, bubbles: !0, cancelable: !0 });
125
+ return this.grid?.dispatchEvent?.(i), i.defaultPrevented;
126
+ }
100
127
  /**
101
128
  * Request a re-render of the grid.
102
129
  */
103
130
  requestRender() {
104
131
  this.grid?.requestRender?.();
105
132
  }
133
+ /**
134
+ * Request a re-render and restore focus styling afterward.
135
+ * Use this when a plugin action (like expand/collapse) triggers a render
136
+ * but needs to maintain keyboard navigation focus.
137
+ */
138
+ requestRenderWithFocus() {
139
+ this.grid?.requestRenderWithFocus?.();
140
+ }
106
141
  /**
107
142
  * Request a lightweight style update without rebuilding DOM.
108
143
  * Use this instead of requestRender() when only CSS classes need updating.
@@ -136,6 +171,19 @@ class _ {
136
171
  get visibleColumns() {
137
172
  return this.grid?._visibleColumns ?? [];
138
173
  }
174
+ /**
175
+ * Get the grid as an HTMLElement for direct DOM operations.
176
+ * Use sparingly - prefer the typed GridElementRef API when possible.
177
+ *
178
+ * @example
179
+ * ```ts
180
+ * const width = this.gridElement.clientWidth;
181
+ * this.gridElement.classList.add('my-plugin-active');
182
+ * ```
183
+ */
184
+ get gridElement() {
185
+ return this.grid;
186
+ }
139
187
  /**
140
188
  * Get the shadow root of the grid.
141
189
  */
@@ -168,8 +216,53 @@ class _ {
168
216
  */
169
217
  get gridIcons() {
170
218
  const e = this.grid?.gridConfig?.icons ?? {};
171
- return { ...R, ...e };
219
+ return { ..._, ...e };
172
220
  }
221
+ // #region Animation Helpers
222
+ /**
223
+ * Check if animations are enabled at the grid level.
224
+ * Respects gridConfig.animation.mode and the CSS variable set by the grid.
225
+ *
226
+ * Plugins should use this to skip animations when:
227
+ * - Animation mode is 'off' or `false`
228
+ * - User prefers reduced motion and mode is 'reduced-motion' (default)
229
+ *
230
+ * @example
231
+ * ```ts
232
+ * private get animationStyle(): 'slide' | 'fade' | false {
233
+ * if (!this.isAnimationEnabled) return false;
234
+ * return this.config.animation ?? 'slide';
235
+ * }
236
+ * ```
237
+ */
238
+ get isAnimationEnabled() {
239
+ const e = this.grid?.effectiveConfig?.animation?.mode ?? "reduced-motion";
240
+ if (e === !1 || e === "off") return !1;
241
+ if (e === !0 || e === "on") return !0;
242
+ const t = this.shadowRoot?.host;
243
+ return t ? getComputedStyle(t).getPropertyValue("--tbw-animation-enabled").trim() !== "0" : !0;
244
+ }
245
+ /**
246
+ * Get the animation duration in milliseconds from CSS variable.
247
+ * Falls back to 200ms if not set.
248
+ *
249
+ * Plugins can use this for their animation timing to stay consistent
250
+ * with the grid-level animation.duration setting.
251
+ *
252
+ * @example
253
+ * ```ts
254
+ * element.animate(keyframes, { duration: this.animationDuration });
255
+ * ```
256
+ */
257
+ get animationDuration() {
258
+ const e = this.shadowRoot?.host;
259
+ if (e) {
260
+ const t = getComputedStyle(e).getPropertyValue("--tbw-animation-duration").trim(), i = parseInt(t, 10);
261
+ if (!isNaN(i)) return i;
262
+ }
263
+ return 200;
264
+ }
265
+ // #endregion
173
266
  /**
174
267
  * Resolve an icon value to string or HTMLElement.
175
268
  * Checks plugin config first, then grid-level icons, then defaults.
@@ -199,8 +292,8 @@ class _ {
199
292
  }
200
293
  // #endregion
201
294
  }
202
- function k(f) {
203
- switch (f.type) {
295
+ function L(l) {
296
+ switch (l.type) {
204
297
  case "number":
205
298
  return (e) => {
206
299
  const t = document.createElement("input");
@@ -230,7 +323,7 @@ function k(f) {
230
323
  const c = document.createElement("option");
231
324
  c.value = String(r.value), c.textContent = r.label, (i.multi && Array.isArray(e.value) && e.value.includes(r.value) || !i.multi && e.value === r.value) && (c.selected = !0), t.appendChild(c);
232
325
  });
233
- const l = () => {
326
+ const o = () => {
234
327
  if (i.multi) {
235
328
  const r = [];
236
329
  Array.from(t.selectedOptions).forEach((c) => {
@@ -239,7 +332,7 @@ function k(f) {
239
332
  } else
240
333
  e.commit(t.value);
241
334
  };
242
- return t.addEventListener("change", l), t.addEventListener("blur", l), t.addEventListener("keydown", (r) => {
335
+ return t.addEventListener("change", o), t.addEventListener("blur", o), t.addEventListener("keydown", (r) => {
243
336
  r.key === "Escape" && e.cancel();
244
337
  }), t;
245
338
  };
@@ -253,30 +346,30 @@ function k(f) {
253
346
  }
254
347
  }
255
348
  const C = 'input,select,textarea,[contenteditable="true"],[contenteditable=""],[tabindex]:not([tabindex="-1"])';
256
- function b(f) {
257
- return !(typeof f != "string" || f === "__proto__" || f === "constructor" || f === "prototype");
349
+ function b(l) {
350
+ return !(typeof l != "string" || l === "__proto__" || l === "constructor" || l === "prototype");
351
+ }
352
+ function T(l) {
353
+ return (l.__editingCellCount ?? 0) > 0;
258
354
  }
259
- function T(f) {
260
- return (f.__editingCellCount ?? 0) > 0;
355
+ function k(l) {
356
+ const e = (l.__editingCellCount ?? 0) + 1;
357
+ l.__editingCellCount = e, l.setAttribute("data-has-editing", "");
261
358
  }
262
- function L(f) {
263
- const e = (f.__editingCellCount ?? 0) + 1;
264
- f.__editingCellCount = e, f.setAttribute("data-has-editing", "");
359
+ function A(l) {
360
+ l.__editingCellCount = 0, l.removeAttribute("data-has-editing");
265
361
  }
266
- function S(f) {
267
- f.__editingCellCount = 0, f.removeAttribute("data-has-editing");
362
+ function y(l, e) {
363
+ return l instanceof HTMLInputElement ? l.type === "checkbox" ? l.checked : l.type === "number" ? l.value === "" ? null : Number(l.value) : l.type === "date" ? l.valueAsDate : l.value : e?.type === "number" && l.value !== "" ? Number(l.value) : l.value;
268
364
  }
269
- function A(f, e, t) {
270
- const i = f.querySelector("input,textarea,select");
271
- if (!i) return;
272
- const s = () => i instanceof HTMLInputElement ? i.type === "checkbox" ? i.checked : i.type === "number" ? i.value === "" ? null : Number(i.value) : i.type === "date" ? i.valueAsDate : i.value : e.type === "number" && i.value !== "" ? Number(i.value) : i.value;
273
- i.addEventListener("blur", () => {
274
- t(s());
275
- }), i instanceof HTMLInputElement && i.type === "checkbox" ? i.addEventListener("change", () => t(i.checked)) : i instanceof HTMLSelectElement && i.addEventListener("change", () => t(s()));
365
+ function q(l, e, t) {
366
+ const i = l.querySelector("input,textarea,select");
367
+ i && (i.addEventListener("blur", () => {
368
+ t(y(i, e));
369
+ }), i instanceof HTMLInputElement && i.type === "checkbox" ? i.addEventListener("change", () => t(i.checked)) : i instanceof HTMLSelectElement && i.addEventListener("change", () => t(y(i, e))));
276
370
  }
277
- class q extends _ {
371
+ class O extends S {
278
372
  name = "editing";
279
- version = "1.0.0";
280
373
  get defaultConfig() {
281
374
  return {
282
375
  editOn: "click"
@@ -328,15 +421,6 @@ class q extends _ {
328
421
  this.#e = -1, this.#r = -1, this.#s.clear(), this.#t.clear(), this.#n.clear(), super.detach();
329
422
  }
330
423
  // #endregion
331
- // #region Config Augmentation (processColumns hook)
332
- /**
333
- * Augment columns with editing metadata.
334
- * This enables the grid to recognize editable columns without core knowledge.
335
- */
336
- processColumns(e) {
337
- return e;
338
- }
339
- // #endregion
340
424
  // #region Event Handlers (event distribution)
341
425
  /**
342
426
  * Handle cell clicks - start editing if configured for click mode.
@@ -361,12 +445,12 @@ class q extends _ {
361
445
  if (e.key === " " || e.key === "Spacebar") {
362
446
  const i = t._focusRow, s = t._focusCol;
363
447
  if (i >= 0 && s >= 0) {
364
- const n = t._visibleColumns[s], l = t._rows[i];
365
- if (n?.editable && n.type === "boolean" && l) {
448
+ const n = t._visibleColumns[s], o = t._rows[i];
449
+ if (n?.editable && n.type === "boolean" && o) {
366
450
  const r = n.field;
367
451
  if (b(r)) {
368
- const u = !l[r];
369
- return this.#c(i, n, u, l), e.preventDefault(), this.requestRender(), !0;
452
+ const f = !o[r];
453
+ return this.#c(i, n, f, o), e.preventDefault(), this.requestRender(), !0;
370
454
  }
371
455
  }
372
456
  }
@@ -378,7 +462,7 @@ class q extends _ {
378
462
  const i = this.config.editOn ?? t.effectiveConfig?.editOn;
379
463
  if (i === !1 || i === "manual") return !1;
380
464
  const s = t._focusRow;
381
- return s >= 0 && t._columns?.some((l) => l.editable) ? (this.beginBulkEdit(s), !0) : !1;
465
+ return s >= 0 && t._columns?.some((o) => o.editable) ? (this.beginBulkEdit(s), !0) : !1;
382
466
  }
383
467
  return !1;
384
468
  }
@@ -393,12 +477,12 @@ class q extends _ {
393
477
  const e = this.grid;
394
478
  if (this.#o && (this.#o = !1, this.#u(e)), this.#n.size !== 0)
395
479
  for (const t of this.#n) {
396
- const [i, s] = t.split(":"), n = parseInt(i, 10), l = parseInt(s, 10), r = e.findRenderedRowElement?.(n);
480
+ const [i, s] = t.split(":"), n = parseInt(i, 10), o = parseInt(s, 10), r = e.findRenderedRowElement?.(n);
397
481
  if (!r) continue;
398
- const c = r.querySelector(`.cell[data-col="${l}"]`);
482
+ const c = r.querySelector(`.cell[data-col="${o}"]`);
399
483
  if (!c || c.classList.contains("editing")) continue;
400
- const u = e._rows[n], a = e._visibleColumns[l];
401
- u && a && this.#a(u, n, a, l, c, !0);
484
+ const f = e._rows[n], d = e._visibleColumns[o];
485
+ f && d && this.#a(f, n, d, o, c, !0);
402
486
  }
403
487
  }
404
488
  /**
@@ -476,12 +560,12 @@ class q extends _ {
476
560
  if ((this.config.editOn ?? t.effectiveConfig?.editOn) === !1 || !t._columns?.some((r) => r.editable)) return;
477
561
  const n = t.findRenderedRowElement?.(e);
478
562
  if (!n) return;
479
- const l = t._rows[e];
480
- this.#d(e, l), Array.from(n.children).forEach((r, c) => {
481
- const u = t._visibleColumns[c];
482
- if (u?.editable) {
483
- const a = r;
484
- a.classList.contains("editing") || this.#a(l, e, u, c, a, !0);
563
+ const o = t._rows[e];
564
+ this.#d(e, o), Array.from(n.children).forEach((r, c) => {
565
+ const f = t._visibleColumns[c];
566
+ if (f?.editable) {
567
+ const d = r;
568
+ d.classList.contains("editing") || this.#a(o, e, f, c, d, !0);
485
569
  }
486
570
  }), setTimeout(() => {
487
571
  let r = n.querySelector(`.cell[data-col="${t._focusCol}"]`);
@@ -512,8 +596,8 @@ class q extends _ {
512
596
  * Begin editing a single cell.
513
597
  */
514
598
  #f(e, t, i) {
515
- const s = this.grid, n = s._rows[e], l = s._visibleColumns[t];
516
- !n || !l?.editable || i.classList.contains("editing") || (this.#e !== e && this.#d(e, n), this.#r = t, this.#a(n, e, l, t, i, !1));
599
+ const s = this.grid, n = s._rows[e], o = s._visibleColumns[t];
600
+ !n || !o?.editable || i.classList.contains("editing") || (this.#e !== e && this.#d(e, n), this.#r = t, this.#a(n, e, o, t, i, !1));
517
601
  }
518
602
  /**
519
603
  * Sync the internal grid state with the plugin's editing state.
@@ -533,16 +617,16 @@ class q extends _ {
533
617
  */
534
618
  #i(e, t) {
535
619
  if (this.#e !== e) return;
536
- const i = this.grid, s = this.#s.get(e), n = i._rows[e], l = i.findRenderedRowElement?.(e);
537
- if (!t && l && n && l.querySelectorAll(".cell.editing").forEach((c) => {
538
- const u = Number(c.getAttribute("data-col"));
539
- if (isNaN(u)) return;
540
- const a = i._visibleColumns[u];
541
- if (!a) return;
542
- const g = c.querySelector("input,textarea,select");
543
- if (g) {
544
- let o;
545
- g instanceof HTMLInputElement && g.type === "checkbox" ? o = g.checked : (o = g.value, a.type === "number" && o !== "" && (o = Number(o))), n[a.field] !== o && this.#c(e, a, o, n);
620
+ const i = this.grid, s = this.#s.get(e), n = i._rows[e], o = i.findRenderedRowElement?.(e);
621
+ if (!t && o && n && o.querySelectorAll(".cell.editing").forEach((c) => {
622
+ const f = Number(c.getAttribute("data-col"));
623
+ if (isNaN(f)) return;
624
+ const d = i._visibleColumns[f];
625
+ if (!d) return;
626
+ const p = c.querySelector("input,textarea,select");
627
+ if (p) {
628
+ const a = y(p, d);
629
+ n[d.field] !== a && this.#c(e, d, a, n);
546
630
  }
547
631
  }), t && s && n)
548
632
  Object.keys(s).forEach((r) => {
@@ -561,119 +645,115 @@ class q extends _ {
561
645
  this.#s.delete(e), this.#e = -1, this.#r = -1, this.#l();
562
646
  for (const r of this.#n)
563
647
  r.startsWith(`${e}:`) && this.#n.delete(r);
564
- l && (l.querySelectorAll(".cell.editing").forEach((r) => {
565
- r.classList.remove("editing"), S(r.parentElement);
566
- }), this.requestRender()), this.#o = !0, l || (this.#u(i), this.#o = !1);
648
+ o && (o.querySelectorAll(".cell.editing").forEach((r) => {
649
+ r.classList.remove("editing"), A(r.parentElement);
650
+ }), this.requestRender()), this.#o = !0, o || (this.#u(i), this.#o = !1);
567
651
  }
568
652
  /**
569
653
  * Commit a single cell value change.
570
654
  */
571
655
  #c(e, t, i, s) {
572
656
  const n = t.field;
573
- if (!b(n) || s[n] === i) return;
574
- s[n] = i;
657
+ if (!b(n)) return;
658
+ const o = s[n];
659
+ if (o === i) return;
575
660
  const r = !this.#t.has(e);
576
- this.#t.add(e), this.#l();
577
- const u = this.grid.findRenderedRowElement?.(e);
578
- u && u.classList.add("changed"), this.emit("cell-commit", {
661
+ if (this.emitCancelable("cell-commit", {
579
662
  row: s,
580
663
  field: n,
664
+ oldValue: o,
581
665
  value: i,
582
666
  rowIndex: e,
583
667
  changedRows: this.changedRows,
584
668
  changedRowIndices: this.changedRowIndices,
585
669
  firstTimeForRow: r
586
- });
670
+ })) return;
671
+ s[n] = i, this.#t.add(e), this.#l();
672
+ const d = this.grid.findRenderedRowElement?.(e);
673
+ d && d.classList.add("changed");
587
674
  }
588
675
  /**
589
676
  * Inject an editor into a cell.
590
677
  */
591
- #a(e, t, i, s, n, l) {
678
+ #a(e, t, i, s, n, o) {
592
679
  if (!i.editable || n.classList.contains("editing")) return;
593
680
  const r = b(i.field) ? e[i.field] : void 0;
594
681
  n.classList.add("editing"), this.#n.add(`${t}:${s}`);
595
682
  const c = n.parentElement;
596
- c && L(c);
597
- let u = !1;
598
- const a = (h) => {
599
- u || this.#e === -1 || this.#c(t, i, h, e);
600
- }, g = () => {
601
- u = !0, b(i.field) && (e[i.field] = r);
602
- }, o = document.createElement("div");
603
- o.style.display = "contents", n.innerHTML = "", n.appendChild(o), o.addEventListener("keydown", (h) => {
604
- h.key === "Enter" && (h.stopPropagation(), h.preventDefault(), u = !0, this.#i(t, !1)), h.key === "Escape" && (h.stopPropagation(), h.preventDefault(), g(), this.#i(t, !0));
683
+ c && k(c);
684
+ let f = !1;
685
+ const d = (h) => {
686
+ f || this.#e === -1 || this.#c(t, i, h, e);
687
+ }, p = () => {
688
+ f = !0, b(i.field) && (e[i.field] = r);
689
+ }, a = document.createElement("div");
690
+ a.className = "tbw-editor-host", n.innerHTML = "", n.appendChild(a), a.addEventListener("keydown", (h) => {
691
+ h.key === "Enter" && (h.stopPropagation(), h.preventDefault(), f = !0, this.#i(t, !1)), h.key === "Escape" && (h.stopPropagation(), h.preventDefault(), p(), this.#i(t, !0));
605
692
  });
606
- const p = i, v = p.__editorTemplate, d = p.editor || (v ? "template" : k(i)), m = r;
607
- if (d === "template" && v)
608
- this.#h(o, p, e, r, a, g, l, t);
609
- else if (typeof d == "string") {
610
- const h = document.createElement(d);
611
- h.value = m, h.addEventListener("change", () => a(h.value)), o.appendChild(h), l || queueMicrotask(() => {
612
- o.querySelector(C)?.focus({ preventScroll: !0 });
693
+ const g = i, v = g.__editorTemplate, u = g.editor || (v ? "template" : L(i)), m = r;
694
+ if (u === "template" && v)
695
+ this.#h(a, g, e, r, d, p, o, t);
696
+ else if (typeof u == "string") {
697
+ const h = document.createElement(u);
698
+ h.value = m, h.addEventListener("change", () => d(h.value)), a.appendChild(h), o || queueMicrotask(() => {
699
+ a.querySelector(C)?.focus({ preventScroll: !0 });
613
700
  });
614
- } else if (typeof d == "function") {
615
- const h = { row: e, value: m, field: i.field, column: i, commit: a, cancel: g }, E = d(h);
616
- typeof E == "string" ? (o.innerHTML = E, A(o, i, a)) : E instanceof Node && o.appendChild(E), l || queueMicrotask(() => {
617
- o.querySelector(C)?.focus({ preventScroll: !0 });
701
+ } else if (typeof u == "function") {
702
+ const h = { row: e, value: m, field: i.field, column: i, commit: d, cancel: p }, E = u(h);
703
+ typeof E == "string" ? (a.innerHTML = E, q(a, i, d)) : E instanceof Node && a.appendChild(E), o || queueMicrotask(() => {
704
+ a.querySelector(C)?.focus({ preventScroll: !0 });
618
705
  });
619
- } else if (d && typeof d == "object") {
706
+ } else if (u && typeof u == "object") {
620
707
  const h = this.grid, E = document.createElement("div");
621
- E.setAttribute("data-external-editor", ""), E.setAttribute("data-field", i.field), o.appendChild(E);
622
- const y = { row: e, value: m, field: i.field, column: i, commit: a, cancel: g };
623
- if (d.mount)
708
+ E.setAttribute("data-external-editor", ""), E.setAttribute("data-field", i.field), a.appendChild(E);
709
+ const w = { row: e, value: m, field: i.field, column: i, commit: d, cancel: p };
710
+ if (u.mount)
624
711
  try {
625
- d.mount({ placeholder: E, context: y, spec: d });
626
- } catch (w) {
627
- console.warn(`[tbw-grid] External editor mount error for column '${i.field}':`, w);
712
+ u.mount({ placeholder: E, context: w, spec: u });
713
+ } catch (R) {
714
+ console.warn(`[tbw-grid] External editor mount error for column '${i.field}':`, R);
628
715
  }
629
716
  else
630
717
  h.dispatchEvent(
631
- new CustomEvent("mount-external-editor", { detail: { placeholder: E, spec: d, context: y } })
718
+ new CustomEvent("mount-external-editor", { detail: { placeholder: E, spec: u, context: w } })
632
719
  );
633
720
  }
634
721
  }
635
722
  /**
636
723
  * Render a template-based editor.
637
724
  */
638
- #h(e, t, i, s, n, l, r, c) {
639
- const u = t.__editorTemplate;
640
- if (!u) return;
641
- const a = u.cloneNode(!0), g = t.__compiledEditor;
642
- g ? a.innerHTML = g({
725
+ #h(e, t, i, s, n, o, r, c) {
726
+ const f = t.__editorTemplate;
727
+ if (!f) return;
728
+ const d = f.cloneNode(!0), p = t.__compiledEditor;
729
+ p ? d.innerHTML = p({
643
730
  row: i,
644
731
  value: s,
645
732
  field: t.field,
646
733
  column: t,
647
734
  commit: n,
648
- cancel: l
649
- }) : a.querySelectorAll("*").forEach((p) => {
650
- p.childNodes.length === 1 && p.firstChild?.nodeType === Node.TEXT_NODE && (p.textContent = p.textContent?.replace(/{{\s*value\s*}}/g, s == null ? "" : String(s)).replace(/{{\s*row\.([a-zA-Z0-9_]+)\s*}}/g, (v, d) => {
651
- if (!b(d)) return "";
652
- const m = i[d];
735
+ cancel: o
736
+ }) : d.querySelectorAll("*").forEach((g) => {
737
+ g.childNodes.length === 1 && g.firstChild?.nodeType === Node.TEXT_NODE && (g.textContent = g.textContent?.replace(/{{\s*value\s*}}/g, s == null ? "" : String(s)).replace(/{{\s*row\.([a-zA-Z0-9_]+)\s*}}/g, (v, u) => {
738
+ if (!b(u)) return "";
739
+ const m = i[u];
653
740
  return m == null ? "" : String(m);
654
741
  }) || "");
655
742
  });
656
- const o = a.querySelector(
743
+ const a = d.querySelector(
657
744
  "input,textarea,select"
658
745
  );
659
- if (o) {
660
- o instanceof HTMLInputElement && o.type === "checkbox" ? o.checked = !!s : o.value = String(s ?? "");
661
- let p = !1;
662
- o.addEventListener("blur", () => {
663
- if (p) return;
664
- const v = o instanceof HTMLInputElement && o.type === "checkbox" ? o.checked : o.value;
665
- n(v);
666
- }), o.addEventListener("keydown", (v) => {
667
- const d = v;
668
- if (d.key === "Enter") {
669
- d.stopPropagation(), d.preventDefault(), p = !0;
670
- const m = o instanceof HTMLInputElement && o.type === "checkbox" ? o.checked : o.value;
671
- n(m), this.#i(c, !1);
672
- }
673
- d.key === "Escape" && (d.stopPropagation(), d.preventDefault(), l(), this.#i(c, !0));
674
- }), o instanceof HTMLInputElement && o.type === "checkbox" && o.addEventListener("change", () => n(o.checked)), r || setTimeout(() => o.focus({ preventScroll: !0 }), 0);
746
+ if (a) {
747
+ a instanceof HTMLInputElement && a.type === "checkbox" ? a.checked = !!s : a.value = String(s ?? "");
748
+ let g = !1;
749
+ a.addEventListener("blur", () => {
750
+ g || n(y(a, t));
751
+ }), a.addEventListener("keydown", (v) => {
752
+ const u = v;
753
+ u.key === "Enter" && (u.stopPropagation(), u.preventDefault(), g = !0, n(y(a, t)), this.#i(c, !1)), u.key === "Escape" && (u.stopPropagation(), u.preventDefault(), o(), this.#i(c, !0));
754
+ }), a instanceof HTMLInputElement && a.type === "checkbox" && a.addEventListener("change", () => n(a.checked)), r || setTimeout(() => a.focus({ preventScroll: !0 }), 0);
675
755
  }
676
- e.appendChild(a);
756
+ e.appendChild(d);
677
757
  }
678
758
  /**
679
759
  * Restore focus to cell after exiting edit mode.
@@ -684,7 +764,7 @@ class q extends _ {
684
764
  const t = e._focusRow, i = e._focusCol, s = e.findRenderedRowElement?.(t);
685
765
  if (s) {
686
766
  Array.from(e._bodyEl.querySelectorAll(".cell-focus")).forEach(
687
- (l) => l.classList.remove("cell-focus")
767
+ (o) => o.classList.remove("cell-focus")
688
768
  );
689
769
  const n = s.querySelector(`.cell[data-row="${t}"][data-col="${i}"]`);
690
770
  n && (n.classList.add("cell-focus"), n.setAttribute("aria-selected", "true"), n.hasAttribute("tabindex") || n.setAttribute("tabindex", "-1"), n.focus({ preventScroll: !0 }));
@@ -696,10 +776,10 @@ class q extends _ {
696
776
  // #endregion
697
777
  }
698
778
  export {
699
- q as EditingPlugin,
779
+ O as EditingPlugin,
700
780
  C as FOCUSABLE_EDITOR_SELECTOR,
701
- S as clearEditingState,
702
- k as defaultEditorFor,
781
+ A as clearEditingState,
782
+ L as defaultEditorFor,
703
783
  T as hasEditingCells
704
784
  };
705
785
  //# sourceMappingURL=index.js.map