@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,4 +1,4 @@
1
- const R = {
1
+ const _ = {
2
2
  expand: "▶",
3
3
  collapse: "▼",
4
4
  sortAsc: "▲",
@@ -8,7 +8,7 @@ const R = {
8
8
  dragHandle: "⋮⋮",
9
9
  toolPanel: "☰"
10
10
  };
11
- class _ {
11
+ class S {
12
12
  /**
13
13
  * Plugin dependencies - declare other plugins this one requires.
14
14
  *
@@ -25,8 +25,11 @@ class _ {
25
25
  * ```
26
26
  */
27
27
  static dependencies;
28
- /** Plugin version - override in subclass if needed */
29
- version = "1.0.0";
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";
30
33
  /** CSS styles to inject into the grid's shadow DOM */
31
34
  styles;
32
35
  /** Custom cell renderers keyed by type name */
@@ -113,12 +116,28 @@ class _ {
113
116
  emit(e, t) {
114
117
  this.grid?.dispatchEvent?.(new CustomEvent(e, { detail: t, bubbles: !0 }));
115
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
+ }
116
127
  /**
117
128
  * Request a re-render of the grid.
118
129
  */
119
130
  requestRender() {
120
131
  this.grid?.requestRender?.();
121
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
+ }
122
141
  /**
123
142
  * Request a lightweight style update without rebuilding DOM.
124
143
  * Use this instead of requestRender() when only CSS classes need updating.
@@ -152,6 +171,19 @@ class _ {
152
171
  get visibleColumns() {
153
172
  return this.grid?._visibleColumns ?? [];
154
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
+ }
155
187
  /**
156
188
  * Get the shadow root of the grid.
157
189
  */
@@ -184,8 +216,53 @@ class _ {
184
216
  */
185
217
  get gridIcons() {
186
218
  const e = this.grid?.gridConfig?.icons ?? {};
187
- return { ...R, ...e };
219
+ return { ..._, ...e };
188
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
189
266
  /**
190
267
  * Resolve an icon value to string or HTMLElement.
191
268
  * Checks plugin config first, then grid-level icons, then defaults.
@@ -215,8 +292,8 @@ class _ {
215
292
  }
216
293
  // #endregion
217
294
  }
218
- function k(f) {
219
- switch (f.type) {
295
+ function L(l) {
296
+ switch (l.type) {
220
297
  case "number":
221
298
  return (e) => {
222
299
  const t = document.createElement("input");
@@ -246,7 +323,7 @@ function k(f) {
246
323
  const c = document.createElement("option");
247
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);
248
325
  });
249
- const l = () => {
326
+ const o = () => {
250
327
  if (i.multi) {
251
328
  const r = [];
252
329
  Array.from(t.selectedOptions).forEach((c) => {
@@ -255,7 +332,7 @@ function k(f) {
255
332
  } else
256
333
  e.commit(t.value);
257
334
  };
258
- 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) => {
259
336
  r.key === "Escape" && e.cancel();
260
337
  }), t;
261
338
  };
@@ -268,31 +345,31 @@ function k(f) {
268
345
  };
269
346
  }
270
347
  }
271
- const w = 'input,select,textarea,[contenteditable="true"],[contenteditable=""],[tabindex]:not([tabindex="-1"])';
272
- function b(f) {
273
- return !(typeof f != "string" || f === "__proto__" || f === "constructor" || f === "prototype");
348
+ const C = 'input,select,textarea,[contenteditable="true"],[contenteditable=""],[tabindex]:not([tabindex="-1"])';
349
+ function b(l) {
350
+ return !(typeof l != "string" || l === "__proto__" || l === "constructor" || l === "prototype");
274
351
  }
275
- function T(f) {
276
- return (f.__editingCellCount ?? 0) > 0;
352
+ function T(l) {
353
+ return (l.__editingCellCount ?? 0) > 0;
277
354
  }
278
- function L(f) {
279
- const e = (f.__editingCellCount ?? 0) + 1;
280
- f.__editingCellCount = e, f.setAttribute("data-has-editing", "");
355
+ function k(l) {
356
+ const e = (l.__editingCellCount ?? 0) + 1;
357
+ l.__editingCellCount = e, l.setAttribute("data-has-editing", "");
281
358
  }
282
- function S(f) {
283
- f.__editingCellCount = 0, f.removeAttribute("data-has-editing");
359
+ function A(l) {
360
+ l.__editingCellCount = 0, l.removeAttribute("data-has-editing");
284
361
  }
285
- function A(f, e, t) {
286
- const i = f.querySelector("input,textarea,select");
287
- if (!i) return;
288
- 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;
289
- i.addEventListener("blur", () => {
290
- t(s());
291
- }), i instanceof HTMLInputElement && i.type === "checkbox" ? i.addEventListener("change", () => t(i.checked)) : i instanceof HTMLSelectElement && i.addEventListener("change", () => t(s()));
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;
292
364
  }
293
- class q extends _ {
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))));
370
+ }
371
+ class O extends S {
294
372
  name = "editing";
295
- version = "1.0.0";
296
373
  get defaultConfig() {
297
374
  return {
298
375
  editOn: "click"
@@ -344,15 +421,6 @@ class q extends _ {
344
421
  this.#e = -1, this.#r = -1, this.#s.clear(), this.#t.clear(), this.#n.clear(), super.detach();
345
422
  }
346
423
  // #endregion
347
- // #region Config Augmentation (processColumns hook)
348
- /**
349
- * Augment columns with editing metadata.
350
- * This enables the grid to recognize editable columns without core knowledge.
351
- */
352
- processColumns(e) {
353
- return e;
354
- }
355
- // #endregion
356
424
  // #region Event Handlers (event distribution)
357
425
  /**
358
426
  * Handle cell clicks - start editing if configured for click mode.
@@ -377,12 +445,12 @@ class q extends _ {
377
445
  if (e.key === " " || e.key === "Spacebar") {
378
446
  const i = t._focusRow, s = t._focusCol;
379
447
  if (i >= 0 && s >= 0) {
380
- const n = t._visibleColumns[s], l = t._rows[i];
381
- 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) {
382
450
  const r = n.field;
383
451
  if (b(r)) {
384
- const u = !l[r];
385
- 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;
386
454
  }
387
455
  }
388
456
  }
@@ -394,7 +462,7 @@ class q extends _ {
394
462
  const i = this.config.editOn ?? t.effectiveConfig?.editOn;
395
463
  if (i === !1 || i === "manual") return !1;
396
464
  const s = t._focusRow;
397
- 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;
398
466
  }
399
467
  return !1;
400
468
  }
@@ -409,12 +477,12 @@ class q extends _ {
409
477
  const e = this.grid;
410
478
  if (this.#o && (this.#o = !1, this.#u(e)), this.#n.size !== 0)
411
479
  for (const t of this.#n) {
412
- 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);
413
481
  if (!r) continue;
414
- const c = r.querySelector(`.cell[data-col="${l}"]`);
482
+ const c = r.querySelector(`.cell[data-col="${o}"]`);
415
483
  if (!c || c.classList.contains("editing")) continue;
416
- const u = e._rows[n], a = e._visibleColumns[l];
417
- 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);
418
486
  }
419
487
  }
420
488
  /**
@@ -492,17 +560,17 @@ class q extends _ {
492
560
  if ((this.config.editOn ?? t.effectiveConfig?.editOn) === !1 || !t._columns?.some((r) => r.editable)) return;
493
561
  const n = t.findRenderedRowElement?.(e);
494
562
  if (!n) return;
495
- const l = t._rows[e];
496
- this.#d(e, l), Array.from(n.children).forEach((r, c) => {
497
- const u = t._visibleColumns[c];
498
- if (u?.editable) {
499
- const a = r;
500
- 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);
501
569
  }
502
570
  }), setTimeout(() => {
503
571
  let r = n.querySelector(`.cell[data-col="${t._focusCol}"]`);
504
572
  if (r?.classList.contains("editing") || (r = n.querySelector(".cell.editing")), r?.classList.contains("editing")) {
505
- const c = r.querySelector(w);
573
+ const c = r.querySelector(C);
506
574
  try {
507
575
  c?.focus({ preventScroll: !0 });
508
576
  } catch {
@@ -528,8 +596,8 @@ class q extends _ {
528
596
  * Begin editing a single cell.
529
597
  */
530
598
  #f(e, t, i) {
531
- const s = this.grid, n = s._rows[e], l = s._visibleColumns[t];
532
- !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));
533
601
  }
534
602
  /**
535
603
  * Sync the internal grid state with the plugin's editing state.
@@ -549,16 +617,16 @@ class q extends _ {
549
617
  */
550
618
  #i(e, t) {
551
619
  if (this.#e !== e) return;
552
- const i = this.grid, s = this.#s.get(e), n = i._rows[e], l = i.findRenderedRowElement?.(e);
553
- if (!t && l && n && l.querySelectorAll(".cell.editing").forEach((c) => {
554
- const u = Number(c.getAttribute("data-col"));
555
- if (isNaN(u)) return;
556
- const a = i._visibleColumns[u];
557
- if (!a) return;
558
- const g = c.querySelector("input,textarea,select");
559
- if (g) {
560
- let o;
561
- 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);
562
630
  }
563
631
  }), t && s && n)
564
632
  Object.keys(s).forEach((r) => {
@@ -577,119 +645,115 @@ class q extends _ {
577
645
  this.#s.delete(e), this.#e = -1, this.#r = -1, this.#l();
578
646
  for (const r of this.#n)
579
647
  r.startsWith(`${e}:`) && this.#n.delete(r);
580
- l && (l.querySelectorAll(".cell.editing").forEach((r) => {
581
- r.classList.remove("editing"), S(r.parentElement);
582
- }), 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);
583
651
  }
584
652
  /**
585
653
  * Commit a single cell value change.
586
654
  */
587
655
  #c(e, t, i, s) {
588
656
  const n = t.field;
589
- if (!b(n) || s[n] === i) return;
590
- s[n] = i;
657
+ if (!b(n)) return;
658
+ const o = s[n];
659
+ if (o === i) return;
591
660
  const r = !this.#t.has(e);
592
- this.#t.add(e), this.#l();
593
- const u = this.grid.findRenderedRowElement?.(e);
594
- u && u.classList.add("changed"), this.emit("cell-commit", {
661
+ if (this.emitCancelable("cell-commit", {
595
662
  row: s,
596
663
  field: n,
664
+ oldValue: o,
597
665
  value: i,
598
666
  rowIndex: e,
599
667
  changedRows: this.changedRows,
600
668
  changedRowIndices: this.changedRowIndices,
601
669
  firstTimeForRow: r
602
- });
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");
603
674
  }
604
675
  /**
605
676
  * Inject an editor into a cell.
606
677
  */
607
- #a(e, t, i, s, n, l) {
678
+ #a(e, t, i, s, n, o) {
608
679
  if (!i.editable || n.classList.contains("editing")) return;
609
680
  const r = b(i.field) ? e[i.field] : void 0;
610
681
  n.classList.add("editing"), this.#n.add(`${t}:${s}`);
611
682
  const c = n.parentElement;
612
- c && L(c);
613
- let u = !1;
614
- const a = (h) => {
615
- u || this.#e === -1 || this.#c(t, i, h, e);
616
- }, g = () => {
617
- u = !0, b(i.field) && (e[i.field] = r);
618
- }, o = document.createElement("div");
619
- o.className = "tbw-editor-host", n.innerHTML = "", n.appendChild(o), o.addEventListener("keydown", (h) => {
620
- 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));
621
692
  });
622
- const p = i, v = p.__editorTemplate, d = p.editor || (v ? "template" : k(i)), m = r;
623
- if (d === "template" && v)
624
- this.#h(o, p, e, r, a, g, l, t);
625
- else if (typeof d == "string") {
626
- const h = document.createElement(d);
627
- h.value = m, h.addEventListener("change", () => a(h.value)), o.appendChild(h), l || queueMicrotask(() => {
628
- o.querySelector(w)?.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 });
629
700
  });
630
- } else if (typeof d == "function") {
631
- const h = { row: e, value: m, field: i.field, column: i, commit: a, cancel: g }, E = d(h);
632
- typeof E == "string" ? (o.innerHTML = E, A(o, i, a)) : E instanceof Node && o.appendChild(E), l || queueMicrotask(() => {
633
- o.querySelector(w)?.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 });
634
705
  });
635
- } else if (d && typeof d == "object") {
706
+ } else if (u && typeof u == "object") {
636
707
  const h = this.grid, E = document.createElement("div");
637
- E.setAttribute("data-external-editor", ""), E.setAttribute("data-field", i.field), o.appendChild(E);
638
- const y = { row: e, value: m, field: i.field, column: i, commit: a, cancel: g };
639
- 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)
640
711
  try {
641
- d.mount({ placeholder: E, context: y, spec: d });
642
- } catch (C) {
643
- console.warn(`[tbw-grid] External editor mount error for column '${i.field}':`, C);
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);
644
715
  }
645
716
  else
646
717
  h.dispatchEvent(
647
- 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 } })
648
719
  );
