@toolbox-web/grid 1.12.1 → 1.14.0

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 (98) hide show
  1. package/all.js +1171 -943
  2. package/all.js.map +1 -1
  3. package/index.js +735 -737
  4. package/index.js.map +1 -1
  5. package/lib/core/grid.d.ts.map +1 -1
  6. package/lib/core/internal/row-animation.d.ts.map +1 -1
  7. package/lib/core/internal/sanitize.d.ts.map +1 -1
  8. package/lib/core/internal/validate-config.d.ts.map +1 -1
  9. package/lib/core/plugin/types.d.ts +1 -1
  10. package/lib/core/plugin/types.d.ts.map +1 -1
  11. package/lib/core/types.d.ts +48 -1
  12. package/lib/core/types.d.ts.map +1 -1
  13. package/lib/plugins/clipboard/ClipboardPlugin.d.ts +69 -8
  14. package/lib/plugins/clipboard/ClipboardPlugin.d.ts.map +1 -1
  15. package/lib/plugins/clipboard/index.d.ts +1 -1
  16. package/lib/plugins/clipboard/index.d.ts.map +1 -1
  17. package/lib/plugins/clipboard/index.js +257 -192
  18. package/lib/plugins/clipboard/index.js.map +1 -1
  19. package/lib/plugins/clipboard/types.d.ts +31 -0
  20. package/lib/plugins/clipboard/types.d.ts.map +1 -1
  21. package/lib/plugins/column-virtualization/index.js.map +1 -1
  22. package/lib/plugins/context-menu/ContextMenuPlugin.d.ts +8 -0
  23. package/lib/plugins/context-menu/ContextMenuPlugin.d.ts.map +1 -1
  24. package/lib/plugins/context-menu/index.js +75 -60
  25. package/lib/plugins/context-menu/index.js.map +1 -1
  26. package/lib/plugins/context-menu/types.d.ts +7 -0
  27. package/lib/plugins/context-menu/types.d.ts.map +1 -1
  28. package/lib/plugins/editing/EditingPlugin.d.ts.map +1 -1
  29. package/lib/plugins/editing/editors.d.ts +2 -2
  30. package/lib/plugins/editing/editors.d.ts.map +1 -1
  31. package/lib/plugins/editing/index.d.ts +1 -1
  32. package/lib/plugins/editing/index.d.ts.map +1 -1
  33. package/lib/plugins/editing/index.js +393 -337
  34. package/lib/plugins/editing/index.js.map +1 -1
  35. package/lib/plugins/editing/types.d.ts +50 -23
  36. package/lib/plugins/editing/types.d.ts.map +1 -1
  37. package/lib/plugins/export/ExportPlugin.d.ts.map +1 -1
  38. package/lib/plugins/export/index.js +75 -66
  39. package/lib/plugins/export/index.js.map +1 -1
  40. package/lib/plugins/filtering/index.d.ts +1 -1
  41. package/lib/plugins/filtering/index.d.ts.map +1 -1
  42. package/lib/plugins/filtering/index.js +9 -9
  43. package/lib/plugins/filtering/index.js.map +1 -1
  44. package/lib/plugins/grouping-columns/index.js.map +1 -1
  45. package/lib/plugins/grouping-rows/index.js.map +1 -1
  46. package/lib/plugins/master-detail/index.js +57 -56
  47. package/lib/plugins/master-detail/index.js.map +1 -1
  48. package/lib/plugins/multi-sort/index.js.map +1 -1
  49. package/lib/plugins/pinned-columns/index.js.map +1 -1
  50. package/lib/plugins/pinned-rows/PinnedRowsPlugin.d.ts +1 -0
  51. package/lib/plugins/pinned-rows/PinnedRowsPlugin.d.ts.map +1 -1
  52. package/lib/plugins/pinned-rows/index.js +118 -87
  53. package/lib/plugins/pinned-rows/index.js.map +1 -1
  54. package/lib/plugins/pinned-rows/pinned-rows.d.ts +2 -1
  55. package/lib/plugins/pinned-rows/pinned-rows.d.ts.map +1 -1
  56. package/lib/plugins/pinned-rows/types.d.ts +23 -2
  57. package/lib/plugins/pinned-rows/types.d.ts.map +1 -1
  58. package/lib/plugins/pivot/index.js.map +1 -1
  59. package/lib/plugins/print/index.js.map +1 -1
  60. package/lib/plugins/reorder/index.js.map +1 -1
  61. package/lib/plugins/responsive/index.js +40 -39
  62. package/lib/plugins/responsive/index.js.map +1 -1
  63. package/lib/plugins/row-reorder/index.js.map +1 -1
  64. package/lib/plugins/selection/SelectionPlugin.d.ts +51 -0
  65. package/lib/plugins/selection/SelectionPlugin.d.ts.map +1 -1
  66. package/lib/plugins/selection/index.js +347 -145
  67. package/lib/plugins/selection/index.js.map +1 -1
  68. package/lib/plugins/selection/types.d.ts +18 -0
  69. package/lib/plugins/selection/types.d.ts.map +1 -1
  70. package/lib/plugins/server-side/index.js.map +1 -1
  71. package/lib/plugins/shared/data-collection.d.ts +33 -0
  72. package/lib/plugins/shared/data-collection.d.ts.map +1 -0
  73. package/lib/plugins/tree/index.js.map +1 -1
  74. package/lib/plugins/undo-redo/index.js.map +1 -1
  75. package/lib/plugins/visibility/index.js.map +1 -1
  76. package/package.json +1 -1
  77. package/public.d.ts +2 -0
  78. package/public.d.ts.map +1 -1
  79. package/themes/dg-theme-bootstrap.css +192 -8
  80. package/themes/dg-theme-material.css +243 -0
  81. package/umd/grid.all.umd.js +42 -42
  82. package/umd/grid.all.umd.js.map +1 -1
  83. package/umd/grid.umd.js +19 -19
  84. package/umd/grid.umd.js.map +1 -1
  85. package/umd/plugins/clipboard.umd.js +5 -5
  86. package/umd/plugins/clipboard.umd.js.map +1 -1
  87. package/umd/plugins/context-menu.umd.js +1 -1
  88. package/umd/plugins/context-menu.umd.js.map +1 -1
  89. package/umd/plugins/editing.umd.js +1 -1
  90. package/umd/plugins/editing.umd.js.map +1 -1
  91. package/umd/plugins/export.umd.js +7 -7
  92. package/umd/plugins/export.umd.js.map +1 -1
  93. package/umd/plugins/filtering.umd.js +1 -1
  94. package/umd/plugins/filtering.umd.js.map +1 -1
  95. package/umd/plugins/pinned-rows.umd.js +1 -1
  96. package/umd/plugins/pinned-rows.umd.js.map +1 -1
  97. package/umd/plugins/selection.umd.js +2 -2
  98. package/umd/plugins/selection.umd.js.map +1 -1
