@toolbox-web/grid 1.21.1 → 1.22.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 (155) hide show
  1. package/README.md +6 -8
  2. package/all.js +2 -6850
  3. package/all.js.map +1 -1
  4. package/index.js +1 -4352
  5. package/index.js.map +1 -1
  6. package/lib/core/grid.d.ts +75 -8
  7. package/lib/core/grid.d.ts.map +1 -1
  8. package/lib/core/internal/rows.d.ts.map +1 -1
  9. package/lib/core/plugin/base-plugin.d.ts +8 -2
  10. package/lib/core/plugin/base-plugin.d.ts.map +1 -1
  11. package/lib/core/styles/index.d.ts.map +1 -1
  12. package/lib/core/types.d.ts +84 -2
  13. package/lib/core/types.d.ts.map +1 -1
  14. package/lib/plugins/clipboard/index.js +1 -733
  15. package/lib/plugins/clipboard/index.js.map +1 -1
  16. package/lib/plugins/clipboard/types.d.ts +5 -0
  17. package/lib/plugins/clipboard/types.d.ts.map +1 -1
  18. package/lib/plugins/column-virtualization/index.js +1 -560
  19. package/lib/plugins/column-virtualization/index.js.map +1 -1
  20. package/lib/plugins/column-virtualization/types.d.ts +5 -0
  21. package/lib/plugins/column-virtualization/types.d.ts.map +1 -1
  22. package/lib/plugins/context-menu/index.js +1 -754
  23. package/lib/plugins/context-menu/index.js.map +1 -1
  24. package/lib/plugins/context-menu/types.d.ts +5 -0
  25. package/lib/plugins/context-menu/types.d.ts.map +1 -1
  26. package/lib/plugins/editing/EditingPlugin.d.ts.map +1 -1
  27. package/lib/plugins/editing/index.d.ts +1 -1
  28. package/lib/plugins/editing/index.d.ts.map +1 -1
  29. package/lib/plugins/editing/index.js +1 -1539
  30. package/lib/plugins/editing/index.js.map +1 -1
  31. package/lib/plugins/editing/types.d.ts +49 -0
  32. package/lib/plugins/editing/types.d.ts.map +1 -1
  33. package/lib/plugins/export/index.js +1 -589
  34. package/lib/plugins/export/index.js.map +1 -1
  35. package/lib/plugins/export/types.d.ts +5 -0
  36. package/lib/plugins/export/types.d.ts.map +1 -1
  37. package/lib/plugins/filtering/FilteringPlugin.d.ts.map +1 -1
  38. package/lib/plugins/filtering/filter-model.d.ts.map +1 -1
  39. package/lib/plugins/filtering/index.js +1 -1283
  40. package/lib/plugins/filtering/index.js.map +1 -1
  41. package/lib/plugins/filtering/types.d.ts +7 -2
  42. package/lib/plugins/filtering/types.d.ts.map +1 -1
  43. package/lib/plugins/grouping-columns/index.js +1 -726
  44. package/lib/plugins/grouping-columns/index.js.map +1 -1
  45. package/lib/plugins/grouping-columns/types.d.ts +3 -0
  46. package/lib/plugins/grouping-columns/types.d.ts.map +1 -1
  47. package/lib/plugins/grouping-rows/index.js +2 -905
  48. package/lib/plugins/grouping-rows/index.js.map +1 -1
  49. package/lib/plugins/grouping-rows/types.d.ts +5 -0
  50. package/lib/plugins/grouping-rows/types.d.ts.map +1 -1
  51. package/lib/plugins/master-detail/index.js +1 -950
  52. package/lib/plugins/master-detail/index.js.map +1 -1
  53. package/lib/plugins/master-detail/types.d.ts +5 -0
  54. package/lib/plugins/master-detail/types.d.ts.map +1 -1
  55. package/lib/plugins/multi-sort/index.js +1 -553
  56. package/lib/plugins/multi-sort/index.js.map +1 -1
  57. package/lib/plugins/multi-sort/types.d.ts +5 -0
  58. package/lib/plugins/multi-sort/types.d.ts.map +1 -1
  59. package/lib/plugins/pinned-columns/index.js +1 -688
  60. package/lib/plugins/pinned-columns/index.js.map +1 -1
  61. package/lib/plugins/pinned-columns/types.d.ts +3 -0
  62. package/lib/plugins/pinned-columns/types.d.ts.map +1 -1
  63. package/lib/plugins/pinned-rows/index.js +1 -704
  64. package/lib/plugins/pinned-rows/index.js.map +1 -1
  65. package/lib/plugins/pinned-rows/types.d.ts +5 -0
  66. package/lib/plugins/pinned-rows/types.d.ts.map +1 -1
  67. package/lib/plugins/pivot/index.js +1 -1191
  68. package/lib/plugins/pivot/index.js.map +1 -1
  69. package/lib/plugins/pivot/types.d.ts +5 -0
  70. package/lib/plugins/pivot/types.d.ts.map +1 -1
  71. package/lib/plugins/print/index.js +1 -691
  72. package/lib/plugins/print/index.js.map +1 -1
  73. package/lib/plugins/print/types.d.ts +3 -0
  74. package/lib/plugins/print/types.d.ts.map +1 -1
  75. package/lib/plugins/reorder/index.js +1 -703
  76. package/lib/plugins/reorder/index.js.map +1 -1
  77. package/lib/plugins/reorder/types.d.ts +5 -0
  78. package/lib/plugins/reorder/types.d.ts.map +1 -1
  79. package/lib/plugins/responsive/index.js +1 -971
  80. package/lib/plugins/responsive/index.js.map +1 -1
  81. package/lib/plugins/responsive/types.d.ts +5 -0
  82. package/lib/plugins/responsive/types.d.ts.map +1 -1
  83. package/lib/plugins/row-reorder/index.js +1 -728
  84. package/lib/plugins/row-reorder/index.js.map +1 -1
  85. package/lib/plugins/row-reorder/types.d.ts +5 -0
  86. package/lib/plugins/row-reorder/types.d.ts.map +1 -1
  87. package/lib/plugins/selection/index.js +1 -1071
  88. package/lib/plugins/selection/index.js.map +1 -1
  89. package/lib/plugins/selection/types.d.ts +3 -0
  90. package/lib/plugins/selection/types.d.ts.map +1 -1
  91. package/lib/plugins/server-side/index.js +1 -521
  92. package/lib/plugins/server-side/index.js.map +1 -1
  93. package/lib/plugins/server-side/types.d.ts +5 -0
  94. package/lib/plugins/server-side/types.d.ts.map +1 -1
  95. package/lib/plugins/tree/index.js +1 -686
  96. package/lib/plugins/tree/index.js.map +1 -1
  97. package/lib/plugins/tree/types.d.ts +5 -0
  98. package/lib/plugins/tree/types.d.ts.map +1 -1
  99. package/lib/plugins/undo-redo/index.js +1 -584
  100. package/lib/plugins/undo-redo/index.js.map +1 -1
  101. package/lib/plugins/undo-redo/types.d.ts +5 -0
  102. package/lib/plugins/undo-redo/types.d.ts.map +1 -1
  103. package/lib/plugins/visibility/index.js +1 -792
  104. package/lib/plugins/visibility/index.js.map +1 -1
  105. package/lib/plugins/visibility/types.d.ts +5 -0
  106. package/lib/plugins/visibility/types.d.ts.map +1 -1
  107. package/package.json +1 -1
  108. package/umd/grid.all.umd.js +1 -186
  109. package/umd/grid.all.umd.js.map +1 -1
  110. package/umd/grid.umd.js +1 -90
  111. package/umd/grid.umd.js.map +1 -1
  112. package/umd/plugins/clipboard.umd.js +1 -6
  113. package/umd/plugins/clipboard.umd.js.map +1 -1
  114. package/umd/plugins/column-virtualization.umd.js +1 -1
  115. package/umd/plugins/column-virtualization.umd.js.map +1 -1
  116. package/umd/plugins/context-menu.umd.js +1 -1
  117. package/umd/plugins/context-menu.umd.js.map +1 -1
  118. package/umd/plugins/editing.umd.js +1 -1
  119. package/umd/plugins/editing.umd.js.map +1 -1
  120. package/umd/plugins/export.umd.js +1 -13
  121. package/umd/plugins/export.umd.js.map +1 -1
  122. package/umd/plugins/filtering.umd.js +1 -1
  123. package/umd/plugins/filtering.umd.js.map +1 -1
  124. package/umd/plugins/grouping-columns.umd.js +1 -1
  125. package/umd/plugins/grouping-columns.umd.js.map +1 -1
  126. package/umd/plugins/grouping-rows.umd.js +1 -4
  127. package/umd/plugins/grouping-rows.umd.js.map +1 -1
  128. package/umd/plugins/master-detail.umd.js +1 -1
  129. package/umd/plugins/master-detail.umd.js.map +1 -1
  130. package/umd/plugins/multi-sort.umd.js +1 -1
  131. package/umd/plugins/multi-sort.umd.js.map +1 -1
  132. package/umd/plugins/pinned-columns.umd.js +1 -1
  133. package/umd/plugins/pinned-columns.umd.js.map +1 -1
  134. package/umd/plugins/pinned-rows.umd.js +1 -1
  135. package/umd/plugins/pinned-rows.umd.js.map +1 -1
  136. package/umd/plugins/pivot.umd.js +1 -1
  137. package/umd/plugins/pivot.umd.js.map +1 -1
  138. package/umd/plugins/print.umd.js +1 -75
  139. package/umd/plugins/print.umd.js.map +1 -1
  140. package/umd/plugins/reorder.umd.js +1 -1
  141. package/umd/plugins/reorder.umd.js.map +1 -1
  142. package/umd/plugins/responsive.umd.js +1 -1
  143. package/umd/plugins/responsive.umd.js.map +1 -1
  144. package/umd/plugins/row-reorder.umd.js +1 -1
  145. package/umd/plugins/row-reorder.umd.js.map +1 -1
  146. package/umd/plugins/selection.umd.js +1 -3
  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