649
720
  }
650
721
  }
651
722
  /**
652
723
  * Render a template-based editor.
653
724
  */
654
- #h(e, t, i, s, n, l, r, c) {
655
- const u = t.__editorTemplate;
656
- if (!u) return;
657
- const a = u.cloneNode(!0), g = t.__compiledEditor;
658
- 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({
659
730
  row: i,
660
731
  value: s,
661
732
  field: t.field,
662
733
  column: t,
663
734
  commit: n,
664
- cancel: l
665
- }) : a.querySelectorAll("*").forEach((p) => {
666
- 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) => {
667
- if (!b(d)) return "";
668
- 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];
669
740
  return m == null ? "" : String(m);
670
741
  }) || "");
671
742
  });
672
- const o = a.querySelector(
743
+ const a = d.querySelector(
673
744
  "input,textarea,select"
674
745
  );
675
- if (o) {
676
- o instanceof HTMLInputElement && o.type === "checkbox" ? o.checked = !!s : o.value = String(s ?? "");
677
- let p = !1;
678
- o.addEventListener("blur", () => {
679
- if (p) return;
680
- const v = o instanceof HTMLInputElement && o.type === "checkbox" ? o.checked : o.value;
681
- n(v);
682
- }), o.addEventListener("keydown", (v) => {
683
- const d = v;
684
- if (d.key === "Enter") {
685
- d.stopPropagation(), d.preventDefault(), p = !0;
686
- const m = o instanceof HTMLInputElement && o.type === "checkbox" ? o.checked : o.value;
687
- n(m), this.#i(c, !1);
688
- }
689
- d.key === "Escape" && (d.stopPropagation(), d.preventDefault(), l(), this.#i(c, !0));
690
- }), 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);
691
755
  }
692
- e.appendChild(a);
756
+ e.appendChild(d);
693
757
  }