@@ -1,4 +1,4 @@
1
- const b = '<svg viewBox="0 0 16 16" width="12" height="12"><path fill="currentColor" d="M6 10.5a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 0 1h-3a.5.5 0 0 1-.5-.5zm-2-3a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5zm-2-3a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5z"/></svg>', x = {
1
+ const b = '<svg viewBox="0 0 16 16" width="12" height="12"><path fill="currentColor" d="M6 10.5a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 0 1h-3a.5.5 0 0 1-.5-.5zm-2-3a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5zm-2-3a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5z"/></svg>', R = {
2
2
  expand: "▶",
3
3
  collapse: "▼",
4
4
  sortAsc: "▲",
@@ -11,7 +11,7 @@ const b = '<svg viewBox="0 0 16 16" width="12" height="12"><path fill="currentCo
11
11
  filterActive: b,
12
12
  print: "🖨️"
13
13
  };
14
- class v {
14
+ class x {
15
15
  /**
16
16
  * Plugin dependencies - declare other plugins this one requires.
17
17
  *
@@ -74,7 +74,7 @@ class v {
74
74
  * Created fresh in attach(), aborted in detach().
75
75
  * This ensures event listeners are properly cleaned up when plugins are re-attached.
76
76
  */
77
- #t;
77
+ #e;
78
78
  /**
79
79
  * Default configuration - subclasses should override this getter.
80
80
  * Note: This must be a getter (not property initializer) for proper inheritance
@@ -83,8 +83,8 @@ class v {
83
83
  get defaultConfig() {
84
84
  return {};
85
85
  }
86
- constructor(t = {}) {
87
- this.userConfig = t;
86
+ constructor(e = {}) {
87
+ this.userConfig = e;
88
88
  }
89
89
  /**
90
90
  * Called when the plugin is attached to a grid.
@@ -101,8 +101,8 @@ class v {
101
101
  * }
102
102
  * ```
103
103
  */
104
- attach(t) {
105
- this.#t?.abort(), this.#t = new AbortController(), this.grid = t, this.config = { ...this.defaultConfig, ...this.userConfig };
104
+ attach(e) {
105
+ this.#e?.abort(), this.#e = new AbortController(), this.grid = e, this.config = { ...this.defaultConfig, ...this.userConfig };
106
106
  }
107
107
  /**
108
108
  * Called when the plugin is detached from a grid.
@@ -118,7 +118,7 @@ class v {
118
118
  * ```
119
119
  */
120
120
  detach() {
121
- this.#t?.abort(), this.#t = void 0;
121
+ this.#e?.abort(), this.#e = void 0;
122
122
  }
123
123
  /**
124
124
  * Get another plugin instance from the same grid.
@@ -132,22 +132,22 @@ class v {
132
132
  * }
133
133
  * ```
134
134
  */
135
- getPlugin(t) {
136
- return this.grid?.getPlugin(t);
135
+ getPlugin(e) {
136
+ return this.grid?.getPlugin(e);
137
137
  }
138
138
  /**
139
139
  * Emit a custom event from the grid.
140
140
  */
141
- emit(t, e) {
142
- this.grid?.dispatchEvent?.(new CustomEvent(t, { detail: e, bubbles: !0 }));
141
+ emit(e, t) {
142
+ this.grid?.dispatchEvent?.(new CustomEvent(e, { detail: t, bubbles: !0 }));
143
143
  }
144
144
  /**
145
145
  * Emit a cancelable custom event from the grid.
146
146
  * @returns `true` if the event was cancelled (preventDefault called), `false` otherwise
147
147
  */
148
- emitCancelable(t, e) {
149
- const o = new CustomEvent(t, { detail: e, bubbles: !0, cancelable: !0 });
150
- return this.grid?.dispatchEvent?.(o), o.defaultPrevented;
148
+ emitCancelable(e, t) {
149
+ const n = new CustomEvent(e, { detail: t, bubbles: !0, cancelable: !0 });
150
+ return this.grid?.dispatchEvent?.(n), n.defaultPrevented;
151
151
  }
152
152
  // =========================================================================
153
153
  // Event Bus - Plugin-to-Plugin Communication
@@ -168,8 +168,8 @@ class v {
168
168
  * });
169
169
  * ```
170
170
  */
171
- on(t, e) {
172
- this.grid?._pluginManager?.subscribe(this, t, e);
171
+ on(e, t) {
172
+ this.grid?._pluginManager?.subscribe(this, e, t);
173
173
  }
174
174
  /**
175
175
  * Unsubscribe from a plugin event.
@@ -182,8 +182,8 @@ class v {
182
182
  * this.off('filter-change');
183
183
  * ```
184
184
  */
185
- off(t) {
186
- this.grid?._pluginManager?.unsubscribe(this, t);
185
+ off(e) {
186
+ this.grid?._pluginManager?.unsubscribe(this, e);
187
187
  }
188
188
  /**
189
189
  * Emit an event to other plugins via the Event Bus.
@@ -203,8 +203,8 @@ class v {
203
203
  * this.emit('filter-change', { field: 'name', value: 'Alice' });
204
204
  * ```
205
205
  */
206
- emitPluginEvent(t, e) {
207
- this.grid?._pluginManager?.emitPluginEvent(t, e);
206
+ emitPluginEvent(e, t) {
207
+ this.grid?._pluginManager?.emitPluginEvent(e, t);
208
208
  }
209
209
  /**
210
210
  * Request a re-render of the grid.
@@ -293,15 +293,15 @@ class v {
293
293
  * document.addEventListener('keydown', handler, { signal: this.disconnectSignal });
294
294
  */
295
295
  get disconnectSignal() {
296
- return this.#t?.signal ?? this.grid?.disconnectSignal;
296
+ return this.#e?.signal ?? this.grid?.disconnectSignal;
297
297
  }
298
298
  /**
299
299
  * Get the grid-level icons configuration.
300
300
  * Returns merged icons (user config + defaults).
301
301
  */
302
302
  get gridIcons() {
303
- const t = this.grid?.gridConfig?.icons ?? {};
304
- return { ...x, ...t };
303
+ const e = this.grid?.gridConfig?.icons ?? {};
304
+ return { ...R, ...e };
305
305
  }
306
306
  // #region Animation Helpers
307
307
  /**
@@ -321,11 +321,11 @@ class v {
321
321
  * ```
322
322
  */
323
323
  get isAnimationEnabled() {
324
- const t = this.grid?.effectiveConfig?.animation?.mode ?? "reduced-motion";
325
- if (t === !1 || t === "off") return !1;
326
- if (t === !0 || t === "on") return !0;
327
- const e = this.gridElement;
328
- return e ? getComputedStyle(e).getPropertyValue("--tbw-animation-enabled").trim() !== "0" : !0;
324
+ const e = this.grid?.effectiveConfig?.animation?.mode ?? "reduced-motion";
325
+ if (e === !1 || e === "off") return !1;
326
+ if (e === !0 || e === "on") return !0;
327
+ const t = this.gridElement;
328
+ return t ? getComputedStyle(t).getPropertyValue("--tbw-animation-enabled").trim() !== "0" : !0;
329
329
  }
330
330
  /**
331
331
  * Get the animation duration in milliseconds from CSS variable.
@@ -340,10 +340,10 @@ class v {
340
340
  * ```
341
341
  */
342
342
  get animationDuration() {
343
- const t = this.gridElement;
344
- if (t) {
345
- const e = getComputedStyle(t).getPropertyValue("--tbw-animation-duration").trim(), o = parseInt(e, 10);
346
- if (!isNaN(o)) return o;
343
+ const e = this.gridElement;
344
+ if (e) {
345
+ const t = getComputedStyle(e).getPropertyValue("--tbw-animation-duration").trim(), n = parseInt(t, 10);
346
+ if (!isNaN(n)) return n;
347
347
  }
348
348
  return 200;
349
349
  }
@@ -356,8 +356,8 @@ class v {
356
356
  * @param pluginOverride - Optional plugin-level override
357
357
  * @returns The resolved icon value
358
358
  */
359
- resolveIcon(t, e) {
360
- return e !== void 0 ? e : this.gridIcons[t];
359
+ resolveIcon(e, t) {
360
+ return t !== void 0 ? t : this.gridIcons[e];
361
361
  }
362
362
  /**
363
363
  * Set an icon value on an element.
@@ -366,75 +366,86 @@ class v {
366
366
  * @param element - The element to set the icon on
367
367
  * @param icon - The icon value (string or HTMLElement)
368
368
  */
369
- setIcon(t, e) {
370
- typeof e == "string" ? t.innerHTML = e : e instanceof HTMLElement && (t.innerHTML = "", t.appendChild(e.cloneNode(!0)));
369
+ setIcon(e, t) {
370
+ typeof t == "string" ? e.innerHTML = t : t instanceof HTMLElement && (e.innerHTML = "", e.appendChild(t.cloneNode(!0)));
371
371
  }
372
372
  /**
373
373
  * Log a warning message.
374
374
  */
375
- warn(t) {
376
- console.warn(`[tbw-grid:${this.name}] ${t}`);
375
+ warn(e) {
376
+ console.warn(`[tbw-grid:${this.name}] ${e}`);
377
377
  }
378
378
  // #endregion
379
379
  }
380
- function E(g) {
381
- return g.meta?.utility === !0;
380
+ function C(a, e, t = !0) {
381
+ let n = a;
382
+ if (t && (n = n.filter((s) => !s.hidden && !s.field.startsWith("__") && s.meta?.utility !== !0)), e?.length) {
383
+ const s = new Set(e);
384
+ n = n.filter((r) => s.has(r.field));
385
+ }
386
+ return n;
387
+ }
388
+ function E(a, e) {
389
+ return e?.length ? [...e].sort((t, n) => t - n).map((t) => a[t]).filter((t) => t != null) : a;
382
390
  }
383
- async function R(g) {
391
+ function I(a) {
392
+ return a == null ? "" : a instanceof Date ? a.toISOString() : typeof a == "object" ? JSON.stringify(a) : String(a);
393
+ }
394
+ async function v(a) {
384
395
  try {
385
- return await navigator.clipboard.writeText(g), !0;
396
+ return await navigator.clipboard.writeText(a), !0;
386
397
  } catch {
387
- const t = document.createElement("textarea");
388
- t.value = g, t.style.position = "fixed", t.style.opacity = "0", t.style.pointerEvents = "none", document.body.appendChild(t), t.select();
389
- const e = document.execCommand("copy");
390
- return document.body.removeChild(t), e;
398
+ const e = document.createElement("textarea");
399
+ e.value = a, e.style.position = "fixed", e.style.opacity = "0", e.style.pointerEvents = "none", document.body.appendChild(e), e.select();
400
+ const t = document.execCommand("copy");
401
+ return document.body.removeChild(e), t;
391
402
  }
392
403
  }
393
- function y(g, t) {
394
- const e = t.delimiter ?? " ", o = t.newline ?? `
395
- `, l = g.replace(/\r\n/g, `
404
+ function y(a, e) {
405
+ const t = e.delimiter ?? " ", n = e.newline ?? `
406
+ `, s = a.replace(/\r\n/g, `
396
407
  `).replace(/\r/g, `
397
- `), n = [];
398
- let s = [], r = "", a = !1;
399
- for (let i = 0; i < l.length; i++) {
400
- const u = l[i];
401
- u === '"' && !a ? a = !0 : u === '"' && a ? l[i + 1] === '"' ? (r += '"', i++) : a = !1 : u === e && !a ? (s.push(r), r = "") : u === o && !a ? (s.push(r), r = "", (s.length > 1 || s.some((d) => d.trim() !== "")) && n.push(s), s = []) : r += u;
408
+ `), r = [];
409
+ let o = [], l = "", c = !1;
410
+ for (let i = 0; i < s.length; i++) {
411
+ const d = s[i];
412
+ d === '"' && !c ? c = !0 : d === '"' && c ? s[i + 1] === '"' ? (l += '"', i++) : c = !1 : d === t && !c ? (o.push(l), l = "") : d === n && !c ? (o.push(l), l = "", (o.length > 1 || o.some((u) => u.trim() !== "")) && r.push(o), o = []) : l += d;
402
413
  }
403
- return s.push(r), (s.length > 1 || s.some((i) => i.trim() !== "")) && n.push(s), n;
414
+ return o.push(l), (o.length > 1 || o.some((i) => i.trim() !== "")) && r.push(o), r;
404
415
  }
405
- async function I() {
416
+ async function S() {
406
417
  try {
407
418
  return await navigator.clipboard.readText();
408
419
  } catch {
409
420
  return "";
410
421
  }
411
422
  }
412
- function _(g, t) {
413
- const { rows: e, target: o, fields: l } = g;
414
- if (!o) return;
415
- const n = t.rows, s = t.effectiveConfig.columns ?? [], r = s.map((d) => d.field), a = /* @__PURE__ */ new Map();
416
- s.forEach((d) => {
417
- a.set(d.field, d.editable === !0);
423
+ function _(a, e) {
424
+ const { rows: t, target: n, fields: s } = a;
425
+ if (!n) return;
426
+ const r = e.rows, o = e.effectiveConfig.columns ?? [], l = o.map((u) => u.field), c = /* @__PURE__ */ new Map();
427
+ o.forEach((u) => {
428
+ c.set(u.field, u.editable === !0);
418
429
  });
419
- const i = [...n], u = o.bounds ? o.bounds.endRow : 1 / 0;
420
- e.forEach((d, w) => {
421
- const h = o.row + w;
422
- if (!(h > u)) {
423
- if (o.bounds) {
424
- if (h >= i.length)
430
+ const i = [...r], d = n.bounds ? n.bounds.endRow : 1 / 0;
431
+ t.forEach((u, g) => {
432
+ const f = n.row + g;
433
+ if (!(f > d)) {
434
+ if (n.bounds) {
435
+ if (f >= i.length)
425
436
  return;
426
- } else for (; h >= i.length; ) {
427
- const c = {};
428
- r.forEach((f) => c[f] = ""), i.push(c);
437
+ } else for (; f >= i.length; ) {
438
+ const m = {};
439
+ l.forEach((p) => m[p] = ""), i.push(m);
429
440
  }
430
- i[h] = { ...i[h] }, d.forEach((c, f) => {
431
- const m = l[f];
432
- m && a.get(m) && (i[h][m] = c);
441
+ i[f] = { ...i[f] }, u.forEach((m, p) => {
442
+ const h = s[p];
443
+ h && c.get(h) && (i[f][h] = m);
433
444
  });
434
445
  }
435
- }), t.rows = i;
446
+ }), e.rows = i;
436
447
  }
437
- class D extends v {
448
+ class M extends x {
438
449
  /**
439
450
  * Plugin dependencies - ClipboardPlugin works best with SelectionPlugin.
440
451
  *
@@ -463,10 +474,10 @@ class D extends v {
463
474
  // #endregion
464
475
  // #region Lifecycle
465
476
  /** @internal */
466
- attach(t) {
467
- super.attach(t), t.addEventListener(
477
+ attach(e) {
478
+ super.attach(e), e.addEventListener(
468
479
  "paste",
469
- (e) => this.#o(e),
480
+ (t) => this.#r(t),
470
481
  { signal: this.disconnectSignal }
471
482
  );
472
483
  }
@@ -477,51 +488,28 @@ class D extends v {
477
488
  // #endregion
478
489
  // #region Event Handlers
479
490
  /** @internal */
480
- onKeyDown(t) {
481
- return (t.ctrlKey || t.metaKey) && t.key === "c" ? (this.#t(t.target), !0) : !1;
491
+ onKeyDown(e) {
492
+ return (e.ctrlKey || e.metaKey) && e.key === "c" ? (this.#e(e.target), !0) : !1;
482
493
  }
483
494
  // #endregion
484
495
  // #region Private Methods
485
496
  /**
486
- * Handle copy operation.
497
+ * Handle copy operation from keyboard shortcut.
487
498
  *
488
- * Everything is treated as a range:
489
- * - With selection: copies the selected range
490
- * - Row mode: range spanning all columns for selected rows
491
- * - No selection plugin: entire grid as a range
492
- * - No selection: try to get focused cell from DOM as 1x1 range
493
- */
494
- #t(t) {
495
- const e = this.#e(), o = this.columns.length - 1, l = this.rows.length - 1;
496
- let n;
497
- if (e && e.ranges.length > 0) {
498
- const { mode: r, ranges: a } = e, i = a[a.length - 1];
499
- r === "row" ? n = {
500
- startRow: i.from.row,
501
- startCol: 0,
502
- endRow: i.to.row,
503
- endCol: o
504
- } : n = {
505
- startRow: i.from.row,
506
- startCol: i.from.col,
507
- endRow: i.to.row,
508
- endCol: i.to.col
509
- };
510
- } else if (!e)
511
- n = { startRow: 0, startCol: 0, endRow: l, endCol: o };
512
- else {
513
- const r = this.#r(t);
514
- if (!r) return;
515
- n = { startRow: r.row, startCol: r.col, endRow: r.row, endCol: r.col };
499
+ * For keyboard-triggered copies, respects the current selection or
500
+ * falls back to the focused cell from the DOM.
501
+ */
502
+ #e(e) {
503
+ const t = this.#t();
504
+ if (t && t.ranges.length === 0) {
505
+ const n = this.#o(e);
506
+ if (!n) return;
507
+ const s = this.columns[n.col];
508
+ if (!s) return;
509
+ this.copy({ rowIndices: [n.row], columns: [s.field] });
510
+ return;
516
511
  }
517
- const s = this.#n(n);
518
- R(s.text).then(() => {
519
- this.lastCopied = { text: s.text, timestamp: Date.now() }, this.emit("copy", {
520
- text: s.text,
521
- rowCount: s.rowCount,
522
- columnCount: s.columnCount
523
- });
524
- });
512
+ this.copy();
525
513
  }
526
514
  /**
527
515
  * Handle native paste event (preferred method - works in iframes).
@@ -538,17 +526,17 @@ class D extends v {
538
526
  * - Range/row: paste is clipped to fit within selection bounds
539
527
  * - No selection: paste starts at row 0, col 0
540
528
  */
541
- #o(t) {
542
- const e = t.clipboardData?.getData("text/plain");
543
- if (!e) return;
544
- t.preventDefault();
545
- const o = y(e, this.config), l = this.#e(), n = l?.ranges?.[0], s = n?.from.row ?? 0, r = n?.from.col ?? 0, i = n && (l?.mode === "range" || l?.mode === "row") && (n.from.row !== n.to.row || n.from.col !== n.to.col) ? { endRow: n.to.row, endCol: n.to.col } : null, u = i?.endCol ?? this.columns.length - 1, d = this.columns[r], w = d ? { row: s, col: r, field: d.field, bounds: i } : null, h = [], c = o[0]?.length ?? 0;
546
- for (let m = 0; m < c && r + m <= u; m++) {
547
- const p = this.columns[r + m];
548
- p && !p.hidden && h.push(p.field);
529
+ #r(e) {
530
+ const t = e.clipboardData?.getData("text/plain");
531
+ if (!t) return;
532
+ e.preventDefault();
533
+ const n = y(t, this.config), s = this.#t(), r = s?.ranges?.[0], o = r?.from.row ?? 0, l = r?.from.col ?? 0, i = r && (s?.mode === "range" || s?.mode === "row") && (r.from.row !== r.to.row || r.from.col !== r.to.col) ? { endRow: r.to.row, endCol: r.to.col } : null, d = i?.endCol ?? this.columns.length - 1, u = this.columns[l], g = u ? { row: o, col: l, field: u.field, bounds: i } : null, f = [], m = n[0]?.length ?? 0;
534
+ for (let h = 0; h < m && l + h <= d; h++) {
535
+ const w = this.columns[l + h];
536
+ w && !w.hidden && f.push(w.field);
549
537
  }
550
- const f = { rows: o, text: e, target: w, fields: h };
551
- this.emit("paste", f), this.#s(f);
538
+ const p = { rows: n, text: t, target: g, fields: f };
539
+ this.emit("paste", p), this.#i(p);
552
540
  }
553
541
  /**
554
542
  * Apply the paste handler to update grid data.
@@ -556,102 +544,179 @@ class D extends v {
556
544
  * Uses the configured `pasteHandler`, or the default handler if not specified.
557
545
  * Set `pasteHandler: null` in config to disable auto-paste.
558
546
  */
559
- #s(t) {
547
+ #i(e) {
560
548
  if (!this.grid) return;
561
- const { pasteHandler: e } = this.config;
562
- if (e === null) return;
563
- (e ?? _)(t, this.grid);
549
+ const { pasteHandler: t } = this.config;
550
+ if (t === null) return;
551
+ (t ?? _)(e, this.grid);
564
552
  }
565
553
  /**
566
554
  * Get the current selection via Query System.
567
555
  * Returns undefined if no selection plugin is loaded or nothing is selected.
568
556
  */
569
- #e() {
570
- return this.grid?.query("getSelection", void 0)?.[0];
557
+ #t() {
558
+ return this.grid?.query("getSelection")?.[0];
571
559
  }
572
560
  /**
573
- * Build text for a rectangular range of cells.
574
- * Utility columns (like expander columns) are automatically excluded.
561
+ * Resolve columns and rows to include based on options and/or current selection.
562
+ *
563
+ * Priority for columns:
564
+ * 1. `options.columns` (explicit field list)
565
+ * 2. Selection range column bounds (range/cell mode only)
566
+ * 3. All visible non-utility columns
567
+ *
568
+ * Priority for rows:
569
+ * 1. `options.rowIndices` (explicit indices)
570
+ * 2. Selection range row bounds
571
+ * 3. All rows
575
572
  */
576
- #n(t) {
577
- const { startRow: e, startCol: o, endRow: l, endCol: n } = t, s = Math.min(e, l), r = Math.max(e, l), a = Math.min(o, n), i = Math.max(o, n), u = this.config.delimiter ?? " ", d = this.config.newline ?? `
578
- `, w = [], h = this.columns.slice(a, i + 1).filter((c) => !E(c));
579
- if (this.config.includeHeaders) {
580
- const c = h.map((f) => f.header || f.field);
581
- w.push(c.join(u));
582
- }
583
- for (let c = s; c <= r; c++) {
584
- const f = this.rows[c];
585
- if (!f) continue;
586
- const m = h.map((p) => {
587
- const C = f[p.field];
588
- return C == null ? "" : C instanceof Date ? C.toISOString() : String(C);
573
+ #n(e) {
574
+ const t = this.#t();
575
+ let n;
576
+ if (e?.columns)
577
+ n = C(this.columns, e.columns);
578
+ else if (t?.ranges.length && t.mode !== "row") {
579
+ const r = t.ranges[t.ranges.length - 1], o = Math.min(r.from.col, r.to.col), l = Math.max(r.from.col, r.to.col);
580
+ n = C(this.columns.slice(o, l + 1));
581
+ } else
582
+ n = C(this.columns);
583
+ let s;
584
+ if (e?.rowIndices)
585
+ s = E(this.rows, e.rowIndices);
586
+ else if (t?.ranges.length) {
587
+ const r = t.ranges[t.ranges.length - 1], o = Math.min(r.from.row, r.to.row), l = Math.max(r.from.row, r.to.row);
588
+ s = [];
589
+ for (let c = o; c <= l; c++) {
590
+ const i = this.rows[c];
591
+ i && s.push(i);
592
+ }
593
+ } else
594
+ s = this.rows;
595
+ return { columns: n, rows: s };
596
+ }
597
+ /**
598
+ * Build delimited text from resolved columns and rows.
599
+ */
600
+ #s(e, t, n) {
601
+ const s = n?.delimiter ?? this.config.delimiter ?? " ", r = n?.newline ?? this.config.newline ?? `
602
+ `, o = n?.includeHeaders ?? this.config.includeHeaders ?? !1, l = n?.processCell ?? this.config.processCell, c = [];
603
+ o && c.push(e.map((i) => i.header || i.field).join(s));
604
+ for (const i of t) {
605
+ const d = e.map((u) => {
606
+ const g = i[u.field];
607
+ return l ? l(g, u.field, i) : I(g);
589
608
  });
590
- w.push(m.join(u));
609
+ c.push(d.join(s));
591
610
  }
592
- return {
593
- text: w.join(d),
594
- rowCount: r - s + 1,
595
- columnCount: i - a + 1
596
- };
611
+ return c.join(r);
597
612
  }
598
613
  /**
599
614
  * Get focused cell coordinates from DOM.
600
615
  * Used as fallback when SelectionPlugin has no selection.
601
616
  */
602
- #r(t) {
603
- const e = t.closest("[data-field-cache]");
604
- if (!e) return null;
605
- const o = e.dataset.fieldCache, l = e.dataset.row;
606
- if (!o || !l) return null;
607
- const n = parseInt(l, 10);
608
- if (isNaN(n)) return null;
609
- const s = this.columns.findIndex((r) => r.field === o);
610
- return s === -1 ? null : { row: n, col: s };
617
+ #o(e) {
618
+ const t = e.closest("[data-field-cache]");
619
+ if (!t) return null;
620
+ const n = t.dataset.fieldCache, s = t.dataset.row;
621
+ if (!n || !s) return null;
622
+ const r = parseInt(s, 10);
623
+ if (isNaN(r)) return null;
624
+ const o = this.columns.findIndex((l) => l.field === n);
625
+ return o === -1 ? null : { row: r, col: o };
611
626
  }
612
627
  // #endregion
613
628
  // #region Public API
614
629
  /**
615
- * Copy currently selected rows to clipboard.
630
+ * Get the text representation of the current selection (or specified data)
631
+ * without writing to the system clipboard.
632
+ *
633
+ * Useful for previewing what would be copied, or for feeding the text into
634
+ * a custom UI (e.g., a "copy with column picker" dialog).
635
+ *
636
+ * @param options - Control which columns/rows to include
637
+ * @returns Delimited text, or empty string if nothing to copy
638
+ *
639
+ * @example Get text for specific columns
640
+ * ```ts
641
+ * const clipboard = grid.getPlugin(ClipboardPlugin);
642
+ * const text = clipboard.getSelectionAsText({
643
+ * columns: ['name', 'email'],
644
+ * includeHeaders: true,
645
+ * });
646
+ * console.log(text);
647
+ * // "Name\tEmail\nAlice\talice@example.com\n..."
648
+ * ```
649
+ */
650
+ getSelectionAsText(e) {
651
+ const { columns: t, rows: n } = this.#n(e);
652
+ return t.length === 0 || n.length === 0 ? "" : this.#s(t, n, e);
653
+ }
654
+ /**
655
+ * Copy data to the system clipboard.
656
+ *
657
+ * Without options, copies the current selection (or entire grid if no selection).
658
+ * With options, copies exactly the specified columns and/or rows — ideal for
659
+ * "copy with column picker" workflows where the user selects rows first,
660
+ * then chooses which columns to include via a dialog.
661
+ *
662
+ * @param options - Control which columns/rows to include
616
663
  * @returns The copied text
664
+ *
665
+ * @example Copy current selection (default)
666
+ * ```ts
667
+ * const clipboard = grid.getPlugin(ClipboardPlugin);
668
+ * await clipboard.copy();
669
+ * ```
670
+ *
671
+ * @example Copy specific columns from specific rows
672
+ * ```ts
673
+ * // User selected rows in the grid, then picked columns in a dialog
674
+ * const selectedRowIndices = [0, 3, 7];
675
+ * const chosenColumns = ['name', 'department', 'salary'];
676
+ * await clipboard.copy({
677
+ * rowIndices: selectedRowIndices,
678
+ * columns: chosenColumns,
679
+ * includeHeaders: true,
680
+ * });
681
+ * ```
617
682
  */
618
- async copy() {
619
- const t = this.#e(), e = this.columns.length - 1;
620
- let o = { startRow: 0, startCol: 0, endRow: this.rows.length - 1, endCol: e };
621
- if (t && t.ranges.length > 0) {
622
- const n = t.ranges[t.ranges.length - 1];
623
- t.mode === "row" ? o = { startRow: n.from.row, startCol: 0, endRow: n.to.row, endCol: e } : o = {
624
- startRow: n.from.row,
625
- startCol: n.from.col,
626
- endRow: n.to.row,
627
- endCol: n.to.col
628
- };
629
- }
630
- const l = this.#n(o);
631
- return await R(l.text), this.lastCopied = { text: l.text, timestamp: Date.now() }, l.text;
683
+ async copy(e) {
684
+ const { columns: t, rows: n } = this.#n(e);
685
+ if (t.length === 0 || n.length === 0) return "";
686
+ const s = this.#s(t, n, e);
687
+ return await v(s), this.lastCopied = { text: s, timestamp: Date.now() }, this.emit("copy", {
688
+ text: s,
689
+ rowCount: n.length,
690
+ columnCount: t.length
691
+ }), s;
632
692
  }
633
693
  /**
634
694
  * Copy specific rows by index to clipboard.
695
+ *
696
+ * Convenience wrapper around {@link copy} for row-based workflows.
697
+ * Supports non-contiguous row indices (e.g., `[0, 3, 7]`).
698
+ *
635
699
  * @param indices - Array of row indices to copy
700
+ * @param options - Additional copy options (columns, headers, etc.)
636
701
  * @returns The copied text
702
+ *
703
+ * @example
704
+ * ```ts
705
+ * const clipboard = grid.getPlugin(ClipboardPlugin);
706
+ * // Copy only rows 0 and 5, including just name and email columns
707
+ * await clipboard.copyRows([0, 5], { columns: ['name', 'email'] });
708
+ * ```
637
709
  */
638
- async copyRows(t) {
639
- if (t.length === 0) return "";
640
- const e = [...t].sort((s, r) => s - r), o = this.columns.length - 1, l = {
641
- startRow: e[0],
642
- startCol: 0,
643
- endRow: e[e.length - 1],
644
- endCol: o
645
- }, n = this.#n(l);
646
- return await R(n.text), this.lastCopied = { text: n.text, timestamp: Date.now() }, n.text;
710
+ async copyRows(e, t) {
711
+ return e.length === 0 ? "" : this.copy({ ...t, rowIndices: e });
647
712
  }
648
713
  /**
649
714
  * Read and parse clipboard content.
650
715
  * @returns Parsed 2D array of cell values, or null if clipboard is empty
651
716
  */
652
717
  async paste() {
653
- const t = await I();
654
- return t ? y(t, this.config) : null;
718
+ const e = await S();
719
+ return e ? y(e, this.config) : null;
655
720
  }
656
721
  /**
657
722
  * Get the last copied text and timestamp.
@@ -663,7 +728,7 @@ class D extends v {
663
728
  // #endregion
664
729
  }
665
730
  export {
666
- D as ClipboardPlugin,
731
+ M as ClipboardPlugin,
667
732
  _ as defaultPasteHandler
668
733
  };
669
734
  //# sourceMappingURL=index.js.map