@toolbox-web/grid 1.12.0 → 1.13.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 (75) hide show
  1. package/all.js +694 -525
  2. package/all.js.map +1 -1
  3. package/index.js +1468 -1449
  4. package/index.js.map +1 -1
  5. package/lib/core/grid.d.ts +2 -1
  6. package/lib/core/grid.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/types.d.ts +9 -1
  10. package/lib/core/types.d.ts.map +1 -1
  11. package/lib/plugins/clipboard/index.js.map +1 -1
  12. package/lib/plugins/column-virtualization/index.js.map +1 -1
  13. package/lib/plugins/context-menu/ContextMenuPlugin.d.ts.map +1 -1
  14. package/lib/plugins/context-menu/index.js +1 -1
  15. package/lib/plugins/context-menu/index.js.map +1 -1
  16. package/lib/plugins/editing/EditingPlugin.d.ts.map +1 -1
  17. package/lib/plugins/editing/editors.d.ts.map +1 -1
  18. package/lib/plugins/editing/index.d.ts +1 -1
  19. package/lib/plugins/editing/index.d.ts.map +1 -1
  20. package/lib/plugins/editing/index.js +187 -170
  21. package/lib/plugins/editing/index.js.map +1 -1
  22. package/lib/plugins/editing/types.d.ts +44 -0
  23. package/lib/plugins/editing/types.d.ts.map +1 -1
  24. package/lib/plugins/export/index.js.map +1 -1
  25. package/lib/plugins/filtering/index.js +9 -9
  26. package/lib/plugins/filtering/index.js.map +1 -1
  27. package/lib/plugins/grouping-columns/GroupingColumnsPlugin.d.ts +8 -1
  28. package/lib/plugins/grouping-columns/GroupingColumnsPlugin.d.ts.map +1 -1
  29. package/lib/plugins/grouping-columns/index.js +59 -60
  30. package/lib/plugins/grouping-columns/index.js.map +1 -1
  31. package/lib/plugins/grouping-rows/index.js.map +1 -1
  32. package/lib/plugins/master-detail/MasterDetailPlugin.d.ts +7 -0
  33. package/lib/plugins/master-detail/MasterDetailPlugin.d.ts.map +1 -1
  34. package/lib/plugins/master-detail/index.js +185 -145
  35. package/lib/plugins/master-detail/index.js.map +1 -1
  36. package/lib/plugins/multi-sort/index.js.map +1 -1
  37. package/lib/plugins/pinned-columns/index.js.map +1 -1
  38. package/lib/plugins/pinned-rows/index.js.map +1 -1
  39. package/lib/plugins/pivot/index.js.map +1 -1
  40. package/lib/plugins/print/index.js.map +1 -1
  41. package/lib/plugins/reorder/index.js.map +1 -1
  42. package/lib/plugins/responsive/index.js +40 -39
  43. package/lib/plugins/responsive/index.js.map +1 -1
  44. package/lib/plugins/row-reorder/index.js.map +1 -1
  45. package/lib/plugins/selection/SelectionPlugin.d.ts +51 -0
  46. package/lib/plugins/selection/SelectionPlugin.d.ts.map +1 -1
  47. package/lib/plugins/selection/index.js +325 -131
  48. package/lib/plugins/selection/index.js.map +1 -1
  49. package/lib/plugins/selection/types.d.ts +18 -0
  50. package/lib/plugins/selection/types.d.ts.map +1 -1
  51. package/lib/plugins/server-side/index.js.map +1 -1
  52. package/lib/plugins/tree/index.js.map +1 -1
  53. package/lib/plugins/undo-redo/index.js.map +1 -1
  54. package/lib/plugins/visibility/index.js.map +1 -1
  55. package/package.json +1 -1
  56. package/public.d.ts +2 -0
  57. package/public.d.ts.map +1 -1
  58. package/themes/dg-theme-bootstrap.css +192 -8
  59. package/themes/dg-theme-material.css +243 -0
  60. package/umd/grid.all.umd.js +43 -43
  61. package/umd/grid.all.umd.js.map +1 -1
  62. package/umd/grid.umd.js +19 -19
  63. package/umd/grid.umd.js.map +1 -1
  64. package/umd/plugins/context-menu.umd.js +1 -1
  65. package/umd/plugins/context-menu.umd.js.map +1 -1
  66. package/umd/plugins/editing.umd.js +1 -1
  67. package/umd/plugins/editing.umd.js.map +1 -1
  68. package/umd/plugins/filtering.umd.js +1 -1
  69. package/umd/plugins/filtering.umd.js.map +1 -1
  70. package/umd/plugins/grouping-columns.umd.js +1 -1
  71. package/umd/plugins/grouping-columns.umd.js.map +1 -1
  72. package/umd/plugins/master-detail.umd.js +1 -1
  73. package/umd/plugins/master-detail.umd.js.map +1 -1
  74. package/umd/plugins/selection.umd.js +2 -2
  75. package/umd/plugins/selection.umd.js.map +1 -1