694
758
  /**
695
759
  * Restore focus to cell after exiting edit mode.
@@ -700,7 +764,7 @@ class q extends _ {
700
764
  const t = e._focusRow, i = e._focusCol, s = e.findRenderedRowElement?.(t);
701
765
  if (s) {
702
766
  Array.from(e._bodyEl.querySelectorAll(".cell-focus")).forEach(
703
- (l) => l.classList.remove("cell-focus")
767
+ (o) => o.classList.remove("cell-focus")
704
768
  );
705
769
  const n = s.querySelector(`.cell[data-row="${t}"][data-col="${i}"]`);
706
770
  n && (n.classList.add("cell-focus"), n.setAttribute("aria-selected", "true"), n.hasAttribute("tabindex") || n.setAttribute("tabindex", "-1"), n.focus({ preventScroll: !0 }));
@@ -712,10 +776,10 @@ class q extends _ {
712
776
  // #endregion
713
777
  }
714
778
  export {
715
- q as EditingPlugin,
716
- w as FOCUSABLE_EDITOR_SELECTOR,
717
- S as clearEditingState,
718
- k as defaultEditorFor,
779
+ O as EditingPlugin,
780
+ C as FOCUSABLE_EDITOR_SELECTOR,
781
+ A as clearEditingState,
782
+ L as defaultEditorFor,
719
783
  T as hasEditingCells
720
784
  };
721
785
  //# sourceMappingURL=index.js.map