@@ -1,1072 +1,2 @@
1
- function R(l) {
2
- if (!l) return -1;
3
- const e = l.getAttribute("data-row");
4
- if (e) return parseInt(e, 10);
5
- const t = l.closest(".data-grid-row");
6
- if (!t) return -1;
7
- const s = t.parentElement;
8
- if (!s) return -1;
9
- const i = s.querySelectorAll(":scope > .data-grid-row");
10
- for (let r = 0; r < i.length; r++)
11
- if (i[r] === t) return r;
12
- return -1;
13
- }
14
- function C(l) {
15
- l && l.querySelectorAll(".cell-focus").forEach((e) => e.classList.remove("cell-focus"));
16
- }
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>', v = {
18
- expand: "▶",
19
- collapse: "▼",
20
- sortAsc: "▲",
21
- sortDesc: "▼",
22
- sortNone: "⇅",
23
- submenuArrow: "▶",
24
- dragHandle: "⋮⋮",
25
- toolPanel: "☰",
26
- filter: p,
27
- filterActive: p,
28
- print: "🖨️"
29
- };
30
- class E {
31
- /**
32
- * Plugin dependencies - declare other plugins this one requires.
33
- *
34
- * Dependencies are validated when the plugin is attached.
35
- * Required dependencies throw an error if missing.
36
- * Optional dependencies log an info message if missing.
37
- *
38
- * @example
39
- * ```typescript
40
- * static readonly dependencies: PluginDependency[] = [
41
- * { name: 'editing', required: true, reason: 'Tracks cell edits for undo/redo' },
42
- * { name: 'selection', required: false, reason: 'Enables selection-based undo' },
43
- * ];
44
- * ```
45
- */
46
- static dependencies;
47
- /**
48
- * Plugin manifest - declares owned properties, config rules, and hook priorities.
49
- *
50
- * This is read by the configuration validator to:
51
- * - Validate that required plugins are loaded when their properties are used
52
- * - Execute configRules to detect invalid/conflicting settings
53
- * - Order hook execution based on priority
54
- *
55
- * @example
56
- * ```typescript
57
- * static override readonly manifest: PluginManifest<MyConfig> = {
58
- * ownedProperties: [
59
- * { property: 'myProp', level: 'column', description: 'the "myProp" column property' },
60
- * ],
61
- * configRules: [
62
- * { id: 'myPlugin/conflict', severity: 'warn', message: '...', check: (c) => c.a && c.b },
63
- * ],
64
- * };
65
- * ```
66
- */
67
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
68
- static manifest;
69
- /**
70
- * Plugin version - defaults to grid version for built-in plugins.
71
- * Third-party plugins can override with their own semver.
72
- */
73
- version = typeof __GRID_VERSION__ < "u" ? __GRID_VERSION__ : "dev";
74
- /** CSS styles to inject into the grid's shadow DOM */
75
- styles;
76
- /** Custom cell renderers keyed by type name */
77
- cellRenderers;
78
- /** Custom header renderers keyed by type name */
79
- headerRenderers;
80
- /** Custom cell editors keyed by type name */
81
- cellEditors;
82
- /** The grid instance this plugin is attached to */
83
- grid;
84
- /** Plugin configuration - merged with defaults in attach() */
85
- config;
86
- /** User-provided configuration from constructor */
87
- userConfig;
88
- /**
89
- * Plugin-level AbortController for event listener cleanup.
90
- * Created fresh in attach(), aborted in detach().
91
- * This ensures event listeners are properly cleaned up when plugins are re-attached.
92
- */
93
- #t;
94
- /**
95
- * Default configuration - subclasses should override this getter.
96
- * Note: This must be a getter (not property initializer) for proper inheritance
97
- * since property initializers run after parent constructor.
98
- */
99
- get defaultConfig() {
100
- return {};
101
- }
102
- constructor(e = {}) {
103
- this.userConfig = e;
104
- }
105
- /**
106
- * Called when the plugin is attached to a grid.
107
- * Override to set up event listeners, initialize state, etc.
108
- *
109
- * @example
110
- * ```ts
111
- * attach(grid: GridElement): void {
112
- * super.attach(grid);
113
- * // Set up document-level listeners with auto-cleanup
114
- * document.addEventListener('keydown', this.handleEscape, {
115
- * signal: this.disconnectSignal
116
- * });
117
- * }
118
- * ```
119
- */
120
- attach(e) {
121
- this.#t?.abort(), this.#t = new AbortController(), this.grid = e, this.config = { ...this.defaultConfig, ...this.userConfig };
122
- }
123
- /**
124
- * Called when the plugin is detached from a grid.
125
- * Override to clean up event listeners, timers, etc.
126
- *
127
- * @example
128
- * ```ts
129
- * detach(): void {
130
- * // Clean up any state not handled by disconnectSignal
131
- * this.selectedRows.clear();
132
- * this.cache = null;
133
- * }
134
- * ```
135
- */
136
- detach() {
137
- this.#t?.abort(), this.#t = void 0;
138
- }
139
- /**
140
- * Get another plugin instance from the same grid.
141
- * Use for inter-plugin communication.
142
- *
143
- * @example
144
- * ```ts
145
- * const selection = this.getPlugin(SelectionPlugin);
146
- * if (selection) {
147
- * const selectedRows = selection.getSelectedRows();
148
- * }
149
- * ```
150
- */
151
- getPlugin(e) {
152
- return this.grid?.getPlugin(e);
153
- }
154
- /**
155
- * Emit a custom event from the grid.
156
- */
157
- emit(e, t) {
158
- this.grid?.dispatchEvent?.(new CustomEvent(e, { detail: t, bubbles: !0 }));
159
- }
160
- /**
161
- * Emit a cancelable custom event from the grid.
162
- * @returns `true` if the event was cancelled (preventDefault called), `false` otherwise
163
- */
164
- emitCancelable(e, t) {
165
- const s = new CustomEvent(e, { detail: t, bubbles: !0, cancelable: !0 });
166
- return this.grid?.dispatchEvent?.(s), s.defaultPrevented;
167
- }
168
- // =========================================================================
169
- // Event Bus - Plugin-to-Plugin Communication
170
- // =========================================================================
171
- /**
172
- * Subscribe to an event from another plugin.
173
- * The subscription is automatically cleaned up when this plugin is detached.
174
- *
175
- * @category Plugin Development
176
- * @param eventType - The event type to listen for (e.g., 'filter-change')
177
- * @param callback - The callback to invoke when the event is emitted
178
- *
179
- * @example
180
- * ```typescript
181
- * // In attach() or other initialization
182
- * this.on('filter-change', (detail) => {
183
- * console.log('Filter changed:', detail);
184
- * });
185
- * ```
186
- */
187
- on(e, t) {
188
- this.grid?._pluginManager?.subscribe(this, e, t);
189
- }
190
- /**
191
- * Unsubscribe from a plugin event.
192
- *
193
- * @category Plugin Development
194
- * @param eventType - The event type to stop listening for
195
- *
196
- * @example
197
- * ```typescript
198
- * this.off('filter-change');
199
- * ```
200
- */
201
- off(e) {
202
- this.grid?._pluginManager?.unsubscribe(this, e);
203
- }
204
- /**
205
- * Emit an event to other plugins via the Event Bus.
206
- * This is for inter-plugin communication only; it does NOT dispatch DOM events.
207
- * Use `emit()` to dispatch DOM events that external consumers can listen to.
208
- *
209
- * @category Plugin Development
210
- * @param eventType - The event type to emit (should be declared in manifest.events)
211
- * @param detail - The event payload
212
- *
213
- * @example
214
- * ```typescript
215
- * // Emit to other plugins (not DOM)
216
- * this.emitPluginEvent('filter-change', { field: 'name', value: 'Alice' });
217
- *
218
- * // For DOM events that consumers can addEventListener to:
219
- * this.emit('filter-change', { field: 'name', value: 'Alice' });
220
- * ```
221
- */
222
- emitPluginEvent(e, t) {
223
- this.grid?._pluginManager?.emitPluginEvent(e, t);
224
- }
225
- /**
226
- * Request a re-render of the grid.
227
- * Uses ROWS phase - does NOT trigger processColumns hooks.
228
- */
229
- requestRender() {
230
- this.grid?.requestRender?.();
231
- }
232
- /**
233
- * Request a columns re-render of the grid.
234
- * Uses COLUMNS phase - triggers processColumns hooks.
235
- * Use this when your plugin needs to reprocess column configuration.
236
- */
237
- requestColumnsRender() {
238
- this.grid?.requestColumnsRender?.();
239
- }
240
- /**
241
- * Request a re-render and restore focus styling afterward.
242
- * Use this when a plugin action (like expand/collapse) triggers a render
243
- * but needs to maintain keyboard navigation focus.
244
- */
245
- requestRenderWithFocus() {
246
- this.grid?.requestRenderWithFocus?.();
247
- }
248
- /**
249
- * Request a lightweight style update without rebuilding DOM.
250
- * Use this instead of requestRender() when only CSS classes need updating.
251
- */
252
- requestAfterRender() {
253
- this.grid?.requestAfterRender?.();
254
- }
255
- /**
256
- * Get the current rows from the grid.
257
- */
258
- get rows() {
259
- return this.grid?.rows ?? [];
260
- }
261
- /**
262
- * Get the original unfiltered/unprocessed rows from the grid.
263
- * Use this when you need all source data regardless of active filters.
264
- */
265
- get sourceRows() {
266
- return this.grid?.sourceRows ?? [];
267
- }
268
- /**
269
- * Get the current columns from the grid.
270
- */
271
- get columns() {
272
- return this.grid?.columns ?? [];
273
- }
274
- /**
275
- * Get only visible columns from the grid (excludes hidden).
276
- * Use this for rendering that needs to match the grid template.
277
- */
278
- get visibleColumns() {
279
- return this.grid?._visibleColumns ?? [];
280
- }
281
- /**
282
- * Get the grid as an HTMLElement for direct DOM operations.
283
- * Use sparingly - prefer the typed GridElementRef API when possible.
284
- *
285
- * @example
286
- * ```ts
287
- * const width = this.gridElement.clientWidth;
288
- * this.gridElement.classList.add('my-plugin-active');
289
- * ```
290
- */
291
- get gridElement() {
292
- return this.grid;
293
- }
294
- /**
295
- * Get the disconnect signal for event listener cleanup.
296
- * This signal is aborted when the grid disconnects from the DOM.
297
- * Use this when adding event listeners that should be cleaned up automatically.
298
- *
299
- * Best for:
300
- * - Document/window-level listeners added in attach()
301
- * - Listeners on the grid element itself
302
- * - Any listener that should persist across renders
303
- *
304
- * Not needed for:
305
- * - Listeners on elements created in afterRender() (removed with element)
306
- *
307
- * @example
308
- * element.addEventListener('click', handler, { signal: this.disconnectSignal });
309
- * document.addEventListener('keydown', handler, { signal: this.disconnectSignal });
310
- */
311
- get disconnectSignal() {
312
- return this.#t?.signal ?? this.grid?.disconnectSignal;
313
- }
314
- /**
315
- * Get the grid-level icons configuration.
316
- * Returns merged icons (user config + defaults).
317
- */
318
- get gridIcons() {
319
- const e = this.grid?.gridConfig?.icons ?? {};
320
- return { ...v, ...e };
321
- }
322
- // #region Animation Helpers
323
- /**
324
- * Check if animations are enabled at the grid level.
325
- * Respects gridConfig.animation.mode and the CSS variable set by the grid.
326
- *
327
- * Plugins should use this to skip animations when:
328
- * - Animation mode is 'off' or `false`
329
- * - User prefers reduced motion and mode is 'reduced-motion' (default)
330
- *
331
- * @example
332
- * ```ts
333
- * private get animationStyle(): 'slide' | 'fade' | false {
334
- * if (!this.isAnimationEnabled) return false;
335
- * return this.config.animation ?? 'slide';
336
- * }
337
- * ```
338
- */
339
- get isAnimationEnabled() {
340
- const e = this.grid?.effectiveConfig?.animation?.mode ?? "reduced-motion";
341
- if (e === !1 || e === "off") return !1;
342
- if (e === !0 || e === "on") return !0;
343
- const t = this.gridElement;
344
- return t ? getComputedStyle(t).getPropertyValue("--tbw-animation-enabled").trim() !== "0" : !0;
345
- }
346
- /**
347
- * Get the animation duration in milliseconds from CSS variable.
348
- * Falls back to 200ms if not set.
349
- *
350
- * Plugins can use this for their animation timing to stay consistent
351
- * with the grid-level animation.duration setting.
352
- *
353
- * @example
354
- * ```ts
355
- * element.animate(keyframes, { duration: this.animationDuration });
356
- * ```
357
- */
358
- get animationDuration() {
359
- const e = this.gridElement;
360
- if (e) {
361
- const t = getComputedStyle(e).getPropertyValue("--tbw-animation-duration").trim(), s = parseInt(t, 10);
362
- if (!isNaN(s)) return s;
363
- }
364
- return 200;
365
- }
366
- // #endregion
367
- /**
368
- * Resolve an icon value to string or HTMLElement.
369
- * Checks plugin config first, then grid-level icons, then defaults.
370
- *
371
- * @param iconKey - The icon key in GridIcons (e.g., 'expand', 'collapse')
372
- * @param pluginOverride - Optional plugin-level override
373
- * @returns The resolved icon value
374
- */
375
- resolveIcon(e, t) {
376
- return t !== void 0 ? t : this.gridIcons[e];
377
- }
378
- /**
379
- * Set an icon value on an element.
380
- * Handles both string (text/HTML) and HTMLElement values.
381
- *
382
- * @param element - The element to set the icon on
383
- * @param icon - The icon value (string or HTMLElement)
384
- */
385
- setIcon(e, t) {
386
- typeof t == "string" ? e.innerHTML = t : t instanceof HTMLElement && (e.innerHTML = "", e.appendChild(t.cloneNode(!0)));
387
- }
388
- /**
389
- * Log a warning message.
390
- */
391
- warn(e) {
392
- console.warn(`[tbw-grid:${this.name}] ${e}`);
393
- }
394
- // #endregion
395
- }
396
- const k = "__tbw_expander";
397
- function I(l) {
398
- return l.field === k;
399
- }
400
- function f(l) {
401
- return l.meta?.utility === !0;
402
- }
403
- function g(l) {
404
- return {
405
- startRow: Math.min(l.startRow, l.endRow),
406
- startCol: Math.min(l.startCol, l.endCol),
407
- endRow: Math.max(l.startRow, l.endRow),
408
- endCol: Math.max(l.startCol, l.endCol)
409
- };
410
- }
411
- function _(l) {
412
- const e = g(l);
413
- return {
414
- from: { row: e.startRow, col: e.startCol },
415
- to: { row: e.endRow, col: e.endCol }
416
- };
417
- }
418
- function y(l) {
419
- return l.map(_);
420
- }
421
- function q(l, e, t) {
422
- const s = g(t);
423
- return l >= s.startRow && l <= s.endRow && e >= s.startCol && e <= s.endCol;
424
- }
425
- function K(l, e, t) {
426
- return t.some((s) => q(l, e, s));
427
- }
428
- function M(l) {
429
- const e = [], t = g(l);
430
- for (let s = t.startRow; s <= t.endRow; s++)
431
- for (let i = t.startCol; i <= t.endCol; i++)
432
- e.push({ row: s, col: i });
433
- return e;
434
- }
435
- function F(l) {
436
- const e = /* @__PURE__ */ new Map();
437
- for (const t of l)
438
- for (const s of M(t))
439
- e.set(`${s.row},${s.col}`, s);
440
- return [...e.values()];
441
- }
442
- function m(l, e) {
443
- return {
444
- startRow: l.row,
445
- startCol: l.col,
446
- endRow: e.row,
447
- endCol: e.col
448
- };
449
- }
450
- function b(l, e) {
451
- const t = g(l), s = g(e);
452
- return t.startRow === s.startRow && t.startCol === s.startCol && t.endRow === s.endRow && t.endCol === s.endCol;
453
- }
454
- const D = '@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-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-grid-row.row-focus:after{content:"";position:absolute;inset:0;pointer-events:none;border-width:0;border-style:var(--tbw-selection-border-style, var(--tbw-border-style));border-color:var(--tbw-range-border-color, var(--tbw-color-accent));border-top-width:var(--tbw-selection-border-width, var(--tbw-border-width));border-bottom-width:var(--tbw-selection-border-width, var(--tbw-border-width));z-index:1}tbw-grid .data-grid-row.row-focus+.data-grid-row.row-focus:after{border-top-width:0}tbw-grid .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 var(--tbw-selection-border-style, var(--tbw-border-style)) var(--tbw-range-border-color);z-index:1}tbw-grid .data-grid-row>.cell.selected.top:after{border-top-width:var(--tbw-selection-border-width, var(--tbw-border-width))}tbw-grid .data-grid-row>.cell.selected.bottom:after{border-bottom-width:var(--tbw-selection-border-width, var(--tbw-border-width))}tbw-grid .data-grid-row>.cell.selected.first:after{border-left-width:var(--tbw-selection-border-width, var(--tbw-border-width))}tbw-grid .data-grid-row>.cell.selected.last:after{border-right-width:var(--tbw-selection-border-width, var(--tbw-border-width))}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 L(l, e, t) {
456
- if (l === "cell" && e.selectedCell)
457
- return {
458
- mode: l,
459
- ranges: [
460
- {
461
- from: { row: e.selectedCell.row, col: e.selectedCell.col },
462
- to: { row: e.selectedCell.row, col: e.selectedCell.col }
463
- }
464
- ]
465
- };
466
- if (l === "row" && e.selected.size > 0) {
467
- const s = [...e.selected].sort((n, h) => n - h), i = [];
468
- let r = s[0], o = r;
469
- for (let n = 1; n < s.length; n++)
470
- s[n] === o + 1 ? o = s[n] : (i.push({ from: { row: r, col: 0 }, to: { row: o, col: t - 1 } }), r = s[n], o = r);
471
- return i.push({ from: { row: r, col: 0 }, to: { row: o, col: t - 1 } }), { mode: l, ranges: i };
472
- }
473
- return l === "range" && e.ranges.length > 0 ? { mode: l, ranges: y(e.ranges) } : { mode: l, ranges: [] };
474
- }
475
- class P extends E {
476
- /**
477
- * Plugin manifest - declares queries and configuration validation rules.
478
- * @internal
479
- */
480
- static manifest = {
481
- queries: [
482
- { type: "getSelection", description: "Get the current selection state" },
483
- { type: "selectRows", description: "Select specific rows by index (row mode only)" },
484
- { type: "getSelectedRowIndices", description: "Get sorted array of selected row indices" },
485
- { type: "getSelectedRows", description: "Get actual row objects for the current selection (works in all modes)" }
486
- ],
487
- configRules: [
488
- {
489
- id: "selection/range-dblclick",
490
- severity: "warn",
491
- message: `"triggerOn: 'dblclick'" has no effect when mode is "range".
492
- → Range selection uses drag interaction (mousedown → mousemove), not click events.
493
- → The "triggerOn" option only affects "cell" and "row" selection modes.`,
494
- check: (e) => e.mode === "range" && e.triggerOn === "dblclick"
495
- }
496
- ]
497
- };
498
- /** @internal */
499
- name = "selection";
500
- /** @internal */
501
- styles = D;
502
- /** @internal */
503
- get defaultConfig() {
504
- return {
505
- mode: "cell",
506
- triggerOn: "click",
507
- enabled: !0,
508
- multiSelect: !0
509
- };
510
- }
511
- // #region Internal State
512
- /** Row selection state (row mode) */
513
- selected = /* @__PURE__ */ new Set();
514
- lastSelected = null;
515
- anchor = null;
516
- /** Range selection state (range mode) */
517
- ranges = [];
518
- activeRange = null;
519
- cellAnchor = null;
520
- isDragging = !1;
521
- /** Pending keyboard navigation update (processed in afterRender) */
522
- pendingKeyboardUpdate = null;
523
- /** Cell selection state (cell mode) */
524
- selectedCell = null;
525
- /** Last synced focus row — used to detect when grid focus moves so selection follows */
526
- lastSyncedFocusRow = -1;
527
- /** Last synced focus col (cell mode) */
528
- lastSyncedFocusCol = -1;
529
- /** True when selection was explicitly set (click/keyboard) — prevents #syncSelectionToFocus from overwriting */
530
- explicitSelection = !1;
531
- // #endregion
532
- // #region Private Helpers - Selection Enabled Check
533
- /**
534
- * Check if selection is enabled at the grid level.
535
- * Grid-wide `selectable: false` or plugin's `enabled: false` disables all selection.
536
- */
537
- isSelectionEnabled() {
538
- return this.config.enabled === !1 ? !1 : this.grid.effectiveConfig?.selectable !== !1;
539
- }
540
- // #endregion
541
- // #region Private Helpers - Selectability
542
- /**
543
- * Check if a row/cell is selectable.
544
- * Returns true if selectable, false if not.
545
- */
546
- checkSelectable(e, t) {
547
- const { isSelectable: s } = this.config;
548
- if (!s) return !0;
549
- const i = this.rows[e];
550
- if (!i) return !1;
551
- const r = t !== void 0 ? this.visibleColumns[t] : void 0;
552
- return s(i, e, r, t);
553
- }
554
- /**
555
- * Check if an entire row is selectable (for row mode).
556
- */
557
- isRowSelectable(e) {
558
- return this.checkSelectable(e);
559
- }
560
- /**
561
- * Check if a cell is selectable (for cell/range modes).
562
- */
563
- isCellSelectable(e, t) {
564
- return this.checkSelectable(e, t);
565
- }
566
- // #endregion
567
- // #region Lifecycle
568
- /** @internal */
569
- attach(e) {
570
- super.attach(e), this.on("filter-applied", () => this.clearSelectionSilent()), this.on("grouping-state-change", () => this.clearSelectionSilent()), this.on("tree-state-change", () => this.clearSelectionSilent());
571
- }
572
- /**
573
- * Handle queries from other plugins.
574
- * @internal
575
- */
576
- handleQuery(e) {
577
- if (e.type === "getSelection")
578
- return this.getSelection();
579
- if (e.type === "getSelectedRowIndices")
580
- return this.getSelectedRowIndices();
581
- if (e.type === "getSelectedRows")
582
- return this.getSelectedRows();
583
- if (e.type === "selectRows")
584
- return this.selectRows(e.context), !0;
585
- }
586
- /** @internal */
587
- detach() {
588
- 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;
589
- }
590
- /**
591
- * Clear selection without emitting an event.
592
- * Used when selection is invalidated by external changes (filtering, grouping, etc.)
593
- */
594
- clearSelectionSilent() {
595
- 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();
596
- }
597
- // #endregion
598
- // #region Event Handlers
599
- /** @internal */
600
- onCellClick(e) {
601
- if (!this.isSelectionEnabled()) return !1;
602
- const { rowIndex: t, colIndex: s, originalEvent: i } = e, { mode: r, triggerOn: o = "click" } = this.config;
603
- if (i.type !== o)
604
- return !1;
605
- const n = e.column, h = n && f(n);
606
- if (r === "cell") {
607
- if (h || !this.isCellSelectable(t, s))
608
- return !1;
609
- const a = this.selectedCell;
610
- return a && a.row === t && a.col === s || (this.selectedCell = { row: t, col: s }, this.emit("selection-change", this.#e()), this.requestAfterRender()), !1;
611
- }
612
- if (r === "row") {
613
- if (!this.isRowSelectable(t))
614
- return !1;
615
- const a = this.config.multiSelect !== !1, d = i.shiftKey && a, c = (i.ctrlKey || i.metaKey) && a, u = n?.meta?.checkboxColumn === !0;
616
- if (d && this.anchor !== null) {
617
- const x = Math.min(this.anchor, t), A = Math.max(this.anchor, t);
618
- c || this.selected.clear();
619
- for (let w = x; w <= A; w++)
620
- this.isRowSelectable(w) && this.selected.add(w);
621
- } else if (c || u && a)
622
- this.selected.has(t) ? this.selected.delete(t) : this.selected.add(t), this.anchor = t;
623
- else {
624
- if (this.selected.size === 1 && this.selected.has(t))
625
- return !1;
626
- this.selected.clear(), this.selected.add(t), this.anchor = t;
627
- }
628
- return this.lastSelected = t, this.explicitSelection = !0, this.emit("selection-change", this.#e()), this.requestAfterRender(), !1;
629
- }
630
- if (r === "range") {
631
- if (h || !this.isCellSelectable(t, s))
632
- return !1;
633
- const a = i.shiftKey, d = (i.ctrlKey || i.metaKey) && this.config.multiSelect !== !1;
634
- if (a && this.cellAnchor) {
635
- const c = m(this.cellAnchor, { row: t, col: s }), u = this.ranges.length > 0 ? this.ranges[this.ranges.length - 1] : null;
636
- if (u && b(u, c))
637
- return !1;
638
- d ? this.ranges.length > 0 ? this.ranges[this.ranges.length - 1] = c : this.ranges.push(c) : this.ranges = [c], this.activeRange = c;
639
- } else if (d) {
640
- const c = {
641
- startRow: t,
642
- startCol: s,
643
- endRow: t,
644
- endCol: s
645
- };
646
- this.ranges.push(c), this.activeRange = c, this.cellAnchor = { row: t, col: s };
647
- } else {
648
- const c = {
649
- startRow: t,
650
- startCol: s,
651
- endRow: t,
652
- endCol: s
653
- };
654
- if (this.ranges.length === 1 && b(this.ranges[0], c))
655
- return !1;
656
- this.ranges = [c], this.activeRange = c, this.cellAnchor = { row: t, col: s };
657
- }
658
- return this.emit("selection-change", this.#e()), this.requestAfterRender(), !1;
659
- }
660
- return !1;
661
- }
662
- /** @internal */
663
- onKeyDown(e) {
664
- if (!this.isSelectionEnabled()) return !1;
665
- const { mode: t } = this.config, i = ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Tab", "Home", "End", "PageUp", "PageDown"].includes(e.key);
666
- if (e.key === "Escape")
667
- return this.grid.query("isEditing").some(Boolean) ? !1 : (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);
668
- if (t === "cell" && i)
669
- return queueMicrotask(() => {
670
- const r = this.grid._focusRow, o = this.grid._focusCol;
671
- this.isCellSelectable(r, o) ? this.selectedCell = { row: r, col: o } : this.selectedCell = null, this.emit("selection-change", this.#e()), this.requestAfterRender();
672
- }), !1;
673
- if (t === "row") {
674
- const r = this.config.multiSelect !== !1;
675
- if (e.key === "ArrowUp" || e.key === "ArrowDown") {
676
- const o = e.shiftKey && r;
677
- return o && this.anchor === null && (this.anchor = this.grid._focusRow), queueMicrotask(() => {
678
- const n = this.grid._focusRow;
679
- if (o && this.anchor !== null) {
680
- this.selected.clear();
681
- const h = Math.min(this.anchor, n), a = Math.max(this.anchor, n);
682
- for (let d = h; d <= a; d++)
683
- this.isRowSelectable(d) && this.selected.add(d);
684
- } else
685
- this.isRowSelectable(n) ? (this.selected.clear(), this.selected.add(n), this.anchor = n) : this.selected.clear();
686
- this.lastSelected = n, this.explicitSelection = !0, this.emit("selection-change", this.#e()), this.requestAfterRender();
687
- }), !1;
688
- }
689
- if (r && e.key === "a" && (e.ctrlKey || e.metaKey))
690
- return this.grid.query("isEditing").some(Boolean) ? !1 : (e.preventDefault(), e.stopPropagation(), this.selectAll(), !0);
691
- }
692
- if (t === "range" && i) {
693
- const r = e.key === "Tab", o = e.shiftKey && !r;
694
- return o && !this.cellAnchor && (this.cellAnchor = { row: this.grid._focusRow, col: this.grid._focusCol }), this.pendingKeyboardUpdate = { shiftKey: o }, queueMicrotask(() => this.requestAfterRender()), !1;
695
- }
696
- return t === "range" && this.config.multiSelect !== !1 && e.key === "a" && (e.ctrlKey || e.metaKey) ? this.grid.query("isEditing").some(Boolean) ? !1 : (e.preventDefault(), e.stopPropagation(), this.selectAll(), !0) : !1;
697
- }
698
- /** @internal */
699
- onCellMouseDown(e) {
700
- if (!this.isSelectionEnabled() || this.config.mode !== "range" || e.rowIndex === void 0 || e.colIndex === void 0 || e.rowIndex < 0 || e.column && f(e.column) || !this.isCellSelectable(e.rowIndex, e.colIndex) || e.originalEvent.shiftKey && this.cellAnchor)
701
- return;
702
- this.isDragging = !0;
703
- const t = e.rowIndex, s = e.colIndex, i = (e.originalEvent.ctrlKey || e.originalEvent.metaKey) && this.config.multiSelect !== !1, r = {
704
- startRow: t,
705
- startCol: s,
706
- endRow: t,
707
- endCol: s
708
- };
709
- return !i && this.ranges.length === 1 && b(this.ranges[0], r) ? (this.cellAnchor = { row: t, col: s }, !0) : (this.cellAnchor = { row: t, col: s }, i || (this.ranges = []), this.ranges.push(r), this.activeRange = r, this.emit("selection-change", this.#e()), this.requestAfterRender(), !0);
710
- }
711
- /** @internal */
712
- onCellMouseMove(e) {
713
- if (!this.isSelectionEnabled() || this.config.mode !== "range" || !this.isDragging || !this.cellAnchor || e.rowIndex === void 0 || e.colIndex === void 0 || e.rowIndex < 0) return;
714
- let t = e.colIndex;
715
- const s = this.visibleColumns[t];
716
- if (s && f(s)) {
717
- const o = this.visibleColumns.findIndex((n) => !f(n));
718
- o >= 0 && (t = o);
719
- }
720
- const i = m(this.cellAnchor, { row: e.rowIndex, col: t }), r = this.ranges.length > 0 ? this.ranges[this.ranges.length - 1] : null;
721
- return r && b(r, 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;
722
- }
723
- /** @internal */
724
- onCellMouseUp(e) {
725
- if (this.isSelectionEnabled() && this.config.mode === "range" && this.isDragging)
726
- return this.isDragging = !1, !0;
727
- }
728
- // #region Checkbox Column
729
- /**
730
- * Inject checkbox column when `checkbox: true` and mode is `'row'`.
731
- * @internal
732
- */
733
- processColumns(e) {
734
- if (this.config.checkbox && this.config.mode === "row") {
735
- if (e.some((r) => r.field === S))
736
- return e;
737
- const t = this.#t(), s = e.findIndex(I), i = s >= 0 ? s + 1 : 0;
738
- return [...e.slice(0, i), t, ...e.slice(i)];
739
- }
740
- return e;
741
- }
742
- /**
743
- * Create the checkbox utility column configuration.
744
- */
745
- #t() {
746
- return {
747
- field: S,
748
- header: "",
749
- width: 32,
750
- resizable: !1,
751
- sortable: !1,
752
- meta: {
753
- lockPosition: !0,
754
- suppressMovable: !0,
755
- utility: !0,
756
- checkboxColumn: !0
757
- },
758
- headerRenderer: () => {
759
- const e = document.createElement("div");
760
- if (e.className = "tbw-checkbox-header", this.config.multiSelect === !1) return e;
761
- const t = document.createElement("input");
762
- return t.type = "checkbox", t.className = "tbw-select-all-checkbox", t.addEventListener("click", (s) => {
763
- s.stopPropagation(), s.target.checked ? this.selectAll() : this.clearSelection();
764
- }), e.appendChild(t), e;
765
- },
766
- renderer: (e) => {
767
- const t = document.createElement("input");
768
- t.type = "checkbox", t.className = "tbw-select-row-checkbox";
769
- const s = e.cellEl;
770
- if (s) {
771
- const i = parseInt(s.getAttribute("data-row") ?? "-1", 10);
772
- i >= 0 && (t.checked = this.selected.has(i));
773
- }
774
- return t;
775
- }
776
- };
777
- }
778
- /**
779
- * Update checkbox checked states to reflect current selection.
780
- * Called from #applySelectionClasses.
781
- */
782
- #i(e) {
783
- e.querySelectorAll(".tbw-select-row-checkbox").forEach((i) => {
784
- const r = i.closest(".cell"), o = r ? R(r) : -1;
785
- o >= 0 && (i.checked = this.selected.has(o));
786
- });
787
- const s = e.querySelector(".tbw-select-all-checkbox");
788
- if (s) {
789
- const i = this.rows.length;
790
- let r = 0;
791
- if (this.config.isSelectable)
792
- for (let h = 0; h < i; h++)
793
- this.isRowSelectable(h) && r++;
794
- else
795
- r = i;
796
- const o = r > 0 && this.selected.size >= r, n = this.selected.size > 0;
797
- s.checked = o, s.indeterminate = n && !o;
798
- }
799
- }
800
- // #endregion
801
- /**
802
- * Sync selection state to the grid's current focus position.
803
- * In row mode, keeps `selected` in sync with `_focusRow`.
804
- * In cell mode, keeps `selectedCell` in sync with `_focusRow`/`_focusCol`.
805
- * Only updates when the focus has changed since the last sync.
806
- * Skips when `explicitSelection` is set (click/keyboard set selection directly).
807
- */
808
- #r(e) {
809
- const t = this.grid._focusRow, s = this.grid._focusCol;
810
- if (e === "row") {
811
- if (this.explicitSelection) {
812
- this.explicitSelection = !1, this.lastSyncedFocusRow = t;
813
- return;
814
- }
815
- 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())));
816
- }
817
- if (e === "cell") {
818
- if (this.explicitSelection) {
819
- this.explicitSelection = !1, this.lastSyncedFocusRow = t, this.lastSyncedFocusCol = s;
820
- return;
821
- }
822
- if ((t !== this.lastSyncedFocusRow || s !== this.lastSyncedFocusCol) && (this.lastSyncedFocusRow = t, this.lastSyncedFocusCol = s, this.isCellSelectable(t, s))) {
823
- const i = this.selectedCell;
824
- (!i || i.row !== t || i.col !== s) && (this.selectedCell = { row: t, col: s }, this.emit("selection-change", this.#e()));
825
- }
826
- }
827
- }
828
- /**
829
- * Apply CSS selection classes to row/cell elements.
830
- * Shared by afterRender and onScrollRender.
831
- */
832
- #s() {
833
- const e = this.gridElement;
834
- if (!e) return;
835
- const { mode: t } = this.config, s = !!this.config.isSelectable;
836
- e.querySelectorAll(".cell").forEach((o) => {
837
- o.classList.remove("selected", "top", "bottom", "first", "last"), s && o.removeAttribute("data-selectable");
838
- });
839
- const r = e.querySelectorAll(".data-grid-row");
840
- if (r.forEach((o) => {
841
- o.classList.remove("selected", "row-focus"), s && o.removeAttribute("data-selectable");
842
- }), t === "row" && (C(e), r.forEach((o) => {
843
- const n = o.querySelector(".cell[data-row]"), h = R(n);
844
- h >= 0 && (s && !this.isRowSelectable(h) && o.setAttribute("data-selectable", "false"), this.selected.has(h) && o.classList.add("selected", "row-focus"));
845
- }), this.config.checkbox && this.#i(e)), (t === "cell" || t === "range") && s && e.querySelectorAll(".cell[data-row][data-col]").forEach((n) => {
846
- const h = parseInt(n.getAttribute("data-row") ?? "-1", 10), a = parseInt(n.getAttribute("data-col") ?? "-1", 10);
847
- h >= 0 && a >= 0 && (this.isCellSelectable(h, a) || n.setAttribute("data-selectable", "false"));
848
- }), t === "range" && this.ranges.length > 0) {
849
- C(e);
850
- const o = this.ranges.map(g), n = (a, d) => {
851
- for (const c of o)
852
- if (a >= c.startRow && a <= c.endRow && d >= c.startCol && d <= c.endCol)
853
- return !0;
854
- return !1;
855
- };
856
- e.querySelectorAll(".cell[data-row][data-col]").forEach((a) => {
857
- const d = parseInt(a.getAttribute("data-row") ?? "-1", 10), c = parseInt(a.getAttribute("data-col") ?? "-1", 10);
858
- if (d >= 0 && c >= 0) {
859
- const u = this.visibleColumns[c];
860
- if (u && f(u))
861
- return;
862
- n(d, c) && (a.classList.add("selected"), n(d - 1, c) || a.classList.add("top"), n(d + 1, c) || a.classList.add("bottom"), n(d, c - 1) || a.classList.add("first"), n(d, c + 1) || a.classList.add("last"));
863
- }
864
- });
865
- }
866
- }
867
- /** @internal */
868
- afterRender() {
869
- if (!this.isSelectionEnabled()) return;
870
- const e = this.gridElement;
871
- if (!e) return;
872
- const t = e.children[0], { mode: s } = this.config;
873
- if (this.pendingKeyboardUpdate && s === "range") {
874
- const { shiftKey: i } = this.pendingKeyboardUpdate;
875
- this.pendingKeyboardUpdate = null;
876
- const r = this.grid._focusRow, o = this.grid._focusCol;
877
- if (i && this.cellAnchor) {
878
- const n = m(this.cellAnchor, { row: r, col: o });
879
- this.ranges = [n], this.activeRange = n;
880
- } else i || (this.ranges = [], this.activeRange = null, this.cellAnchor = { row: r, col: o });
881
- this.emit("selection-change", this.#e());
882
- }
883
- this.#r(s), this.grid.setAttribute("data-selection-mode", s), t && t.classList.toggle("selecting", this.isDragging), this.#s();
884
- }
885
- /**
886
- * Called after scroll-triggered row rendering.
887
- * Reapplies selection classes to recycled DOM elements.
888
- * @internal
889
- */
890
- onScrollRender() {
891
- this.isSelectionEnabled() && this.#s();
892
- }
893
- // #endregion
894
- // #region Public API
895
- /**
896
- * Get the current selection as a unified result.
897
- * Works for all selection modes and always returns ranges.
898
- *
899
- * @example
900
- * ```ts
901
- * const selection = plugin.getSelection();
902
- * if (selection.ranges.length > 0) {
903
- * const { from, to } = selection.ranges[0];
904
- * // For cell mode: from === to (single cell)
905
- * // For row mode: from.col = 0, to.col = lastCol (full row)
906
- * // For range mode: rectangular selection
907
- * }
908
- * ```
909
- */
910
- getSelection() {
911
- return {
912
- mode: this.config.mode,
913
- ranges: this.#e().ranges,
914
- anchor: this.cellAnchor
915
- };
916
- }
917
- /**
918
- * Get all selected cells across all ranges.
919
- */
920
- getSelectedCells() {
921
- return F(this.ranges);
922
- }
923
- /**
924
- * Check if a specific cell is in range selection.
925
- */
926
- isCellSelected(e, t) {
927
- return K(e, t, this.ranges);
928
- }
929
- /**
930
- * Select all selectable rows (row mode) or all cells (range mode).
931
- *
932
- * In row mode, selects every row where `isSelectable` returns true (or all rows if no callback).
933
- * In range mode, creates a single range spanning all rows and columns.
934
- * Has no effect in cell mode.
935
- *
936
- * @example
937
- * ```ts
938
- * const plugin = grid.getPlugin(SelectionPlugin);
939
- * plugin.selectAll(); // Selects everything in current mode
940
- * ```
941
- */
942
- selectAll() {
943
- const { mode: e, multiSelect: t } = this.config;
944
- if (t !== !1) {
945
- if (e === "row") {
946
- this.selected.clear();
947
- for (let s = 0; s < this.rows.length; s++)
948
- this.isRowSelectable(s) && this.selected.add(s);
949
- this.explicitSelection = !0, this.emit("selection-change", this.#e()), this.requestAfterRender();
950
- } else if (e === "range") {
951
- const s = this.rows.length, i = this.columns.length;
952
- if (s > 0 && i > 0) {
953
- const r = {
954
- startRow: 0,
955
- startCol: 0,
956
- endRow: s - 1,
957
- endCol: i - 1
958
- };
959
- this.ranges = [r], this.activeRange = r, this.emit("selection-change", this.#e()), this.requestAfterRender();
960
- }
961
- }
962
- }
963
- }
964
- /**
965
- * Select specific rows by index (row mode only).
966
- * Replaces the current selection with the provided row indices.
967
- * Indices that are out of bounds or fail the `isSelectable` check are ignored.
968
- *
969
- * @param indices - Array of row indices to select
970
- *
971
- * @example
972
- * ```ts
973
- * const plugin = grid.getPlugin(SelectionPlugin);
974
- * plugin.selectRows([0, 2, 4]); // Select rows 0, 2, and 4
975
- * ```
976
- */
977
- selectRows(e) {
978
- if (this.config.mode !== "row") return;
979
- const t = this.config.multiSelect === !1 && e.length > 1 ? [e[e.length - 1]] : e;
980
- this.selected.clear();
981
- for (const s of t)
982
- s >= 0 && s < this.rows.length && this.isRowSelectable(s) && this.selected.add(s);
983
- this.anchor = t.length > 0 ? t[t.length - 1] : null, this.explicitSelection = !0, this.emit("selection-change", this.#e()), this.requestAfterRender();
984
- }
985
- /**
986
- * Get the indices of all selected rows (convenience for row mode).
987
- * Returns indices sorted in ascending order.
988
- *
989
- * @example
990
- * ```ts
991
- * const plugin = grid.getPlugin(SelectionPlugin);
992
- * const rows = plugin.getSelectedRowIndices(); // [0, 2, 4]
993
- * ```
994
- */
995
- getSelectedRowIndices() {
996
- return [...this.selected].sort((e, t) => e - t);
997
- }
998
- /**
999
- * Get the actual row objects for the current selection.
1000
- *
1001
- * Works across all selection modes:
1002
- * - **Row mode**: Returns the row objects for all selected rows.
1003
- * - **Cell mode**: Returns the single row containing the selected cell, or `[]`.
1004
- * - **Range mode**: Returns the unique row objects that intersect any selected range.
1005
- *
1006
- * Row objects are resolved from the grid's processed (sorted/filtered) row array,
1007
- * so they always reflect the current visual order.
1008
- *
1009
- * @example
1010
- * ```ts
1011
- * const plugin = grid.getPlugin(SelectionPlugin);
1012
- * const selected = plugin.getSelectedRows(); // [{ id: 1, name: 'Alice' }, ...]
1013
- * ```
1014
- */
1015
- getSelectedRows() {
1016
- const { mode: e } = this.config, t = this.rows;
1017
- if (e === "row")
1018
- return this.getSelectedRowIndices().filter((s) => s >= 0 && s < t.length).map((s) => t[s]);
1019
- if (e === "cell" && this.selectedCell) {
1020
- const { row: s } = this.selectedCell;
1021
- return s >= 0 && s < t.length ? [t[s]] : [];
1022
- }
1023
- if (e === "range" && this.ranges.length > 0) {
1024
- const s = /* @__PURE__ */ new Set();
1025
- for (const i of this.ranges) {
1026
- const r = Math.max(0, Math.min(i.startRow, i.endRow)), o = Math.min(t.length - 1, Math.max(i.startRow, i.endRow));
1027
- for (let n = r; n <= o; n++)
1028
- s.add(n);
1029
- }
1030
- return [...s].sort((i, r) => i - r).map((i) => t[i]);
1031
- }
1032
- return [];
1033
- }
1034
- /**
1035
- * Clear all selection.
1036
- */
1037
- clearSelection() {
1038
- this.selectedCell = null, this.selected.clear(), this.anchor = null, this.ranges = [], this.activeRange = null, this.cellAnchor = null, this.emit("selection-change", { mode: this.config.mode, ranges: [] }), this.requestAfterRender();
1039
- }
1040
- /**
1041
- * Set selected ranges programmatically.
1042
- */
1043
- setRanges(e) {
1044
- this.ranges = e.map((t) => ({
1045
- startRow: t.from.row,
1046
- startCol: t.from.col,
1047
- endRow: t.to.row,
1048
- endCol: t.to.col
1049
- })), this.activeRange = this.ranges.length > 0 ? this.ranges[this.ranges.length - 1] : null, this.emit("selection-change", {
1050
- mode: this.config.mode,
1051
- ranges: y(this.ranges)
1052
- }), this.requestAfterRender();
1053
- }
1054
- // #endregion
1055
- // #region Private Helpers
1056
- #e() {
1057
- return L(
1058
- this.config.mode,
1059
- {
1060
- selectedCell: this.selectedCell,
1061
- selected: this.selected,
1062
- ranges: this.ranges
1063
- },
1064
- this.columns.length
1065
- );
1066
- }
1067
- // #endregion
1068
- }
1069
- export {
1070
- P as SelectionPlugin
1071
- };
1
+ function e(e){if(!e)return-1;const t=e.getAttribute("data-row");if(t)return parseInt(t,10);const s=e.closest(".data-grid-row");if(!s)return-1;const i=s.parentElement;if(!i)return-1;const r=i.querySelectorAll(":scope > .data-grid-row");for(let o=0;o<r.length;o++)if(r[o]===s)return o;return-1}function t(e){e&&e.querySelectorAll(".cell-focus").forEach(e=>e.classList.remove("cell-focus"))}const s='<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>',i={expand:"▶",collapse:"▼",sortAsc:"▲",sortDesc:"▼",sortNone:"⇅",submenuArrow:"▶",dragHandle:"⋮⋮",toolPanel:"☰",filter:s,filterActive:s,print:"🖨️"};class r{static dependencies;static manifest;version="undefined"!=typeof __GRID_VERSION__?__GRID_VERSION__:"dev";styles;cellRenderers;headerRenderers;cellEditors;grid;config;userConfig;#e;get defaultConfig(){return{}}constructor(e={}){this.userConfig=e}attach(e){this.#e?.abort(),this.#e=new AbortController,this.grid=e,this.config={...this.defaultConfig,...this.userConfig}}detach(){this.#e?.abort(),this.#e=void 0}getPlugin(e){return this.grid?.getPlugin(e)}emit(e,t){this.grid?.dispatchEvent?.(new CustomEvent(e,{detail:t,bubbles:!0}))}emitCancelable(e,t){const s=new CustomEvent(e,{detail:t,bubbles:!0,cancelable:!0});return this.grid?.dispatchEvent?.(s),s.defaultPrevented}on(e,t){this.grid?._pluginManager?.subscribe(this,e,t)}off(e){this.grid?._pluginManager?.unsubscribe(this,e)}emitPluginEvent(e,t){this.grid?._pluginManager?.emitPluginEvent(e,t)}requestRender(){this.grid?.requestRender?.()}requestColumnsRender(){this.grid?.requestColumnsRender?.()}requestRenderWithFocus(){this.grid?.requestRenderWithFocus?.()}requestAfterRender(){this.grid?.requestAfterRender?.()}get rows(){return this.grid?.rows??[]}get sourceRows(){return this.grid?.sourceRows??[]}get columns(){return this.grid?.columns??[]}get visibleColumns(){return this.grid?._visibleColumns??[]}get gridElement(){return this.grid}get disconnectSignal(){return this.#e?.signal??this.grid?.disconnectSignal}get gridIcons(){const e=this.grid?.gridConfig?.icons??{};return{...i,...e}}get isAnimationEnabled(){const e=this.grid?.effectiveConfig?.animation?.mode??"reduced-motion";if(!1===e||"off"===e)return!1;if(!0===e||"on"===e)return!0;const t=this.gridElement;if(t){return"0"!==getComputedStyle(t).getPropertyValue("--tbw-animation-enabled").trim()}return!0}get animationDuration(){const e=this.gridElement;if(e){const t=getComputedStyle(e).getPropertyValue("--tbw-animation-duration").trim(),s=parseInt(t,10);if(!isNaN(s))return s}return 200}resolveIcon(e,t){return void 0!==t?t:this.gridIcons[e]}setIcon(e,t){"string"==typeof t?e.innerHTML=t:t instanceof HTMLElement&&(e.innerHTML="",e.appendChild(t.cloneNode(!0)))}warn(e){console.warn(`[tbw-grid:${this.name}] ${e}`)}}function o(e){return"__tbw_expander"===e.field}function n(e){return!0===e.meta?.utility}function l(e){return{startRow:Math.min(e.startRow,e.endRow),startCol:Math.min(e.startCol,e.endCol),endRow:Math.max(e.startRow,e.endRow),endCol:Math.max(e.startCol,e.endCol)}}function c(e){const t=l(e);return{from:{row:t.startRow,col:t.startCol},to:{row:t.endRow,col:t.endCol}}}function a(e){return e.map(c)}function d(e,t,s){return s.some(s=>function(e,t,s){const i=l(s);return e>=i.startRow&&e<=i.endRow&&t>=i.startCol&&t<=i.endCol}(e,t,s))}function h(e){const t=[],s=l(e);for(let i=s.startRow;i<=s.endRow;i++)for(let e=s.startCol;e<=s.endCol;e++)t.push({row:i,col:e});return t}function g(e,t){return{startRow:e.row,startCol:e.col,endRow:t.row,endCol:t.col}}function u(e,t){const s=l(e),i=l(t);return s.startRow===i.startRow&&s.startCol===i.startCol&&s.endRow===i.endRow&&s.endCol===i.endCol}const w="__tbw_checkbox";class f extends r{static manifest={queries:[{type:"getSelection",description:"Get the current selection state"},{type:"selectRows",description:"Select specific rows by index (row mode only)"},{type:"getSelectedRowIndices",description:"Get sorted array of selected row indices"},{type:"getSelectedRows",description:"Get actual row objects for the current selection (works in all modes)"}],configRules:[{id:"selection/range-dblclick",severity:"warn",message:'"triggerOn: \'dblclick\'" has no effect when mode is "range".\n → Range selection uses drag interaction (mousedown → mousemove), not click events.\n → The "triggerOn" option only affects "cell" and "row" selection modes.',check:e=>"range"===e.mode&&"dblclick"===e.triggerOn}]};name="selection";styles='@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-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-grid-row.row-focus:after{content:"";position:absolute;inset:0;pointer-events:none;border-width:0;border-style:var(--tbw-selection-border-style, var(--tbw-border-style));border-color:var(--tbw-range-border-color, var(--tbw-color-accent));border-top-width:var(--tbw-selection-border-width, var(--tbw-border-width));border-bottom-width:var(--tbw-selection-border-width, var(--tbw-border-width));z-index:1}tbw-grid .data-grid-row.row-focus+.data-grid-row.row-focus:after{border-top-width:0}tbw-grid .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 var(--tbw-selection-border-style, var(--tbw-border-style)) var(--tbw-range-border-color);z-index:1}tbw-grid .data-grid-row>.cell.selected.top:after{border-top-width:var(--tbw-selection-border-width, var(--tbw-border-width))}tbw-grid .data-grid-row>.cell.selected.bottom:after{border-bottom-width:var(--tbw-selection-border-width, var(--tbw-border-width))}tbw-grid .data-grid-row>.cell.selected.first:after{border-left-width:var(--tbw-selection-border-width, var(--tbw-border-width))}tbw-grid .data-grid-row>.cell.selected.last:after{border-right-width:var(--tbw-selection-border-width, var(--tbw-border-width))}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}}';get defaultConfig(){return{mode:"cell",triggerOn:"click",enabled:!0,multiSelect:!0}}selected=/* @__PURE__ */new Set;lastSelected=null;anchor=null;ranges=[];activeRange=null;cellAnchor=null;isDragging=!1;pendingKeyboardUpdate=null;selectedCell=null;lastSyncedFocusRow=-1;lastSyncedFocusCol=-1;explicitSelection=!1;isSelectionEnabled(){return!1!==this.config.enabled&&!1!==this.grid.effectiveConfig?.selectable}checkSelectable(e,t){const{isSelectable:s}=this.config;if(!s)return!0;const i=this.rows[e];if(!i)return!1;return s(i,e,void 0!==t?this.visibleColumns[t]:void 0,t)}isRowSelectable(e){return this.checkSelectable(e)}isCellSelectable(e,t){return this.checkSelectable(e,t)}attach(e){super.attach(e),this.on("filter-applied",()=>this.clearSelectionSilent()),this.on("grouping-state-change",()=>this.clearSelectionSilent()),this.on("tree-state-change",()=>this.clearSelectionSilent())}handleQuery(e){return"getSelection"===e.type?this.getSelection():"getSelectedRowIndices"===e.type?this.getSelectedRowIndices():"getSelectedRows"===e.type?this.getSelectedRows():"selectRows"===e.type?(this.selectRows(e.context),!0):void 0}detach(){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}clearSelectionSilent(){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()}onCellClick(e){if(!this.isSelectionEnabled())return!1;const{rowIndex:t,colIndex:s,originalEvent:i}=e,{mode:r,triggerOn:o="click"}=this.config;if(i.type!==o)return!1;const l=e.column,c=l&&n(l);if("cell"===r){if(c)return!1;if(!this.isCellSelectable(t,s))return!1;const e=this.selectedCell;return e&&e.row===t&&e.col===s||(this.selectedCell={row:t,col:s},this.emit("selection-change",this.#t()),this.requestAfterRender()),!1}if("row"===r){if(!this.isRowSelectable(t))return!1;const e=!1!==this.config.multiSelect,s=i.shiftKey&&e,r=(i.ctrlKey||i.metaKey)&&e,o=!0===l?.meta?.checkboxColumn;if(s&&null!==this.anchor){const e=Math.min(this.anchor,t),s=Math.max(this.anchor,t);r||this.selected.clear();for(let t=e;t<=s;t++)this.isRowSelectable(t)&&this.selected.add(t)}else if(r||o&&e)this.selected.has(t)?this.selected.delete(t):this.selected.add(t),this.anchor=t;else{if(1===this.selected.size&&this.selected.has(t))return!1;this.selected.clear(),this.selected.add(t),this.anchor=t}return this.lastSelected=t,this.explicitSelection=!0,this.emit("selection-change",this.#t()),this.requestAfterRender(),!1}if("range"===r){if(c)return!1;if(!this.isCellSelectable(t,s))return!1;const e=i.shiftKey,r=(i.ctrlKey||i.metaKey)&&!1!==this.config.multiSelect;if(e&&this.cellAnchor){const e=g(this.cellAnchor,{row:t,col:s}),i=this.ranges.length>0?this.ranges[this.ranges.length-1]:null;if(i&&u(i,e))return!1;r?this.ranges.length>0?this.ranges[this.ranges.length-1]=e:this.ranges.push(e):this.ranges=[e],this.activeRange=e}else if(r){const e={startRow:t,startCol:s,endRow:t,endCol:s};this.ranges.push(e),this.activeRange=e,this.cellAnchor={row:t,col:s}}else{const e={startRow:t,startCol:s,endRow:t,endCol:s};if(1===this.ranges.length&&u(this.ranges[0],e))return!1;this.ranges=[e],this.activeRange=e,this.cellAnchor={row:t,col:s}}return this.emit("selection-change",this.#t()),this.requestAfterRender(),!1}return!1}onKeyDown(e){if(!this.isSelectionEnabled())return!1;const{mode:t}=this.config,s=["ArrowUp","ArrowDown","ArrowLeft","ArrowRight","Tab","Home","End","PageUp","PageDown"].includes(e.key);if("Escape"===e.key){return!this.grid.query("isEditing").some(Boolean)&&("cell"===t?this.selectedCell=null:"row"===t?(this.selected.clear(),this.anchor=null):"range"===t&&(this.ranges=[],this.activeRange=null,this.cellAnchor=null),this.emit("selection-change",this.#t()),this.requestAfterRender(),!0)}if("cell"===t&&s)return queueMicrotask(()=>{const e=this.grid._focusRow,t=this.grid._focusCol;this.isCellSelectable(e,t)?this.selectedCell={row:e,col:t}:this.selectedCell=null,this.emit("selection-change",this.#t()),this.requestAfterRender()}),!1;if("row"===t){const t=!1!==this.config.multiSelect;if("ArrowUp"===e.key||"ArrowDown"===e.key){const s=e.shiftKey&&t;return s&&null===this.anchor&&(this.anchor=this.grid._focusRow),queueMicrotask(()=>{const e=this.grid._focusRow;if(s&&null!==this.anchor){this.selected.clear();const t=Math.min(this.anchor,e),s=Math.max(this.anchor,e);for(let e=t;e<=s;e++)this.isRowSelectable(e)&&this.selected.add(e)}else this.isRowSelectable(e)?(this.selected.clear(),this.selected.add(e),this.anchor=e):this.selected.clear();this.lastSelected=e,this.explicitSelection=!0,this.emit("selection-change",this.#t()),this.requestAfterRender()}),!1}if(t&&"a"===e.key&&(e.ctrlKey||e.metaKey)){return!this.grid.query("isEditing").some(Boolean)&&(e.preventDefault(),e.stopPropagation(),this.selectAll(),!0)}}if("range"===t&&s){const t="Tab"===e.key,s=e.shiftKey&&!t;return s&&!this.cellAnchor&&(this.cellAnchor={row:this.grid._focusRow,col:this.grid._focusCol}),this.pendingKeyboardUpdate={shiftKey:s},queueMicrotask(()=>this.requestAfterRender()),!1}if("range"===t&&!1!==this.config.multiSelect&&"a"===e.key&&(e.ctrlKey||e.metaKey)){return!this.grid.query("isEditing").some(Boolean)&&(e.preventDefault(),e.stopPropagation(),this.selectAll(),!0)}return!1}onCellMouseDown(e){if(!this.isSelectionEnabled())return;if("range"!==this.config.mode)return;if(void 0===e.rowIndex||void 0===e.colIndex)return;if(e.rowIndex<0)return;if(e.column&&n(e.column))return;if(!this.isCellSelectable(e.rowIndex,e.colIndex))return;if(e.originalEvent.shiftKey&&this.cellAnchor)return;this.isDragging=!0;const t=e.rowIndex,s=e.colIndex,i=(e.originalEvent.ctrlKey||e.originalEvent.metaKey)&&!1!==this.config.multiSelect,r={startRow:t,startCol:s,endRow:t,endCol:s};return!i&&1===this.ranges.length&&u(this.ranges[0],r)?(this.cellAnchor={row:t,col:s},!0):(this.cellAnchor={row:t,col:s},i||(this.ranges=[]),this.ranges.push(r),this.activeRange=r,this.emit("selection-change",this.#t()),this.requestAfterRender(),!0)}onCellMouseMove(e){if(!this.isSelectionEnabled())return;if("range"!==this.config.mode)return;if(!this.isDragging||!this.cellAnchor)return;if(void 0===e.rowIndex||void 0===e.colIndex)return;if(e.rowIndex<0)return;let t=e.colIndex;const s=this.visibleColumns[t];if(s&&n(s)){const e=this.visibleColumns.findIndex(e=>!n(e));e>=0&&(t=e)}const i=g(this.cellAnchor,{row:e.rowIndex,col:t}),r=this.ranges.length>0?this.ranges[this.ranges.length-1]:null;return r&&u(r,i)||(this.ranges.length>0?this.ranges[this.ranges.length-1]=i:this.ranges.push(i),this.activeRange=i,this.emit("selection-change",this.#t()),this.requestAfterRender()),!0}onCellMouseUp(e){if(this.isSelectionEnabled()&&"range"===this.config.mode)return this.isDragging?(this.isDragging=!1,!0):void 0}processColumns(e){if(this.config.checkbox&&"row"===this.config.mode){if(e.some(e=>e.field===w))return e;const t=this.#s(),s=e.findIndex(o),i=s>=0?s+1:0;return[...e.slice(0,i),t,...e.slice(i)]}return e}#s(){return{field:w,header:"",width:32,resizable:!1,sortable:!1,meta:{lockPosition:!0,suppressMovable:!0,utility:!0,checkboxColumn:!0},headerRenderer:()=>{const e=document.createElement("div");if(e.className="tbw-checkbox-header",!1===this.config.multiSelect)return e;const t=document.createElement("input");return t.type="checkbox",t.className="tbw-select-all-checkbox",t.addEventListener("click",e=>{e.stopPropagation(),e.target.checked?this.selectAll():this.clearSelection()}),e.appendChild(t),e},renderer:e=>{const t=document.createElement("input");t.type="checkbox",t.className="tbw-select-row-checkbox";const s=e.cellEl;if(s){const e=parseInt(s.getAttribute("data-row")??"-1",10);e>=0&&(t.checked=this.selected.has(e))}return t}}}#i(t){t.querySelectorAll(".tbw-select-row-checkbox").forEach(t=>{const s=t.closest(".cell"),i=s?e(s):-1;i>=0&&(t.checked=this.selected.has(i))});const s=t.querySelector(".tbw-select-all-checkbox");if(s){const e=this.rows.length;let t=0;if(this.config.isSelectable)for(let s=0;s<e;s++)this.isRowSelectable(s)&&t++;else t=e;const i=t>0&&this.selected.size>=t,r=this.selected.size>0;s.checked=i,s.indeterminate=r&&!i}}#r(e){const t=this.grid._focusRow,s=this.grid._focusCol;if("row"===e){if(this.explicitSelection)return this.explicitSelection=!1,void(this.lastSyncedFocusRow=t);t!==this.lastSyncedFocusRow&&(this.lastSyncedFocusRow=t,this.isRowSelectable(t)&&(this.selected.has(t)&&1===this.selected.size||(this.selected.clear(),this.selected.add(t),this.lastSelected=t,this.anchor=t,this.emit("selection-change",this.#t()))))}if("cell"===e){if(this.explicitSelection)return this.explicitSelection=!1,this.lastSyncedFocusRow=t,void(this.lastSyncedFocusCol=s);if((t!==this.lastSyncedFocusRow||s!==this.lastSyncedFocusCol)&&(this.lastSyncedFocusRow=t,this.lastSyncedFocusCol=s,this.isCellSelectable(t,s))){const e=this.selectedCell;e&&e.row===t&&e.col===s||(this.selectedCell={row:t,col:s},this.emit("selection-change",this.#t()))}}}#o(){const s=this.gridElement;if(!s)return;const{mode:i}=this.config,r=!!this.config.isSelectable;s.querySelectorAll(".cell").forEach(e=>{e.classList.remove("selected","top","bottom","first","last"),r&&e.removeAttribute("data-selectable")});const o=s.querySelectorAll(".data-grid-row");if(o.forEach(e=>{e.classList.remove("selected","row-focus"),r&&e.removeAttribute("data-selectable")}),"row"===i&&(t(s),o.forEach(t=>{const s=e(t.querySelector(".cell[data-row]"));s>=0&&(r&&!this.isRowSelectable(s)&&t.setAttribute("data-selectable","false"),this.selected.has(s)&&t.classList.add("selected","row-focus"))}),this.config.checkbox&&this.#i(s)),("cell"===i||"range"===i)&&r){s.querySelectorAll(".cell[data-row][data-col]").forEach(e=>{const t=parseInt(e.getAttribute("data-row")??"-1",10),s=parseInt(e.getAttribute("data-col")??"-1",10);t>=0&&s>=0&&(this.isCellSelectable(t,s)||e.setAttribute("data-selectable","false"))})}if("range"===i&&this.ranges.length>0){t(s);const e=this.ranges.map(l),i=(t,s)=>{for(const i of e)if(t>=i.startRow&&t<=i.endRow&&s>=i.startCol&&s<=i.endCol)return!0;return!1};s.querySelectorAll(".cell[data-row][data-col]").forEach(e=>{const t=parseInt(e.getAttribute("data-row")??"-1",10),s=parseInt(e.getAttribute("data-col")??"-1",10);if(t>=0&&s>=0){const r=this.visibleColumns[s];if(r&&n(r))return;i(t,s)&&(e.classList.add("selected"),i(t-1,s)||e.classList.add("top"),i(t+1,s)||e.classList.add("bottom"),i(t,s-1)||e.classList.add("first"),i(t,s+1)||e.classList.add("last"))}})}}afterRender(){if(!this.isSelectionEnabled())return;const e=this.gridElement;if(!e)return;const t=e.children[0],{mode:s}=this.config;if(this.pendingKeyboardUpdate&&"range"===s){const{shiftKey:e}=this.pendingKeyboardUpdate;this.pendingKeyboardUpdate=null;const t=this.grid._focusRow,s=this.grid._focusCol;if(e&&this.cellAnchor){const e=g(this.cellAnchor,{row:t,col:s});this.ranges=[e],this.activeRange=e}else e||(this.ranges=[],this.activeRange=null,this.cellAnchor={row:t,col:s});this.emit("selection-change",this.#t())}this.#r(s),this.grid.setAttribute("data-selection-mode",s),t&&t.classList.toggle("selecting",this.isDragging),this.#o()}onScrollRender(){this.isSelectionEnabled()&&this.#o()}getSelection(){return{mode:this.config.mode,ranges:this.#t().ranges,anchor:this.cellAnchor}}getSelectedCells(){return function(e){const t=/* @__PURE__ */new Map;for(const s of e)for(const e of h(s))t.set(`${e.row},${e.col}`,e);return[...t.values()]}(this.ranges)}isCellSelected(e,t){return d(e,t,this.ranges)}selectAll(){const{mode:e,multiSelect:t}=this.config;if(!1!==t)if("row"===e){this.selected.clear();for(let e=0;e<this.rows.length;e++)this.isRowSelectable(e)&&this.selected.add(e);this.explicitSelection=!0,this.emit("selection-change",this.#t()),this.requestAfterRender()}else if("range"===e){const e=this.rows.length,t=this.columns.length;if(e>0&&t>0){const s={startRow:0,startCol:0,endRow:e-1,endCol:t-1};this.ranges=[s],this.activeRange=s,this.emit("selection-change",this.#t()),this.requestAfterRender()}}}selectRows(e){if("row"!==this.config.mode)return;const t=!1===this.config.multiSelect&&e.length>1?[e[e.length-1]]:e;this.selected.clear();for(const s of t)s>=0&&s<this.rows.length&&this.isRowSelectable(s)&&this.selected.add(s);this.anchor=t.length>0?t[t.length-1]:null,this.explicitSelection=!0,this.emit("selection-change",this.#t()),this.requestAfterRender()}getSelectedRowIndices(){return[...this.selected].sort((e,t)=>e-t)}getSelectedRows(){const{mode:e}=this.config,t=this.rows;if("row"===e)return this.getSelectedRowIndices().filter(e=>e>=0&&e<t.length).map(e=>t[e]);if("cell"===e&&this.selectedCell){const{row:e}=this.selectedCell;return e>=0&&e<t.length?[t[e]]:[]}if("range"===e&&this.ranges.length>0){const e=/* @__PURE__ */new Set;for(const s of this.ranges){const i=Math.max(0,Math.min(s.startRow,s.endRow)),r=Math.min(t.length-1,Math.max(s.startRow,s.endRow));for(let t=i;t<=r;t++)e.add(t)}return[...e].sort((e,t)=>e-t).map(e=>t[e])}return[]}clearSelection(){this.selectedCell=null,this.selected.clear(),this.anchor=null,this.ranges=[],this.activeRange=null,this.cellAnchor=null,this.emit("selection-change",{mode:this.config.mode,ranges:[]}),this.requestAfterRender()}setRanges(e){this.ranges=e.map(e=>({startRow:e.from.row,startCol:e.from.col,endRow:e.to.row,endCol:e.to.col})),this.activeRange=this.ranges.length>0?this.ranges[this.ranges.length-1]:null,this.emit("selection-change",{mode:this.config.mode,ranges:a(this.ranges)}),this.requestAfterRender()}#t(){return function(e,t,s){if("cell"===e&&t.selectedCell)return{mode:e,ranges:[{from:{row:t.selectedCell.row,col:t.selectedCell.col},to:{row:t.selectedCell.row,col:t.selectedCell.col}}]};if("row"===e&&t.selected.size>0){const i=[...t.selected].sort((e,t)=>e-t),r=[];let o=i[0],n=o;for(let e=1;e<i.length;e++)i[e]===n+1?n=i[e]:(r.push({from:{row:o,col:0},to:{row:n,col:s-1}}),o=i[e],n=o);return r.push({from:{row:o,col:0},to:{row:n,col:s-1}}),{mode:e,ranges:r}}return"range"===e&&t.ranges.length>0?{mode:e,ranges:a(t.ranges)}:{mode:e,ranges:[]}}(this.config.mode,{selectedCell:this.selectedCell,selected:this.selected,ranges:this.ranges},this.columns.length)}}export{f as SelectionPlugin};
1072
2
  //# sourceMappingURL=index.js.map