@@ -1,8 +1,8 @@
1
- function S(r) {
2
- if (!r) return -1;
3
- const e = r.getAttribute("data-row");
1
+ function R(n) {
2
+ if (!n) return -1;
3
+ const e = n.getAttribute("data-row");
4
4
  if (e) return parseInt(e, 10);
5
- const t = r.closest(".data-grid-row");
5
+ const t = n.closest(".data-grid-row");
6
6
  if (!t) return -1;
7
7
  const s = t.parentElement;
8
8
  if (!s) return -1;
@@ -11,10 +11,10 @@ function S(r) {
11
11
  if (i[l] === t) return l;
12
12
  return -1;
13
13
  }
14
- function m(r) {
15
- r && r.querySelectorAll(".cell-focus").forEach((e) => e.classList.remove("cell-focus"));
14
+ function C(n) {
15
+ n && n.querySelectorAll(".cell-focus").forEach((e) => e.classList.remove("cell-focus"));
16
16
  }
17
- const R = '<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>', y = {
17
+ const p = '<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>', A = {
18
18
  expand: "▶",
19
19
  collapse: "▼",
20
20
  sortAsc: "▲",
@@ -23,11 +23,11 @@ const R = '<svg viewBox="0 0 16 16" width="12" height="12"><path fill="currentCo
23
23
  submenuArrow: "▶",
24
24
  dragHandle: "⋮⋮",
25
25
  toolPanel: "☰",
26
- filter: R,
27
- filterActive: R,
26
+ filter: p,
27
+ filterActive: p,
28
28
  print: "🖨️"
29
29
  };
30
- class v {
30
+ class k {
31
31
  /**
32
32
  * Plugin dependencies - declare other plugins this one requires.
33
33
  *
@@ -317,7 +317,7 @@ class v {
317
317
  */
318
318
  get gridIcons() {
319
319
  const e = this.grid?.gridConfig?.icons ?? {};
320
- return { ...y, ...e };
320
+ return { ...A, ...e };
321
321
  }
322
322
  // #region Animation Helpers
323
323
  /**
@@ -393,65 +393,69 @@ class v {
393
393
  }
394
394
  // #endregion
395
395
  }
396
- function g(r) {
397
- return r.meta?.utility === !0;
396
+ const E = "__tbw_expander";
397
+ function I(n) {
398
+ return n.field === E;
398
399
  }
399
- function u(r) {
400
+ function f(n) {
401
+ return n.meta?.utility === !0;
402
+ }
403
+ function g(n) {
400
404
  return {
401
- startRow: Math.min(r.startRow, r.endRow),
402
- startCol: Math.min(r.startCol, r.endCol),
403
- endRow: Math.max(r.startRow, r.endRow),
404
- endCol: Math.max(r.startCol, r.endCol)
405
+ startRow: Math.min(n.startRow, n.endRow),
406
+ startCol: Math.min(n.startCol, n.endCol),
407
+ endRow: Math.max(n.startRow, n.endRow),
408
+ endCol: Math.max(n.startCol, n.endCol)
405
409
  };
406
410
  }
407
- function E(r) {
408
- const e = u(r);
411
+ function v(n) {
412
+ const e = g(n);
409
413
  return {
410
414
  from: { row: e.startRow, col: e.startCol },
411
415
  to: { row: e.endRow, col: e.endCol }
412
416
  };
413
417
  }
414
- function p(r) {
415
- return r.map(E);
418
+ function y(n) {
419
+ return n.map(v);
416
420
  }
417
- function I(r, e, t) {
418
- const s = u(t);
419
- return r >= s.startRow && r <= s.endRow && e >= s.startCol && e <= s.endCol;
421
+ function _(n, e, t) {
422
+ const s = g(t);
423
+ return n >= s.startRow && n <= s.endRow && e >= s.startCol && e <= s.endCol;
420
424
  }
421
- function C(r, e, t) {
422
- return t.some((s) => I(r, e, s));
425
+ function q(n, e, t) {
426
+ return t.some((s) => _(n, e, s));
423
427
  }
424
- function x(r) {
425
- const e = [], t = u(r);
428
+ function K(n) {
429
+ const e = [], t = g(n);
426
430
  for (let s = t.startRow; s <= t.endRow; s++)
427
431
  for (let i = t.startCol; i <= t.endCol; i++)
428
432
  e.push({ row: s, col: i });
429
433
  return e;
430
434
  }
431
- function q(r) {
435
+ function M(n) {
432
436
  const e = /* @__PURE__ */ new Map();
433
- for (const t of r)
434
- for (const s of x(t))
437
+ for (const t of n)
438
+ for (const s of K(t))
435
439
  e.set(`${s.row},${s.col}`, s);
436
440
  return [...e.values()];
437
441
  }
438
- function b(r, e) {
442
+ function m(n, e) {
439
443
  return {
440
- startRow: r.row,
441
- startCol: r.col,
444
+ startRow: n.row,
445
+ startCol: n.col,
442
446
  endRow: e.row,
443
447
  endCol: e.col
444
448
  };
445
449
  }
446
- function w(r, e) {
447
- const t = u(r), s = u(e);
450
+ function b(n, e) {
451
+ const t = g(n), s = g(e);
448
452
  return t.startRow === s.startRow && t.startCol === s.startCol && t.endRow === s.endRow && t.endCol === s.endCol;
449
453
  }
450
- const k = "@layer tbw-plugins{tbw-grid.selecting .data-grid-row>.cell{-webkit-user-select:none;user-select:none}tbw-grid:has(.selection){-webkit-user-select:none;user-select:none}tbw-grid[data-has-focus] .data-grid-row.row-focus{background-color:var(--tbw-focus-background, rgba(from var(--tbw-color-accent) r g b / 12%))}tbw-grid[data-selection-mode=row] .cell-focus{outline:none}tbw-grid .data-grid-row>.cell.selected{background-color:var(--tbw-range-selection-bg)}tbw-grid .data-grid-row>.cell.selected.top{border-top:2px solid var(--tbw-range-border-color)}tbw-grid .data-grid-row>.cell.selected.bottom{border-bottom:2px solid var(--tbw-range-border-color)}tbw-grid .data-grid-row>.cell.selected.first{border-left:2px solid var(--tbw-range-border-color)}tbw-grid .data-grid-row>.cell.selected.last{border-right:2px solid var(--tbw-range-border-color)}tbw-grid .data-grid-row[data-selectable=false]{cursor:not-allowed;opacity:.6}tbw-grid .data-grid-row[data-selectable=false].row-focus{background-color:var(--tbw-color-row-alt)}tbw-grid .data-grid-row>.cell[data-selectable=false]{cursor:not-allowed;opacity:.6}tbw-grid .data-grid-row>.cell[data-selectable=false].selected{background-color:var(--tbw-selection-warning-bg, rgba(from var(--tbw-color-error) r g b / 50%))}tbw-grid .tbw-selection-summary{font-size:var(--tbw-font-size-sm, .8125rem);color:var(--tbw-color-fg-muted);white-space:nowrap}}";
451
- function _(r, e, t) {
452
- if (r === "cell" && e.selectedCell)
454
+ const F = '@layer tbw-plugins{tbw-grid.selecting .data-grid-row>.cell{-webkit-user-select:none;user-select:none}tbw-grid:has(.selection){-webkit-user-select:none;user-select:none}tbw-grid[data-has-focus] .data-grid-row.row-focus{background-color:var(--tbw-focus-background, rgba(from var(--tbw-color-accent) r g b / 12%));outline:none;position:relative}tbw-grid[data-has-focus] .data-grid-row.row-focus:after{content:"";position:absolute;inset:0;pointer-events:none;border:0 solid var(--tbw-range-border-color, var(--tbw-color-accent));border-top-width:2px;border-bottom-width:2px;z-index:1}tbw-grid[data-has-focus] .data-grid-row.row-focus+.data-grid-row.row-focus:after{border-top-width:0}tbw-grid[data-has-focus] .data-grid-row.row-focus:has(+.data-grid-row.row-focus):after{border-bottom-width:0}tbw-grid[data-selection-mode=row] .cell-focus,tbw-grid[data-selection-mode=row] .row-focus,tbw-grid[data-selection-mode=range] .cell-focus{outline:none}tbw-grid .data-grid-row>.cell.selected{background-color:var(--tbw-range-selection-bg);position:relative}tbw-grid .data-grid-row>.cell.selected:after{content:"";position:absolute;inset:0;pointer-events:none;border:0 solid var(--tbw-range-border-color);z-index:1}tbw-grid .data-grid-row>.cell.selected.top:after{border-top-width:2px}tbw-grid .data-grid-row>.cell.selected.bottom:after{border-bottom-width:2px}tbw-grid .data-grid-row>.cell.selected.first:after{border-left-width:2px}tbw-grid .data-grid-row>.cell.selected.last:after{border-right-width:2px}tbw-grid .data-grid-row[data-selectable=false]{cursor:not-allowed;opacity:.6}tbw-grid .data-grid-row[data-selectable=false].row-focus{background-color:var(--tbw-color-row-alt)}tbw-grid .data-grid-row>.cell[data-selectable=false]{cursor:not-allowed;opacity:.6}tbw-grid .data-grid-row>.cell[data-selectable=false].selected{background-color:var(--tbw-selection-warning-bg, rgba(from var(--tbw-color-error) r g b / 50%))}tbw-grid .tbw-selection-summary{font-size:var(--tbw-font-size-sm, .8125rem);color:var(--tbw-color-fg-muted);white-space:nowrap}tbw-grid .data-grid-row>.cell[data-field=__tbw_checkbox],tbw-grid .header-row>.cell[data-field=__tbw_checkbox]{text-align:center;cursor:pointer;padding:0;display:flex;align-items:center;justify-content:center}tbw-grid .tbw-select-row-checkbox{pointer-events:none;margin:0;cursor:pointer}tbw-grid .tbw-checkbox-header{display:flex;justify-content:center;align-items:center;height:100%}tbw-grid .tbw-select-all-checkbox{margin:0;cursor:pointer}}', S = "__tbw_checkbox";
455
+ function D(n, e, t) {
456
+ if (n === "cell" && e.selectedCell)
453
457
  return {
454
- mode: r,
458
+ mode: n,
455
459
  ranges: [
456
460
  {
457
461
  from: { row: e.selectedCell.row, col: e.selectedCell.col },
@@ -459,16 +463,16 @@ function _(r, e, t) {
459
463
  }
460
464
  ]
461
465
  };
462
- if (r === "row" && e.selected.size > 0) {
463
- const s = [...e.selected].map((i) => ({
464
- from: { row: i, col: 0 },
465
- to: { row: i, col: t - 1 }
466
- }));
467
- return { mode: r, ranges: s };
468
- }
469
- return r === "range" && e.ranges.length > 0 ? { mode: r, ranges: p(e.ranges) } : { mode: r, ranges: [] };
466
+ if (n === "row" && e.selected.size > 0) {
467
+ const s = [...e.selected].sort((o, h) => o - h), i = [];
468
+ let l = s[0], r = l;
469
+ for (let o = 1; o < s.length; o++)
470
+ s[o] === r + 1 ? r = s[o] : (i.push({ from: { row: l, col: 0 }, to: { row: r, col: t - 1 } }), l = s[o], r = l);
471
+ return i.push({ from: { row: l, col: 0 }, to: { row: r, col: t - 1 } }), { mode: n, ranges: i };
472
+ }
473
+ return n === "range" && e.ranges.length > 0 ? { mode: n, ranges: y(e.ranges) } : { mode: n, ranges: [] };
470
474
  }
471
- class D extends v {
475
+ class L extends k {
472
476
  /**
473
477
  * Plugin manifest - declares queries and configuration validation rules.
474
478
  * @internal
@@ -489,7 +493,7 @@ class D extends v {
489
493
  /** @internal */
490
494
  name = "selection";
491
495
  /** @internal */
492
- styles = k;
496
+ styles = F;
493
497
  /** @internal */
494
498
  get defaultConfig() {
495
499
  return {
@@ -512,6 +516,12 @@ class D extends v {
512
516
  pendingKeyboardUpdate = null;
513
517
  /** Cell selection state (cell mode) */
514
518
  selectedCell = null;
519
+ /** Last synced focus row — used to detect when grid focus moves so selection follows */
520
+ lastSyncedFocusRow = -1;
521
+ /** Last synced focus col (cell mode) */
522
+ lastSyncedFocusCol = -1;
523
+ /** True when selection was explicitly set (click/keyboard) — prevents #syncSelectionToFocus from overwriting */
524
+ explicitSelection = !1;
515
525
  // #endregion
516
526
  // #region Private Helpers - Selection Enabled Check
517
527
  /**
@@ -563,42 +573,58 @@ class D extends v {
563
573
  }
564
574
  /** @internal */
565
575
  detach() {
566
- this.selected.clear(), this.ranges = [], this.activeRange = null, this.cellAnchor = null, this.isDragging = !1, this.selectedCell = null, this.pendingKeyboardUpdate = null;
576
+ this.selected.clear(), this.ranges = [], this.activeRange = null, this.cellAnchor = null, this.isDragging = !1, this.selectedCell = null, this.pendingKeyboardUpdate = null, this.lastSyncedFocusRow = -1, this.lastSyncedFocusCol = -1;
567
577
  }
568
578
  /**
569
579
  * Clear selection without emitting an event.
570
580
  * Used when selection is invalidated by external changes (filtering, grouping, etc.)
571
581
  */
572
582
  clearSelectionSilent() {
573
- this.selected.clear(), this.ranges = [], this.activeRange = null, this.cellAnchor = null, this.selectedCell = null, this.lastSelected = null, this.anchor = null, this.requestAfterRender();
583
+ this.selected.clear(), this.ranges = [], this.activeRange = null, this.cellAnchor = null, this.selectedCell = null, this.lastSelected = null, this.anchor = null, this.lastSyncedFocusRow = -1, this.lastSyncedFocusCol = -1, this.requestAfterRender();
574
584
  }
575
585
  // #endregion
576
586
  // #region Event Handlers
577
587
  /** @internal */
578
588
  onCellClick(e) {
579
589
  if (!this.isSelectionEnabled()) return !1;
580
- const { rowIndex: t, colIndex: s, originalEvent: i } = e, { mode: l, triggerOn: n = "click" } = this.config;
581
- if (i.type !== n)
590
+ const { rowIndex: t, colIndex: s, originalEvent: i } = e, { mode: l, triggerOn: r = "click" } = this.config;
591
+ if (i.type !== r)
582
592
  return !1;
583
- const o = this.columns[s], d = o && g(o);
593
+ const o = this.columns[s], h = o && f(o);
584
594
  if (l === "cell") {
585
- if (d || !this.isCellSelectable(t, s))
595
+ if (h || !this.isCellSelectable(t, s))
586
596
  return !1;
587
597
  const a = this.selectedCell;
588
598
  return a && a.row === t && a.col === s || (this.selectedCell = { row: t, col: s }, this.emit("selection-change", this.#e()), this.requestAfterRender()), !1;
589
599
  }
590
- if (l === "row")
591
- return !this.isRowSelectable(t) || this.selected.size === 1 && this.selected.has(t) || (this.selected.clear(), this.selected.add(t), this.lastSelected = t, this.emit("selection-change", this.#e()), this.requestAfterRender()), !1;
600
+ if (l === "row") {
601
+ if (!this.isRowSelectable(t))
602
+ return !1;
603
+ const a = i.shiftKey, d = i.ctrlKey || i.metaKey, c = o?.meta?.checkboxColumn === !0;
604
+ if (a && this.anchor !== null) {
605
+ const u = Math.min(this.anchor, t), x = Math.max(this.anchor, t);
606
+ d || this.selected.clear();
607
+ for (let w = u; w <= x; w++)
608
+ this.isRowSelectable(w) && this.selected.add(w);
609
+ } else if (d || c)
610
+ this.selected.has(t) ? this.selected.delete(t) : this.selected.add(t), this.anchor = t;
611
+ else {
612
+ if (this.selected.size === 1 && this.selected.has(t))
613
+ return !1;
614
+ this.selected.clear(), this.selected.add(t), this.anchor = t;
615
+ }
616
+ return this.lastSelected = t, this.explicitSelection = !0, this.emit("selection-change", this.#e()), this.requestAfterRender(), !1;
617
+ }
592
618
  if (l === "range") {
593
- if (d || !this.isCellSelectable(t, s))
619
+ if (h || !this.isCellSelectable(t, s))
594
620
  return !1;
595
- const a = i.shiftKey, h = i.ctrlKey || i.metaKey;
621
+ const a = i.shiftKey, d = i.ctrlKey || i.metaKey;
596
622
  if (a && this.cellAnchor) {
597
- const c = b(this.cellAnchor, { row: t, col: s }), f = this.ranges.length > 0 ? this.ranges[this.ranges.length - 1] : null;
598
- if (f && w(f, c))
623
+ const c = m(this.cellAnchor, { row: t, col: s }), u = this.ranges.length > 0 ? this.ranges[this.ranges.length - 1] : null;
624
+ if (u && b(u, c))
599
625
  return !1;
600
- h ? this.ranges.length > 0 ? this.ranges[this.ranges.length - 1] = c : this.ranges.push(c) : this.ranges = [c], this.activeRange = c;
601
- } else if (h) {
626
+ d ? this.ranges.length > 0 ? this.ranges[this.ranges.length - 1] = c : this.ranges.push(c) : this.ranges = [c], this.activeRange = c;
627
+ } else if (d) {
602
628
  const c = {
603
629
  startRow: t,
604
630
  startCol: s,
@@ -613,7 +639,7 @@ class D extends v {
613
639
  endRow: t,
614
640
  endCol: s
615
641
  };
616
- if (this.ranges.length === 1 && w(this.ranges[0], c))
642
+ if (this.ranges.length === 1 && b(this.ranges[0], c))
617
643
  return !1;
618
644
  this.ranges = [c], this.activeRange = c, this.cellAnchor = { row: t, col: s };
619
645
  }
@@ -629,99 +655,200 @@ class D extends v {
629
655
  return t === "cell" ? this.selectedCell = null : t === "row" ? (this.selected.clear(), this.anchor = null) : t === "range" && (this.ranges = [], this.activeRange = null, this.cellAnchor = null), this.emit("selection-change", this.#e()), this.requestAfterRender(), !0;
630
656
  if (t === "cell" && i)
631
657
  return queueMicrotask(() => {
632
- const l = this.grid._focusRow, n = this.grid._focusCol;
633
- this.isCellSelectable(l, n) ? this.selectedCell = { row: l, col: n } : this.selectedCell = null, this.emit("selection-change", this.#e()), this.requestAfterRender();
658
+ const l = this.grid._focusRow, r = this.grid._focusCol;
659
+ this.isCellSelectable(l, r) ? this.selectedCell = { row: l, col: r } : this.selectedCell = null, this.emit("selection-change", this.#e()), this.requestAfterRender();
634
660
  }), !1;
635
- if (t === "row" && (e.key === "ArrowUp" || e.key === "ArrowDown"))
636
- return queueMicrotask(() => {
637
- const l = this.grid._focusRow;
638
- this.isRowSelectable(l) ? (this.selected.clear(), this.selected.add(l), this.lastSelected = l) : this.selected.clear(), this.emit("selection-change", this.#e()), this.requestAfterRender();
639
- }), !1;
640
- if (t === "range" && i) {
641
- const l = e.key === "Tab", n = e.shiftKey && !l;
642
- return n && !this.cellAnchor && (this.cellAnchor = { row: this.grid._focusRow, col: this.grid._focusCol }), this.pendingKeyboardUpdate = { shiftKey: n }, queueMicrotask(() => this.requestAfterRender()), !1;
643
- }
644
- if (t === "range" && e.key === "a" && (e.ctrlKey || e.metaKey)) {
645
- const l = this.rows.length, n = this.columns.length;
646
- if (l > 0 && n > 0) {
647
- e.preventDefault(), e.stopPropagation();
648
- const o = {
649
- startRow: 0,
650
- startCol: 0,
651
- endRow: l - 1,
652
- endCol: n - 1
653
- };
654
- return this.ranges = [o], this.activeRange = o, this.emit("selection-change", this.#e()), this.requestAfterRender(), !0;
661
+ if (t === "row") {
662
+ if (e.key === "ArrowUp" || e.key === "ArrowDown") {
663
+ const l = e.shiftKey;
664
+ return l && this.anchor === null && (this.anchor = this.grid._focusRow), queueMicrotask(() => {
665
+ const r = this.grid._focusRow;
666
+ if (l && this.anchor !== null) {
667
+ this.selected.clear();
668
+ const o = Math.min(this.anchor, r), h = Math.max(this.anchor, r);
669
+ for (let a = o; a <= h; a++)
670
+ this.isRowSelectable(a) && this.selected.add(a);
671
+ } else
672
+ this.isRowSelectable(r) ? (this.selected.clear(), this.selected.add(r), this.anchor = r) : this.selected.clear();
673
+ this.lastSelected = r, this.explicitSelection = !0, this.emit("selection-change", this.#e()), this.requestAfterRender();
674
+ }), !1;
655
675
  }
676
+ if (e.key === "a" && (e.ctrlKey || e.metaKey))
677
+ return e.preventDefault(), e.stopPropagation(), this.selectAll(), !0;
656
678
  }
657
- return !1;
679
+ if (t === "range" && i) {
680
+ const l = e.key === "Tab", r = e.shiftKey && !l;
681
+ return r && !this.cellAnchor && (this.cellAnchor = { row: this.grid._focusRow, col: this.grid._focusCol }), this.pendingKeyboardUpdate = { shiftKey: r }, queueMicrotask(() => this.requestAfterRender()), !1;
682
+ }
683
+ return t === "range" && e.key === "a" && (e.ctrlKey || e.metaKey) ? (e.preventDefault(), e.stopPropagation(), this.selectAll(), !0) : !1;
658
684
  }
659
685
  /** @internal */
660
686
  onCellMouseDown(e) {
661
687
  if (!this.isSelectionEnabled() || this.config.mode !== "range" || e.rowIndex === void 0 || e.colIndex === void 0 || e.rowIndex < 0) return;
662
688
  const t = this.columns[e.colIndex];
663
- if (t && g(t) || !this.isCellSelectable(e.rowIndex, e.colIndex) || e.originalEvent.shiftKey && this.cellAnchor)
689
+ if (t && f(t) || !this.isCellSelectable(e.rowIndex, e.colIndex) || e.originalEvent.shiftKey && this.cellAnchor)
664
690
  return;
665
691
  this.isDragging = !0;
666
- const s = e.rowIndex, i = e.colIndex, l = e.originalEvent.ctrlKey || e.originalEvent.metaKey, n = {
692
+ const s = e.rowIndex, i = e.colIndex, l = e.originalEvent.ctrlKey || e.originalEvent.metaKey, r = {
667
693
  startRow: s,
668
694
  startCol: i,
669
695
  endRow: s,
670
696
  endCol: i
671
697
  };
672
- return !l && this.ranges.length === 1 && w(this.ranges[0], n) ? (this.cellAnchor = { row: s, col: i }, !0) : (this.cellAnchor = { row: s, col: i }, l || (this.ranges = []), this.ranges.push(n), this.activeRange = n, this.emit("selection-change", this.#e()), this.requestAfterRender(), !0);
698
+ return !l && this.ranges.length === 1 && b(this.ranges[0], r) ? (this.cellAnchor = { row: s, col: i }, !0) : (this.cellAnchor = { row: s, col: i }, l || (this.ranges = []), this.ranges.push(r), this.activeRange = r, this.emit("selection-change", this.#e()), this.requestAfterRender(), !0);
673
699
  }
674
700
  /** @internal */
675
701
  onCellMouseMove(e) {
676
702
  if (!this.isSelectionEnabled() || this.config.mode !== "range" || !this.isDragging || !this.cellAnchor || e.rowIndex === void 0 || e.colIndex === void 0 || e.rowIndex < 0) return;
677
703
  let t = e.colIndex;
678
704
  const s = this.columns[t];
679
- if (s && g(s)) {
680
- const n = this.columns.findIndex((o) => !g(o));
681
- n >= 0 && (t = n);
705
+ if (s && f(s)) {
706
+ const r = this.columns.findIndex((o) => !f(o));
707
+ r >= 0 && (t = r);
682
708
  }
683
- const i = b(this.cellAnchor, { row: e.rowIndex, col: t }), l = this.ranges.length > 0 ? this.ranges[this.ranges.length - 1] : null;
684
- return l && w(l, i) || (this.ranges.length > 0 ? this.ranges[this.ranges.length - 1] = i : this.ranges.push(i), this.activeRange = i, this.emit("selection-change", this.#e()), this.requestAfterRender()), !0;
709
+ const i = m(this.cellAnchor, { row: e.rowIndex, col: t }), l = this.ranges.length > 0 ? this.ranges[this.ranges.length - 1] : null;
710
+ return l && b(l, i) || (this.ranges.length > 0 ? this.ranges[this.ranges.length - 1] = i : this.ranges.push(i), this.activeRange = i, this.emit("selection-change", this.#e()), this.requestAfterRender()), !0;
685
711
  }
686
712
  /** @internal */
687
713
  onCellMouseUp(e) {
688
714
  if (this.isSelectionEnabled() && this.config.mode === "range" && this.isDragging)
689
715
  return this.isDragging = !1, !0;
690
716
  }
717
+ // #region Checkbox Column
691
718
  /**
692
- * Apply selection classes to visible cells/rows.
693
- * Shared by afterRender and onScrollRender.
719
+ * Inject checkbox column when `checkbox: true` and mode is `'row'`.
720
+ * @internal
721
+ */
722
+ processColumns(e) {
723
+ if (this.config.checkbox && this.config.mode === "row") {
724
+ if (e.some((l) => l.field === S))
725
+ return e;
726
+ const t = this.#t(), s = e.findIndex(I), i = s >= 0 ? s + 1 : 0;
727
+ return [...e.slice(0, i), t, ...e.slice(i)];
728
+ }
729
+ return e;
730
+ }
731
+ /**
732
+ * Create the checkbox utility column configuration.
694
733
  */
695
734
  #t() {
735
+ return {
736
+ field: S,
737
+ header: "",
738
+ width: 32,
739
+ resizable: !1,
740
+ sortable: !1,
741
+ meta: {
742
+ lockPosition: !0,
743
+ suppressMovable: !0,
744
+ utility: !0,
745
+ checkboxColumn: !0
746
+ },
747
+ headerRenderer: () => {
748
+ const e = document.createElement("div");
749
+ e.className = "tbw-checkbox-header";
750
+ const t = document.createElement("input");
751
+ return t.type = "checkbox", t.className = "tbw-select-all-checkbox", t.addEventListener("click", (s) => {
752
+ s.stopPropagation(), s.target.checked ? this.selectAll() : this.clearSelection();
753
+ }), e.appendChild(t), e;
754
+ },
755
+ renderer: (e) => {
756
+ const t = document.createElement("input");
757
+ t.type = "checkbox", t.className = "tbw-select-row-checkbox";
758
+ const s = e.cellEl;
759
+ if (s) {
760
+ const i = parseInt(s.getAttribute("data-row") ?? "-1", 10);
761
+ i >= 0 && (t.checked = this.selected.has(i));
762
+ }
763
+ return t;
764
+ }
765
+ };
766
+ }
767
+ /**
768
+ * Update checkbox checked states to reflect current selection.
769
+ * Called from #applySelectionClasses.
770
+ */
771
+ #i(e) {
772
+ e.querySelectorAll(".tbw-select-row-checkbox").forEach((i) => {
773
+ const l = i.closest(".cell"), r = l ? R(l) : -1;
774
+ r >= 0 && (i.checked = this.selected.has(r));
775
+ });
776
+ const s = e.querySelector(".tbw-select-all-checkbox");
777
+ if (s) {
778
+ const i = this.rows.length;
779
+ let l = 0;
780
+ if (this.config.isSelectable)
781
+ for (let h = 0; h < i; h++)
782
+ this.isRowSelectable(h) && l++;
783
+ else
784
+ l = i;
785
+ const r = l > 0 && this.selected.size >= l, o = this.selected.size > 0;
786
+ s.checked = r, s.indeterminate = o && !r;
787
+ }
788
+ }
789
+ // #endregion
790
+ /**
791
+ * Sync selection state to the grid's current focus position.
792
+ * In row mode, keeps `selected` in sync with `_focusRow`.
793
+ * In cell mode, keeps `selectedCell` in sync with `_focusRow`/`_focusCol`.
794
+ * Only updates when the focus has changed since the last sync.
795
+ * Skips when `explicitSelection` is set (click/keyboard set selection directly).
796
+ */
797
+ #r(e) {
798
+ const t = this.grid._focusRow, s = this.grid._focusCol;
799
+ if (e === "row") {
800
+ if (this.explicitSelection) {
801
+ this.explicitSelection = !1, this.lastSyncedFocusRow = t;
802
+ return;
803
+ }
804
+ t !== this.lastSyncedFocusRow && (this.lastSyncedFocusRow = t, this.isRowSelectable(t) && (!this.selected.has(t) || this.selected.size !== 1) && (this.selected.clear(), this.selected.add(t), this.lastSelected = t, this.anchor = t, this.emit("selection-change", this.#e())));
805
+ }
806
+ if (e === "cell") {
807
+ if (this.explicitSelection) {
808
+ this.explicitSelection = !1, this.lastSyncedFocusRow = t, this.lastSyncedFocusCol = s;
809
+ return;
810
+ }
811
+ if ((t !== this.lastSyncedFocusRow || s !== this.lastSyncedFocusCol) && (this.lastSyncedFocusRow = t, this.lastSyncedFocusCol = s, this.isCellSelectable(t, s))) {
812
+ const i = this.selectedCell;
813
+ (!i || i.row !== t || i.col !== s) && (this.selectedCell = { row: t, col: s }, this.emit("selection-change", this.#e()));
814
+ }
815
+ }
816
+ }
817
+ /**
818
+ * Apply CSS selection classes to row/cell elements.
819
+ * Shared by afterRender and onScrollRender.
820
+ */
821
+ #s() {
696
822
  const e = this.gridElement;
697
823
  if (!e) return;
698
824
  const { mode: t } = this.config, s = !!this.config.isSelectable;
699
- e.querySelectorAll(".cell").forEach((n) => {
700
- n.classList.remove("selected", "top", "bottom", "first", "last"), s && n.removeAttribute("data-selectable");
825
+ e.querySelectorAll(".cell").forEach((r) => {
826
+ r.classList.remove("selected", "top", "bottom", "first", "last"), s && r.removeAttribute("data-selectable");
701
827
  });
702
828
  const l = e.querySelectorAll(".data-grid-row");
703
- if (l.forEach((n) => {
704
- n.classList.remove("selected", "row-focus"), s && n.removeAttribute("data-selectable");
705
- }), t === "row" && (m(e), l.forEach((n) => {
706
- const o = n.querySelector(".cell[data-row]"), d = S(o);
707
- d >= 0 && (s && !this.isRowSelectable(d) && n.setAttribute("data-selectable", "false"), this.selected.has(d) && n.classList.add("selected", "row-focus"));
708
- })), (t === "cell" || t === "range") && s && e.querySelectorAll(".cell[data-row][data-col]").forEach((o) => {
709
- const d = parseInt(o.getAttribute("data-row") ?? "-1", 10), a = parseInt(o.getAttribute("data-col") ?? "-1", 10);
710
- d >= 0 && a >= 0 && (this.isCellSelectable(d, a) || o.setAttribute("data-selectable", "false"));
829
+ if (l.forEach((r) => {
830
+ r.classList.remove("selected", "row-focus"), s && r.removeAttribute("data-selectable");
831
+ }), t === "row" && (C(e), l.forEach((r) => {
832
+ const o = r.querySelector(".cell[data-row]"), h = R(o);
833
+ h >= 0 && (s && !this.isRowSelectable(h) && r.setAttribute("data-selectable", "false"), this.selected.has(h) && r.classList.add("selected", "row-focus"));
834
+ }), this.config.checkbox && this.#i(e)), (t === "cell" || t === "range") && s && e.querySelectorAll(".cell[data-row][data-col]").forEach((o) => {
835
+ const h = parseInt(o.getAttribute("data-row") ?? "-1", 10), a = parseInt(o.getAttribute("data-col") ?? "-1", 10);
836
+ h >= 0 && a >= 0 && (this.isCellSelectable(h, a) || o.setAttribute("data-selectable", "false"));
711
837
  }), t === "range" && this.ranges.length > 0) {
712
- m(e);
713
- const n = this.activeRange ? u(this.activeRange) : null, o = this.columns.findIndex((a) => !g(a));
714
- this.columns.length - 1, e.querySelectorAll(".cell[data-row][data-col]").forEach((a) => {
715
- const h = parseInt(a.getAttribute("data-row") ?? "-1", 10), c = parseInt(a.getAttribute("data-col") ?? "-1", 10);
716
- if (h >= 0 && c >= 0) {
717
- const f = this.columns[c];
718
- if (f && g(f))
838
+ C(e);
839
+ const r = this.ranges.map(g), o = (a, d) => {
840
+ for (const c of r)
841
+ if (a >= c.startRow && a <= c.endRow && d >= c.startCol && d <= c.endCol)
842
+ return !0;
843
+ return !1;
844
+ };
845
+ e.querySelectorAll(".cell[data-row][data-col]").forEach((a) => {
846
+ const d = parseInt(a.getAttribute("data-row") ?? "-1", 10), c = parseInt(a.getAttribute("data-col") ?? "-1", 10);
847
+ if (d >= 0 && c >= 0) {
848
+ const u = this.columns[c];
849
+ if (u && f(u))
719
850
  return;
720
- if (C(h, c, this.ranges) && (a.classList.add("selected"), n)) {
721
- h === n.startRow && a.classList.add("top"), h === n.endRow && a.classList.add("bottom");
722
- const A = Math.max(n.startCol, o);
723
- c === A && a.classList.add("first"), c === n.endCol && a.classList.add("last");
724
- }
851
+ o(d, c) && (a.classList.add("selected"), o(d - 1, c) || a.classList.add("top"), o(d + 1, c) || a.classList.add("bottom"), o(d, c - 1) || a.classList.add("first"), o(d, c + 1) || a.classList.add("last"));
725
852
  }
726
853
  });
727
854
  }
@@ -735,14 +862,14 @@ class D extends v {
735
862
  if (this.pendingKeyboardUpdate && s === "range") {
736
863
  const { shiftKey: i } = this.pendingKeyboardUpdate;
737
864
  this.pendingKeyboardUpdate = null;
738
- const l = this.grid._focusRow, n = this.grid._focusCol;
865
+ const l = this.grid._focusRow, r = this.grid._focusCol;
739
866
  if (i && this.cellAnchor) {
740
- const o = b(this.cellAnchor, { row: l, col: n });
867
+ const o = m(this.cellAnchor, { row: l, col: r });
741
868
  this.ranges = [o], this.activeRange = o;
742
- } else i || (this.ranges = [], this.activeRange = null, this.cellAnchor = { row: l, col: n });
869
+ } else i || (this.ranges = [], this.activeRange = null, this.cellAnchor = { row: l, col: r });
743
870
  this.emit("selection-change", this.#e());
744
871
  }
745
- this.grid.setAttribute("data-selection-mode", s), t && t.classList.toggle("selecting", this.isDragging), this.#t();
872
+ this.#r(s), this.grid.setAttribute("data-selection-mode", s), t && t.classList.toggle("selecting", this.isDragging), this.#s();
746
873
  }
747
874
  /**
748
875
  * Called after scroll-triggered row rendering.
@@ -750,7 +877,7 @@ class D extends v {
750
877
  * @internal
751
878
  */
752
879
  onScrollRender() {
753
- this.isSelectionEnabled() && this.#t();
880
+ this.isSelectionEnabled() && this.#s();
754
881
  }
755
882
  // #endregion
756
883
  // #region Public API
@@ -780,13 +907,80 @@ class D extends v {
780
907
  * Get all selected cells across all ranges.
781
908
  */
782
909
  getSelectedCells() {
783
- return q(this.ranges);
910
+ return M(this.ranges);
784
911
  }
785
912
  /**
786
913
  * Check if a specific cell is in range selection.
787
914
  */
788
915
  isCellSelected(e, t) {
789
- return C(e, t, this.ranges);
916
+ return q(e, t, this.ranges);
917
+ }
918
+ /**
919
+ * Select all selectable rows (row mode) or all cells (range mode).
920
+ *
921
+ * In row mode, selects every row where `isSelectable` returns true (or all rows if no callback).
922
+ * In range mode, creates a single range spanning all rows and columns.
923
+ * Has no effect in cell mode.
924
+ *
925
+ * @example
926
+ * ```ts
927
+ * const plugin = grid.getPlugin(SelectionPlugin);
928
+ * plugin.selectAll(); // Selects everything in current mode
929
+ * ```
930
+ */
931
+ selectAll() {
932
+ const { mode: e } = this.config;
933
+ if (e === "row") {
934
+ this.selected.clear();
935
+ for (let t = 0; t < this.rows.length; t++)
936
+ this.isRowSelectable(t) && this.selected.add(t);
937
+ this.explicitSelection = !0, this.emit("selection-change", this.#e()), this.requestAfterRender();
938
+ } else if (e === "range") {
939
+ const t = this.rows.length, s = this.columns.length;
940
+ if (t > 0 && s > 0) {
941
+ const i = {
942
+ startRow: 0,
943
+ startCol: 0,
944
+ endRow: t - 1,
945
+ endCol: s - 1
946
+ };
947
+ this.ranges = [i], this.activeRange = i, this.emit("selection-change", this.#e()), this.requestAfterRender();
948
+ }
949
+ }
950
+ }
951
+ /**
952
+ * Select specific rows by index (row mode only).
953
+ * Replaces the current selection with the provided row indices.
954
+ * Indices that are out of bounds or fail the `isSelectable` check are ignored.
955
+ *
956
+ * @param indices - Array of row indices to select
957
+ *
958
+ * @example
959
+ * ```ts
960
+ * const plugin = grid.getPlugin(SelectionPlugin);
961
+ * plugin.selectRows([0, 2, 4]); // Select rows 0, 2, and 4
962
+ * ```
963
+ */
964
+ selectRows(e) {
965
+ if (this.config.mode === "row") {
966
+ this.selected.clear();
967
+ for (const t of e)
968
+ t >= 0 && t < this.rows.length && this.isRowSelectable(t) && this.selected.add(t);
969
+ this.anchor = e.length > 0 ? e[e.length - 1] : null, this.explicitSelection = !0, this.emit("selection-change", this.#e()), this.requestAfterRender();
970
+ }
971
+ }
972
+ /**
973
+ * Get the indices of all selected rows (convenience for row mode).
974
+ * Returns indices sorted in ascending order.
975
+ *
976
+ * @example
977
+ * ```ts
978
+ * const plugin = grid.getPlugin(SelectionPlugin);
979
+ * const rows = plugin.getSelectedRowIndices(); // [0, 2, 4]
980
+ * ```
981
+ */
982
+ getSelectedRowIndices() {
983
+ return [...this.selected].sort((e, t) => e - t);
790
984
  }
791
985
  /**
792
986
  * Clear all selection.
@@ -805,13 +999,13 @@ class D extends v {
805
999
  endCol: t.to.col
806
1000
  })), this.activeRange = this.ranges.length > 0 ? this.ranges[this.ranges.length - 1] : null, this.emit("selection-change", {
807
1001
  mode: this.config.mode,
808
- ranges: p(this.ranges)
1002
+ ranges: y(this.ranges)
809
1003
  }), this.requestAfterRender();
810
1004
  }
811
1005
  // #endregion
812
1006
  // #region Private Helpers
813
1007
  #e() {
814
- return _(
1008
+ return D(
815
1009
  this.config.mode,
816
1010
  {
817
1011
  selectedCell: this.selectedCell,
@@ -824,6 +1018,6 @@ class D extends v {
824
1018
  // #endregion
825
1019
  }
826
1020
  export {
827
- D as SelectionPlugin
1021
+ L as SelectionPlugin
828
1022
  };
829
1023
  //# sourceMappingURL=index.js.map