@toolbox-web/grid 1.21.0 → 1.21.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/all.js +2 -6850
  2. package/all.js.map +1 -1
  3. package/index.js +1 -4352
  4. package/index.js.map +1 -1
  5. package/lib/core/grid.d.ts +22 -6
  6. package/lib/core/grid.d.ts.map +1 -1
  7. package/lib/core/styles/index.d.ts.map +1 -1
  8. package/lib/plugins/clipboard/index.js +1 -733
  9. package/lib/plugins/clipboard/index.js.map +1 -1
  10. package/lib/plugins/column-virtualization/index.js +1 -560
  11. package/lib/plugins/column-virtualization/index.js.map +1 -1
  12. package/lib/plugins/context-menu/index.js +1 -754
  13. package/lib/plugins/context-menu/index.js.map +1 -1
  14. package/lib/plugins/editing/EditingPlugin.d.ts.map +1 -1
  15. package/lib/plugins/editing/index.d.ts +1 -1
  16. package/lib/plugins/editing/index.d.ts.map +1 -1
  17. package/lib/plugins/editing/index.js +1 -1539
  18. package/lib/plugins/editing/index.js.map +1 -1
  19. package/lib/plugins/editing/types.d.ts +23 -0
  20. package/lib/plugins/editing/types.d.ts.map +1 -1
  21. package/lib/plugins/export/index.js +1 -589
  22. package/lib/plugins/export/index.js.map +1 -1
  23. package/lib/plugins/filtering/FilteringPlugin.d.ts.map +1 -1
  24. package/lib/plugins/filtering/filter-model.d.ts.map +1 -1
  25. package/lib/plugins/filtering/index.js +1 -1283
  26. package/lib/plugins/filtering/index.js.map +1 -1
  27. package/lib/plugins/filtering/types.d.ts +4 -2
  28. package/lib/plugins/filtering/types.d.ts.map +1 -1
  29. package/lib/plugins/grouping-columns/index.js +1 -726
  30. package/lib/plugins/grouping-columns/index.js.map +1 -1
  31. package/lib/plugins/grouping-rows/index.js +2 -905
  32. package/lib/plugins/grouping-rows/index.js.map +1 -1
  33. package/lib/plugins/master-detail/index.js +1 -950
  34. package/lib/plugins/master-detail/index.js.map +1 -1
  35. package/lib/plugins/multi-sort/index.js +1 -553
  36. package/lib/plugins/multi-sort/index.js.map +1 -1
  37. package/lib/plugins/pinned-columns/index.js +1 -688
  38. package/lib/plugins/pinned-columns/index.js.map +1 -1
  39. package/lib/plugins/pinned-rows/index.js +1 -704
  40. package/lib/plugins/pinned-rows/index.js.map +1 -1
  41. package/lib/plugins/pivot/index.js +1 -1191
  42. package/lib/plugins/pivot/index.js.map +1 -1
  43. package/lib/plugins/print/index.js +1 -691
  44. package/lib/plugins/print/index.js.map +1 -1
  45. package/lib/plugins/reorder/index.js +1 -703
  46. package/lib/plugins/reorder/index.js.map +1 -1
  47. package/lib/plugins/responsive/index.js +1 -971
  48. package/lib/plugins/responsive/index.js.map +1 -1
  49. package/lib/plugins/row-reorder/index.js +1 -728
  50. package/lib/plugins/row-reorder/index.js.map +1 -1
  51. package/lib/plugins/selection/index.js +1 -1071
  52. package/lib/plugins/selection/index.js.map +1 -1
  53. package/lib/plugins/server-side/index.js +1 -521
  54. package/lib/plugins/server-side/index.js.map +1 -1
  55. package/lib/plugins/tree/index.js +1 -686
  56. package/lib/plugins/tree/index.js.map +1 -1
  57. package/lib/plugins/undo-redo/index.js +1 -584
  58. package/lib/plugins/undo-redo/index.js.map +1 -1
  59. package/lib/plugins/visibility/index.js +1 -792
  60. package/lib/plugins/visibility/index.js.map +1 -1
  61. package/package.json +4 -5
  62. package/umd/grid.all.umd.js +1 -186
  63. package/umd/grid.all.umd.js.map +1 -1
  64. package/umd/grid.umd.js +1 -90
  65. package/umd/grid.umd.js.map +1 -1
  66. package/umd/plugins/clipboard.umd.js +1 -6
  67. package/umd/plugins/clipboard.umd.js.map +1 -1
  68. package/umd/plugins/column-virtualization.umd.js +1 -1
  69. package/umd/plugins/column-virtualization.umd.js.map +1 -1
  70. package/umd/plugins/context-menu.umd.js +1 -1
  71. package/umd/plugins/context-menu.umd.js.map +1 -1
  72. package/umd/plugins/editing.umd.js +1 -1
  73. package/umd/plugins/editing.umd.js.map +1 -1
  74. package/umd/plugins/export.umd.js +1 -13
  75. package/umd/plugins/export.umd.js.map +1 -1
  76. package/umd/plugins/filtering.umd.js +1 -1
  77. package/umd/plugins/filtering.umd.js.map +1 -1
  78. package/umd/plugins/grouping-columns.umd.js +1 -1
  79. package/umd/plugins/grouping-columns.umd.js.map +1 -1
  80. package/umd/plugins/grouping-rows.umd.js +1 -4
  81. package/umd/plugins/grouping-rows.umd.js.map +1 -1
  82. package/umd/plugins/master-detail.umd.js +1 -1
  83. package/umd/plugins/master-detail.umd.js.map +1 -1
  84. package/umd/plugins/multi-sort.umd.js +1 -1
  85. package/umd/plugins/multi-sort.umd.js.map +1 -1
  86. package/umd/plugins/pinned-columns.umd.js +1 -1
  87. package/umd/plugins/pinned-columns.umd.js.map +1 -1
  88. package/umd/plugins/pinned-rows.umd.js +1 -1
  89. package/umd/plugins/pinned-rows.umd.js.map +1 -1
  90. package/umd/plugins/pivot.umd.js +1 -1
  91. package/umd/plugins/pivot.umd.js.map +1 -1
  92. package/umd/plugins/print.umd.js +1 -75
  93. package/umd/plugins/print.umd.js.map +1 -1
  94. package/umd/plugins/reorder.umd.js +1 -1
  95. package/umd/plugins/reorder.umd.js.map +1 -1
  96. package/umd/plugins/responsive.umd.js +1 -1
  97. package/umd/plugins/responsive.umd.js.map +1 -1
  98. package/umd/plugins/row-reorder.umd.js +1 -1
  99. package/umd/plugins/row-reorder.umd.js.map +1 -1
  100. package/umd/plugins/selection.umd.js +1 -3
  101. package/umd/plugins/selection.umd.js.map +1 -1
  102. package/umd/plugins/server-side.umd.js +1 -1
  103. package/umd/plugins/server-side.umd.js.map +1 -1
  104. package/umd/plugins/tree.umd.js +1 -1
  105. package/umd/plugins/tree.umd.js.map +1 -1
  106. package/umd/plugins/undo-redo.umd.js +1 -1
  107. package/umd/plugins/undo-redo.umd.js.map +1 -1
  108. package/umd/plugins/visibility.umd.js +1 -1
  109. package/umd/plugins/visibility.umd.js.map +1 -1
@@ -1,692 +1,2 @@
1
- const h = "tbw-print-isolation-style";
2
- function p(r, t) {
3
- const e = document.createElement("style");
4
- return e.id = h, e.textContent = `
5
- /* Print isolation: hide everything except the target grid */
6
- @media print {
7
- /* Hide all body children by default */
8
- body > *:not(#${r}) {
9
- display: none !important;
10
- }
11
-
12
- /* But show the grid and ensure it's not hidden by ancestor rules */
13
- #${r} {
14
- display: block !important;
15
- position: static !important;
16
- visibility: visible !important;
17
- opacity: 1 !important;
18
- overflow: visible !important;
19
- height: auto !important;
20
- width: 100% !important;
21
- max-height: none !important;
22
- margin: 0 !important;
23
- padding: 0 !important;
24
- transform: none !important;
25
- }
26
-
27
- /* If grid is nested, we need to show its ancestors too */
28
- #${r},
29
- #${r} * {
30
- visibility: visible !important;
31
- }
32
-
33
- /* Walk up the DOM and show all ancestors of the grid */
34
- body *:has(> #${r}),
35
- body *:has(#${r}) {
36
- display: block !important;
37
- visibility: visible !important;
38
- opacity: 1 !important;
39
- overflow: visible !important;
40
- height: auto !important;
41
- position: static !important;
42
- transform: none !important;
43
- background: transparent !important;
44
- border: none !important;
45
- padding: 0 !important;
46
- margin: 0 !important;
47
- }
48
-
49
- /* Hide siblings of ancestors (everything that's not in the path to the grid) */
50
- body *:has(#${r}) > *:not(:has(#${r})):not(#${r}) {
51
- display: none !important;
52
- }
53
-
54
- /* Page settings */
55
- @page {
56
- size: ${t};
57
- margin: 1cm;
58
- }
59
-
60
- /* Ensure proper print styling */
61
- body {
62
- margin: 0 !important;
63
- padding: 0 !important;
64
- background: white !important;
65
- color-scheme: light !important;
66
- }
67
- }
68
-
69
- /* Screen: also apply isolation for print preview */
70
- @media screen {
71
- /* When this stylesheet is active, we're about to print */
72
- /* No screen-specific rules needed - isolation only applies to print */
73
- }
74
- `, e;
75
- }
76
- async function u(r, t = {}) {
77
- const { orientation: e = "landscape" } = t, i = r.id;
78
- document.querySelectorAll(`#${CSS.escape(i)}`).length > 1 && console.warn(
79
- `[tbw-grid:print] Multiple elements found with id="${i}". Print isolation may not work correctly. Ensure each grid has a unique ID.`
80
- ), document.getElementById(h)?.remove();
81
- const s = p(i, e);
82
- return document.head.appendChild(s), new Promise((a) => {
83
- const l = () => {
84
- window.removeEventListener("afterprint", l), document.getElementById(h)?.remove(), a();
85
- };
86
- window.addEventListener("afterprint", l), window.print(), setTimeout(() => {
87
- window.removeEventListener("afterprint", l), document.getElementById(h)?.remove(), a();
88
- }, 5e3);
89
- });
90
- }
91
- const m = '<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>', g = {
92
- expand: "▶",
93
- collapse: "▼",
94
- sortAsc: "▲",
95
- sortDesc: "▼",
96
- sortNone: "⇅",
97
- submenuArrow: "▶",
98
- dragHandle: "⋮⋮",
99
- toolPanel: "☰",
100
- filter: m,
101
- filterActive: m,
102
- print: "🖨️"
103
- };
104
- class w {
105
- /**
106
- * Plugin dependencies - declare other plugins this one requires.
107
- *
108
- * Dependencies are validated when the plugin is attached.
109
- * Required dependencies throw an error if missing.
110
- * Optional dependencies log an info message if missing.
111
- *
112
- * @example
113
- * ```typescript
114
- * static readonly dependencies: PluginDependency[] = [
115
- * { name: 'editing', required: true, reason: 'Tracks cell edits for undo/redo' },
116
- * { name: 'selection', required: false, reason: 'Enables selection-based undo' },
117
- * ];
118
- * ```
119
- */
120
- static dependencies;
121
- /**
122
- * Plugin manifest - declares owned properties, config rules, and hook priorities.
123
- *
124
- * This is read by the configuration validator to:
125
- * - Validate that required plugins are loaded when their properties are used
126
- * - Execute configRules to detect invalid/conflicting settings
127
- * - Order hook execution based on priority
128
- *
129
- * @example
130
- * ```typescript
131
- * static override readonly manifest: PluginManifest<MyConfig> = {
132
- * ownedProperties: [
133
- * { property: 'myProp', level: 'column', description: 'the "myProp" column property' },
134
- * ],
135
- * configRules: [
136
- * { id: 'myPlugin/conflict', severity: 'warn', message: '...', check: (c) => c.a && c.b },
137
- * ],
138
- * };
139
- * ```
140
- */
141
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
142
- static manifest;
143
- /**
144
- * Plugin version - defaults to grid version for built-in plugins.
145
- * Third-party plugins can override with their own semver.
146
- */
147
- version = typeof __GRID_VERSION__ < "u" ? __GRID_VERSION__ : "dev";
148
- /** CSS styles to inject into the grid's shadow DOM */
149
- styles;
150
- /** Custom cell renderers keyed by type name */
151
- cellRenderers;
152
- /** Custom header renderers keyed by type name */
153
- headerRenderers;
154
- /** Custom cell editors keyed by type name */
155
- cellEditors;
156
- /** The grid instance this plugin is attached to */
157
- grid;
158
- /** Plugin configuration - merged with defaults in attach() */
159
- config;
160
- /** User-provided configuration from constructor */
161
- userConfig;
162
- /**
163
- * Plugin-level AbortController for event listener cleanup.
164
- * Created fresh in attach(), aborted in detach().
165
- * This ensures event listeners are properly cleaned up when plugins are re-attached.
166
- */
167
- #t;
168
- /**
169
- * Default configuration - subclasses should override this getter.
170
- * Note: This must be a getter (not property initializer) for proper inheritance
171
- * since property initializers run after parent constructor.
172
- */
173
- get defaultConfig() {
174
- return {};
175
- }
176
- constructor(t = {}) {
177
- this.userConfig = t;
178
- }
179
- /**
180
- * Called when the plugin is attached to a grid.
181
- * Override to set up event listeners, initialize state, etc.
182
- *
183
- * @example
184
- * ```ts
185
- * attach(grid: GridElement): void {
186
- * super.attach(grid);
187
- * // Set up document-level listeners with auto-cleanup
188
- * document.addEventListener('keydown', this.handleEscape, {
189
- * signal: this.disconnectSignal
190
- * });
191
- * }
192
- * ```
193
- */
194
- attach(t) {
195
- this.#t?.abort(), this.#t = new AbortController(), this.grid = t, this.config = { ...this.defaultConfig, ...this.userConfig };
196
- }
197
- /**
198
- * Called when the plugin is detached from a grid.
199
- * Override to clean up event listeners, timers, etc.
200
- *
201
- * @example
202
- * ```ts
203
- * detach(): void {
204
- * // Clean up any state not handled by disconnectSignal
205
- * this.selectedRows.clear();
206
- * this.cache = null;
207
- * }
208
- * ```
209
- */
210
- detach() {
211
- this.#t?.abort(), this.#t = void 0;
212
- }
213
- /**
214
- * Get another plugin instance from the same grid.
215
- * Use for inter-plugin communication.
216
- *
217
- * @example
218
- * ```ts
219
- * const selection = this.getPlugin(SelectionPlugin);
220
- * if (selection) {
221
- * const selectedRows = selection.getSelectedRows();
222
- * }
223
- * ```
224
- */
225
- getPlugin(t) {
226
- return this.grid?.getPlugin(t);
227
- }
228
- /**
229
- * Emit a custom event from the grid.
230
- */
231
- emit(t, e) {
232
- this.grid?.dispatchEvent?.(new CustomEvent(t, { detail: e, bubbles: !0 }));
233
- }
234
- /**
235
- * Emit a cancelable custom event from the grid.
236
- * @returns `true` if the event was cancelled (preventDefault called), `false` otherwise
237
- */
238
- emitCancelable(t, e) {
239
- const i = new CustomEvent(t, { detail: e, bubbles: !0, cancelable: !0 });
240
- return this.grid?.dispatchEvent?.(i), i.defaultPrevented;
241
- }
242
- // =========================================================================
243
- // Event Bus - Plugin-to-Plugin Communication
244
- // =========================================================================
245
- /**
246
- * Subscribe to an event from another plugin.
247
- * The subscription is automatically cleaned up when this plugin is detached.
248
- *
249
- * @category Plugin Development
250
- * @param eventType - The event type to listen for (e.g., 'filter-change')
251
- * @param callback - The callback to invoke when the event is emitted
252
- *
253
- * @example
254
- * ```typescript
255
- * // In attach() or other initialization
256
- * this.on('filter-change', (detail) => {
257
- * console.log('Filter changed:', detail);
258
- * });
259
- * ```
260
- */
261
- on(t, e) {
262
- this.grid?._pluginManager?.subscribe(this, t, e);
263
- }
264
- /**
265
- * Unsubscribe from a plugin event.
266
- *
267
- * @category Plugin Development
268
- * @param eventType - The event type to stop listening for
269
- *
270
- * @example
271
- * ```typescript
272
- * this.off('filter-change');
273
- * ```
274
- */
275
- off(t) {
276
- this.grid?._pluginManager?.unsubscribe(this, t);
277
- }
278
- /**
279
- * Emit an event to other plugins via the Event Bus.
280
- * This is for inter-plugin communication only; it does NOT dispatch DOM events.
281
- * Use `emit()` to dispatch DOM events that external consumers can listen to.
282
- *
283
- * @category Plugin Development
284
- * @param eventType - The event type to emit (should be declared in manifest.events)
285
- * @param detail - The event payload
286
- *
287
- * @example
288
- * ```typescript
289
- * // Emit to other plugins (not DOM)
290
- * this.emitPluginEvent('filter-change', { field: 'name', value: 'Alice' });
291
- *
292
- * // For DOM events that consumers can addEventListener to:
293
- * this.emit('filter-change', { field: 'name', value: 'Alice' });
294
- * ```
295
- */
296
- emitPluginEvent(t, e) {
297
- this.grid?._pluginManager?.emitPluginEvent(t, e);
298
- }
299
- /**
300
- * Request a re-render of the grid.
301
- * Uses ROWS phase - does NOT trigger processColumns hooks.
302
- */
303
- requestRender() {
304
- this.grid?.requestRender?.();
305
- }
306
- /**
307
- * Request a columns re-render of the grid.
308
- * Uses COLUMNS phase - triggers processColumns hooks.
309
- * Use this when your plugin needs to reprocess column configuration.
310
- */
311
- requestColumnsRender() {
312
- this.grid?.requestColumnsRender?.();
313
- }
314
- /**
315
- * Request a re-render and restore focus styling afterward.
316
- * Use this when a plugin action (like expand/collapse) triggers a render
317
- * but needs to maintain keyboard navigation focus.
318
- */
319
- requestRenderWithFocus() {
320
- this.grid?.requestRenderWithFocus?.();
321
- }
322
- /**
323
- * Request a lightweight style update without rebuilding DOM.
324
- * Use this instead of requestRender() when only CSS classes need updating.
325
- */
326
- requestAfterRender() {
327
- this.grid?.requestAfterRender?.();
328
- }
329
- /**
330
- * Get the current rows from the grid.
331
- */
332
- get rows() {
333
- return this.grid?.rows ?? [];
334
- }
335
- /**
336
- * Get the original unfiltered/unprocessed rows from the grid.
337
- * Use this when you need all source data regardless of active filters.
338
- */
339
- get sourceRows() {
340
- return this.grid?.sourceRows ?? [];
341
- }
342
- /**
343
- * Get the current columns from the grid.
344
- */
345
- get columns() {
346
- return this.grid?.columns ?? [];
347
- }
348
- /**
349
- * Get only visible columns from the grid (excludes hidden).
350
- * Use this for rendering that needs to match the grid template.
351
- */
352
- get visibleColumns() {
353
- return this.grid?._visibleColumns ?? [];
354
- }
355
- /**
356
- * Get the grid as an HTMLElement for direct DOM operations.
357
- * Use sparingly - prefer the typed GridElementRef API when possible.
358
- *
359
- * @example
360
- * ```ts
361
- * const width = this.gridElement.clientWidth;
362
- * this.gridElement.classList.add('my-plugin-active');
363
- * ```
364
- */
365
- get gridElement() {
366
- return this.grid;
367
- }
368
- /**
369
- * Get the disconnect signal for event listener cleanup.
370
- * This signal is aborted when the grid disconnects from the DOM.
371
- * Use this when adding event listeners that should be cleaned up automatically.
372
- *
373
- * Best for:
374
- * - Document/window-level listeners added in attach()
375
- * - Listeners on the grid element itself
376
- * - Any listener that should persist across renders
377
- *
378
- * Not needed for:
379
- * - Listeners on elements created in afterRender() (removed with element)
380
- *
381
- * @example
382
- * element.addEventListener('click', handler, { signal: this.disconnectSignal });
383
- * document.addEventListener('keydown', handler, { signal: this.disconnectSignal });
384
- */
385
- get disconnectSignal() {
386
- return this.#t?.signal ?? this.grid?.disconnectSignal;
387
- }
388
- /**
389
- * Get the grid-level icons configuration.
390
- * Returns merged icons (user config + defaults).
391
- */
392
- get gridIcons() {
393
- const t = this.grid?.gridConfig?.icons ?? {};
394
- return { ...g, ...t };
395
- }
396
- // #region Animation Helpers
397
- /**
398
- * Check if animations are enabled at the grid level.
399
- * Respects gridConfig.animation.mode and the CSS variable set by the grid.
400
- *
401
- * Plugins should use this to skip animations when:
402
- * - Animation mode is 'off' or `false`
403
- * - User prefers reduced motion and mode is 'reduced-motion' (default)
404
- *
405
- * @example
406
- * ```ts
407
- * private get animationStyle(): 'slide' | 'fade' | false {
408
- * if (!this.isAnimationEnabled) return false;
409
- * return this.config.animation ?? 'slide';
410
- * }
411
- * ```
412
- */
413
- get isAnimationEnabled() {
414
- const t = this.grid?.effectiveConfig?.animation?.mode ?? "reduced-motion";
415
- if (t === !1 || t === "off") return !1;
416
- if (t === !0 || t === "on") return !0;
417
- const e = this.gridElement;
418
- return e ? getComputedStyle(e).getPropertyValue("--tbw-animation-enabled").trim() !== "0" : !0;
419
- }
420
- /**
421
- * Get the animation duration in milliseconds from CSS variable.
422
- * Falls back to 200ms if not set.
423
- *
424
- * Plugins can use this for their animation timing to stay consistent
425
- * with the grid-level animation.duration setting.
426
- *
427
- * @example
428
- * ```ts
429
- * element.animate(keyframes, { duration: this.animationDuration });
430
- * ```
431
- */
432
- get animationDuration() {
433
- const t = this.gridElement;
434
- if (t) {
435
- const e = getComputedStyle(t).getPropertyValue("--tbw-animation-duration").trim(), i = parseInt(e, 10);
436
- if (!isNaN(i)) return i;
437
- }
438
- return 200;
439
- }
440
- // #endregion
441
- /**
442
- * Resolve an icon value to string or HTMLElement.
443
- * Checks plugin config first, then grid-level icons, then defaults.
444
- *
445
- * @param iconKey - The icon key in GridIcons (e.g., 'expand', 'collapse')
446
- * @param pluginOverride - Optional plugin-level override
447
- * @returns The resolved icon value
448
- */
449
- resolveIcon(t, e) {
450
- return e !== void 0 ? e : this.gridIcons[t];
451
- }
452
- /**
453
- * Set an icon value on an element.
454
- * Handles both string (text/HTML) and HTMLElement values.
455
- *
456
- * @param element - The element to set the icon on
457
- * @param icon - The icon value (string or HTMLElement)
458
- */
459
- setIcon(t, e) {
460
- typeof e == "string" ? t.innerHTML = e : e instanceof HTMLElement && (t.innerHTML = "", t.appendChild(e.cloneNode(!0)));
461
- }
462
- /**
463
- * Log a warning message.
464
- */
465
- warn(t) {
466
- console.warn(`[tbw-grid:${this.name}] ${t}`);
467
- }
468
- // #endregion
469
- }
470
- const b = ".tbw-print-header,.tbw-print-footer{display:none}@media print{tbw-grid{overflow:visible!important;height:auto!important;border:none!important;border-radius:0!important;color-scheme:light only;-webkit-print-color-adjust:exact;print-color-adjust:exact}tbw-grid .tbw-grid-content{overflow:visible!important;height:auto!important;max-height:none!important}tbw-grid .tbw-scroll-area{overflow:visible!important;height:auto!important;max-height:none!important}tbw-grid .rows-body{overflow:visible!important;height:auto!important;max-height:none!important}tbw-grid .rows-container,tbw-grid .rows-viewport,tbw-grid .rows{overflow:visible!important;height:auto!important;max-height:none!important;transform:none!important}tbw-grid .rows-viewport .rows{position:static!important}tbw-grid .resize-handle,tbw-grid [part=sort-indicator],tbw-grid .tbw-filter-btn,tbw-grid .tool-panel,tbw-grid .tool-panel-content,tbw-grid .tbw-shell-header,tbw-grid .shell-toolbar,tbw-grid .tool-panel-toggle,tbw-grid [data-print-hide],tbw-grid .expander-cell,tbw-grid .tree-toggle,tbw-grid .context-menu,tbw-grid .faux-vscroll{display:none!important}tbw-grid .tbw-print-header{display:flex;justify-content:space-between;align-items:baseline;padding:var(--tbw-spacing-md, .5em) 0;margin-bottom:var(--tbw-spacing-md, .5em);border-bottom:2px solid var(--tbw-print-border, var(--tbw-color-border-strong));font-family:inherit}.tbw-print-header-title{font-size:1.25em;font-weight:700}.tbw-print-header-timestamp{font-size:var(--tbw-font-size-sm, .875em);color:var(--tbw-print-muted, var(--tbw-color-fg-muted))}tbw-grid .tbw-print-footer{display:block;margin-top:var(--tbw-spacing-md, .5em);padding-top:var(--tbw-spacing-md, .5em);border-top:1px solid var(--tbw-print-border, var(--tbw-color-border));font-size:var(--tbw-font-size-xs, .75em);color:var(--tbw-print-muted, var(--tbw-color-fg-muted));text-align:end}tbw-grid .data-grid-row{break-inside:avoid;page-break-inside:avoid}tbw-grid .cell{border:1px solid var(--tbw-print-cell-border, var(--tbw-color-border))!important}tbw-grid .header-row,tbw-grid .data-grid-row{padding-inline-end:1px}tbw-grid .data-grid-row:hover,tbw-grid .cell:hover{background:inherit!important}@page{margin:1cm}@page{tbw-grid.print-landscape{size:landscape}}@page{tbw-grid.print-portrait{size:portrait}}}", f = {
471
- button: !1,
472
- orientation: "landscape",
473
- warnThreshold: 500,
474
- maxRows: 0,
475
- includeTitle: !0,
476
- includeTimestamp: !0,
477
- title: "",
478
- isolate: !1
479
- };
480
- class v extends w {
481
- /** @internal */
482
- name = "print";
483
- /** @internal */
484
- version = "1.0.0";
485
- /** CSS styles for print mode */
486
- styles = b;
487
- /** Current print state */
488
- #t = !1;
489
- /** Saved column visibility state */
490
- #r = null;
491
- /** Saved virtualization state */
492
- #n = null;
493
- /** Saved rows when maxRows limit is applied */
494
- #o = null;
495
- /** Print header element */
496
- #e = null;
497
- /** Print footer element */
498
- #i = null;
499
- /** Applied scale factor (legacy, used for cleanup) */
500
- #a = null;
501
- /**
502
- * Get the grid typed as PrintGridRef for internal access.
503
- */
504
- get #s() {
505
- return this.grid;
506
- }
507
- /**
508
- * Check if print is currently in progress
509
- */
510
- isPrinting() {
511
- return this.#t;
512
- }
513
- /**
514
- * Trigger the browser print dialog
515
- *
516
- * This method:
517
- * 1. Validates row count against maxRows limit
518
- * 2. Disables virtualization to render all rows
519
- * 3. Applies print-specific CSS classes
520
- * 4. Opens the browser print dialog (or isolated window if `isolate: true`)
521
- * 5. Restores normal state after printing
522
- *
523
- * @param params - Optional parameters to override config for this print
524
- * @param params.isolate - If true, prints in an isolated window containing only the grid
525
- * @returns Promise that resolves when print dialog closes
526
- */
527
- async print(t) {
528
- if (this.#t) {
529
- console.warn("[PrintPlugin] Print already in progress");
530
- return;
531
- }
532
- const e = this.gridElement;
533
- if (!e) {
534
- console.warn("[PrintPlugin] Grid not available");
535
- return;
536
- }
537
- const i = { ...f, ...this.config, ...t }, s = this.rows.length;
538
- let a = s, l = !1;
539
- if (i.warnThreshold > 0 && s > i.warnThreshold) {
540
- const d = i.maxRows > 0 ? `
541
-
542
- Note: Output will be limited to ${i.maxRows.toLocaleString()} rows.` : "";
543
- if (!confirm(
544
- `This grid has ${s.toLocaleString()} rows. Printing large datasets may cause performance issues or browser slowdowns.${d}
545
-
546
- Click OK to continue, or Cancel to abort.`
547
- ))
548
- return;
549
- }
550
- i.maxRows > 0 && s > i.maxRows && (a = i.maxRows, l = !0), this.#t = !0;
551
- const c = performance.now();
552
- this.emit("print-start", {
553
- rowCount: a,
554
- limitApplied: l,
555
- originalRowCount: s
556
- });
557
- try {
558
- const d = this.#s;
559
- this.#n = {
560
- bypassThreshold: d._virtualization?.bypassThreshold ?? 24
561
- }, this.#p(), l && (this.#o = this.sourceRows, this.grid.rows = this.sourceRows.slice(0, a), await new Promise((n) => setTimeout(n, 50))), (i.includeTitle || i.includeTimestamp) && this.#d(i), await this.#h(), await new Promise((n) => requestAnimationFrame(n)), await new Promise((n) => requestAnimationFrame(n)), e.classList.add(`print-${i.orientation}`), await new Promise((n) => requestAnimationFrame(n)), await new Promise((n) => requestAnimationFrame(n)), i.isolate ? await this.#m(i) : await this.#c(), this.emit("print-complete", {
562
- success: !0,
563
- rowCount: a,
564
- duration: Math.round(performance.now() - c)
565
- });
566
- } catch (d) {
567
- console.error("[PrintPlugin] Print failed:", d), this.emit("print-complete", {
568
- success: !1,
569
- rowCount: 0,
570
- duration: Math.round(performance.now() - c)
571
- });
572
- } finally {
573
- this.#g(), this.#t = !1;
574
- }
575
- }
576
- /**
577
- * Add print header with title and timestamp
578
- */
579
- #d(t) {
580
- const e = this.gridElement;
581
- if (e) {
582
- if (this.#e = document.createElement("div"), this.#e.className = "tbw-print-header", t.includeTitle) {
583
- const i = t.title || this.grid.effectiveConfig?.shell?.header?.title || "Grid Data", o = document.createElement("div");
584
- o.className = "tbw-print-header-title", o.textContent = i, this.#e.appendChild(o);
585
- }
586
- if (t.includeTimestamp) {
587
- const i = document.createElement("div");
588
- i.className = "tbw-print-header-timestamp", i.textContent = `Printed: ${(/* @__PURE__ */ new Date()).toLocaleString()}`, this.#e.appendChild(i);
589
- }
590
- e.insertBefore(this.#e, e.firstChild), this.#i = document.createElement("div"), this.#i.className = "tbw-print-footer", this.#i.textContent = `Page generated from ${window.location.hostname}`, e.appendChild(this.#i);
591
- }
592
- }
593
- /**
594
- * Disable virtualization to render all rows
595
- */
596
- async #h() {
597
- const t = this.#s;
598
- if (!t._virtualization) return;
599
- const e = this.rows.length;
600
- t._virtualization.bypassThreshold = e + 100, t.refreshVirtualWindow(!0), await new Promise((i) => setTimeout(i, 100));
601
- }
602
- /**
603
- * Trigger the browser print dialog
604
- */
605
- async #c() {
606
- return new Promise((t) => {
607
- const e = () => {
608
- window.removeEventListener("afterprint", e), t();
609
- };
610
- window.addEventListener("afterprint", e), window.print(), setTimeout(() => {
611
- typeof window < "u" && window.removeEventListener("afterprint", e), t();
612
- }, 1e3);
613
- });
614
- }
615
- /**
616
- * Print in isolation by hiding all other page content.
617
- * This excludes navigation, sidebars, etc. while keeping the grid in place.
618
- */
619
- async #m(t) {
620
- const e = this.gridElement;
621
- e && await u(e, {
622
- orientation: t.orientation
623
- });
624
- }
625
- /**
626
- * Hide columns marked with printHidden: true
627
- */
628
- #p() {
629
- const t = this.columns;
630
- if (t) {
631
- this.#r = /* @__PURE__ */ new Map();
632
- for (const e of t)
633
- e.printHidden && e.field && (this.#r.set(e.field, !e.hidden), this.grid.setColumnVisible(e.field, !1));
634
- }
635
- }
636
- /**
637
- * Restore columns that were hidden for printing
638
- */
639
- #u() {
640
- if (this.#r) {
641
- for (const [t, e] of this.#r)
642
- this.grid.setColumnVisible(t, e);
643
- this.#r = null;
644
- }
645
- }
646
- /**
647
- * Cleanup after printing
648
- */
649
- #g() {
650
- const t = this.gridElement;
651
- if (!t) return;
652
- this.#u(), t.classList.remove("print-portrait", "print-landscape"), this.#a !== null && (t.style.transform = "", t.style.transformOrigin = "", t.style.width = "", this.#a = null), this.#e && (this.#e.remove(), this.#e = null), this.#i && (this.#i.remove(), this.#i = null);
653
- const e = this.#s;
654
- this.#n && e._virtualization && (e._virtualization.bypassThreshold = this.#n.bypassThreshold, e.refreshVirtualWindow(!0), this.#n = null), this.#o !== null && (this.grid.rows = this.#o, this.#o = null);
655
- }
656
- /**
657
- * Register toolbar button if configured
658
- * @internal
659
- */
660
- afterRender() {
661
- this.config?.button && !this.#l && (this.#w(), this.#l = !0);
662
- }
663
- /** Track if toolbar button is registered */
664
- #l = !1;
665
- /**
666
- * Register print button in toolbar
667
- */
668
- #w() {
669
- this.#s.registerToolbarContent?.({
670
- id: "print-button",
671
- order: 900,
672
- // High order to appear at the end
673
- render: (e) => {
674
- const i = document.createElement("button");
675
- i.className = "tbw-toolbar-btn tbw-print-btn", i.title = "Print grid", i.type = "button";
676
- const o = this.resolveIcon("print") || "🖨️";
677
- this.setIcon(i, o), i.addEventListener(
678
- "click",
679
- () => {
680
- this.print();
681
- },
682
- { signal: this.disconnectSignal }
683
- ), e.appendChild(i);
684
- }
685
- });
686
- }
687
- }
688
- export {
689
- v as PrintPlugin,
690
- u as printGridIsolated
691
- };
1
+ const t="tbw-print-isolation-style";async function i(i,e={}){const{orientation:n="landscape"}=e,r=i.id;document.querySelectorAll(`#${CSS.escape(r)}`).length>1&&console.warn(`[tbw-grid:print] Multiple elements found with id="${r}". Print isolation may not work correctly. Ensure each grid has a unique ID.`),document.getElementById(t)?.remove();const o=function(i,e){const n=document.createElement("style");return n.id=t,n.textContent=`\n /* Print isolation: hide everything except the target grid */\n @media print {\n /* Hide all body children by default */\n body > *:not(#${i}) {\n display: none !important;\n }\n\n /* But show the grid and ensure it's not hidden by ancestor rules */\n #${i} {\n display: block !important;\n position: static !important;\n visibility: visible !important;\n opacity: 1 !important;\n overflow: visible !important;\n height: auto !important;\n width: 100% !important;\n max-height: none !important;\n margin: 0 !important;\n padding: 0 !important;\n transform: none !important;\n }\n\n /* If grid is nested, we need to show its ancestors too */\n #${i},\n #${i} * {\n visibility: visible !important;\n }\n\n /* Walk up the DOM and show all ancestors of the grid */\n body *:has(> #${i}),\n body *:has(#${i}) {\n display: block !important;\n visibility: visible !important;\n opacity: 1 !important;\n overflow: visible !important;\n height: auto !important;\n position: static !important;\n transform: none !important;\n background: transparent !important;\n border: none !important;\n padding: 0 !important;\n margin: 0 !important;\n }\n\n /* Hide siblings of ancestors (everything that's not in the path to the grid) */\n body *:has(#${i}) > *:not(:has(#${i})):not(#${i}) {\n display: none !important;\n }\n\n /* Page settings */\n @page {\n size: ${e};\n margin: 1cm;\n }\n\n /* Ensure proper print styling */\n body {\n margin: 0 !important;\n padding: 0 !important;\n background: white !important;\n color-scheme: light !important;\n }\n }\n\n /* Screen: also apply isolation for print preview */\n @media screen {\n /* When this stylesheet is active, we're about to print */\n /* No screen-specific rules needed - isolation only applies to print */\n }\n `,n}(r,n);return document.head.appendChild(o),new Promise(i=>{const e=()=>{window.removeEventListener("afterprint",e),document.getElementById(t)?.remove(),i()};window.addEventListener("afterprint",e),window.print(),setTimeout(()=>{window.removeEventListener("afterprint",e),document.getElementById(t)?.remove(),i()},5e3)})}const e='<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>',n={expand:"▶",collapse:"▼",sortAsc:"▲",sortDesc:"▼",sortNone:"⇅",submenuArrow:"▶",dragHandle:"⋮⋮",toolPanel:"☰",filter:e,filterActive:e,print:"🖨️"};class r{static dependencies;static manifest;version="undefined"!=typeof __GRID_VERSION__?__GRID_VERSION__:"dev";styles;cellRenderers;headerRenderers;cellEditors;grid;config;userConfig;#t;get defaultConfig(){return{}}constructor(t={}){this.userConfig=t}attach(t){this.#t?.abort(),this.#t=new AbortController,this.grid=t,this.config={...this.defaultConfig,...this.userConfig}}detach(){this.#t?.abort(),this.#t=void 0}getPlugin(t){return this.grid?.getPlugin(t)}emit(t,i){this.grid?.dispatchEvent?.(new CustomEvent(t,{detail:i,bubbles:!0}))}emitCancelable(t,i){const e=new CustomEvent(t,{detail:i,bubbles:!0,cancelable:!0});return this.grid?.dispatchEvent?.(e),e.defaultPrevented}on(t,i){this.grid?._pluginManager?.subscribe(this,t,i)}off(t){this.grid?._pluginManager?.unsubscribe(this,t)}emitPluginEvent(t,i){this.grid?._pluginManager?.emitPluginEvent(t,i)}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.#t?.signal??this.grid?.disconnectSignal}get gridIcons(){const t=this.grid?.gridConfig?.icons??{};return{...n,...t}}get isAnimationEnabled(){const t=this.grid?.effectiveConfig?.animation?.mode??"reduced-motion";if(!1===t||"off"===t)return!1;if(!0===t||"on"===t)return!0;const i=this.gridElement;if(i){return"0"!==getComputedStyle(i).getPropertyValue("--tbw-animation-enabled").trim()}return!0}get animationDuration(){const t=this.gridElement;if(t){const i=getComputedStyle(t).getPropertyValue("--tbw-animation-duration").trim(),e=parseInt(i,10);if(!isNaN(e))return e}return 200}resolveIcon(t,i){return void 0!==i?i:this.gridIcons[t]}setIcon(t,i){"string"==typeof i?t.innerHTML=i:i instanceof HTMLElement&&(t.innerHTML="",t.appendChild(i.cloneNode(!0)))}warn(t){console.warn(`[tbw-grid:${this.name}] ${t}`)}}const o={button:!1,orientation:"landscape",warnThreshold:500,maxRows:0,includeTitle:!0,includeTimestamp:!0,title:"",isolate:!1};class s extends r{name="print";version="1.0.0";styles=".tbw-print-header,.tbw-print-footer{display:none}@media print{tbw-grid{overflow:visible!important;height:auto!important;border:none!important;border-radius:0!important;color-scheme:light only;-webkit-print-color-adjust:exact;print-color-adjust:exact}tbw-grid .tbw-grid-content{overflow:visible!important;height:auto!important;max-height:none!important}tbw-grid .tbw-scroll-area{overflow:visible!important;height:auto!important;max-height:none!important}tbw-grid .rows-body{overflow:visible!important;height:auto!important;max-height:none!important}tbw-grid .rows-container,tbw-grid .rows-viewport,tbw-grid .rows{overflow:visible!important;height:auto!important;max-height:none!important;transform:none!important}tbw-grid .rows-viewport .rows{position:static!important}tbw-grid .resize-handle,tbw-grid [part=sort-indicator],tbw-grid .tbw-filter-btn,tbw-grid .tool-panel,tbw-grid .tool-panel-content,tbw-grid .tbw-shell-header,tbw-grid .shell-toolbar,tbw-grid .tool-panel-toggle,tbw-grid [data-print-hide],tbw-grid .expander-cell,tbw-grid .tree-toggle,tbw-grid .context-menu,tbw-grid .faux-vscroll{display:none!important}tbw-grid .tbw-print-header{display:flex;justify-content:space-between;align-items:baseline;padding:var(--tbw-spacing-md, .5em) 0;margin-bottom:var(--tbw-spacing-md, .5em);border-bottom:2px solid var(--tbw-print-border, var(--tbw-color-border-strong));font-family:inherit}.tbw-print-header-title{font-size:1.25em;font-weight:700}.tbw-print-header-timestamp{font-size:var(--tbw-font-size-sm, .875em);color:var(--tbw-print-muted, var(--tbw-color-fg-muted))}tbw-grid .tbw-print-footer{display:block;margin-top:var(--tbw-spacing-md, .5em);padding-top:var(--tbw-spacing-md, .5em);border-top:1px solid var(--tbw-print-border, var(--tbw-color-border));font-size:var(--tbw-font-size-xs, .75em);color:var(--tbw-print-muted, var(--tbw-color-fg-muted));text-align:end}tbw-grid .data-grid-row{break-inside:avoid;page-break-inside:avoid}tbw-grid .cell{border:1px solid var(--tbw-print-cell-border, var(--tbw-color-border))!important}tbw-grid .header-row,tbw-grid .data-grid-row{padding-inline-end:1px}tbw-grid .data-grid-row:hover,tbw-grid .cell:hover{background:inherit!important}@page{margin:1cm}@page{tbw-grid.print-landscape{size:landscape}}@page{tbw-grid.print-portrait{size:portrait}}}";#i=!1;#e=null;#n=null;#r=null;#o=null;#s=null;#a=null;get#d(){return this.grid}isPrinting(){return this.#i}async print(t){if(this.#i)return void console.warn("[PrintPlugin] Print already in progress");const i=this.gridElement;if(!i)return void console.warn("[PrintPlugin] Grid not available");const e={...o,...this.config,...t},n=this.rows.length;let r=n,s=!1;if(e.warnThreshold>0&&n>e.warnThreshold){const t=e.maxRows>0?`\n\nNote: Output will be limited to ${e.maxRows.toLocaleString()} rows.`:"";if(!confirm(`This grid has ${n.toLocaleString()} rows. Printing large datasets may cause performance issues or browser slowdowns.${t}\n\nClick OK to continue, or Cancel to abort.`))return}e.maxRows>0&&n>e.maxRows&&(r=e.maxRows,s=!0),this.#i=!0;const a=performance.now();this.emit("print-start",{rowCount:r,limitApplied:s,originalRowCount:n});try{const t=this.#d;this.#n={bypassThreshold:t._virtualization?.bypassThreshold??24},this.#l(),s&&(this.#r=this.sourceRows,this.grid.rows=this.sourceRows.slice(0,r),await new Promise(t=>setTimeout(t,50))),(e.includeTitle||e.includeTimestamp)&&this.#p(e),await this.#h(),await new Promise(t=>requestAnimationFrame(t)),await new Promise(t=>requestAnimationFrame(t)),i.classList.add(`print-${e.orientation}`),await new Promise(t=>requestAnimationFrame(t)),await new Promise(t=>requestAnimationFrame(t)),e.isolate?await this.#m(e):await this.#c(),this.emit("print-complete",{success:!0,rowCount:r,duration:Math.round(performance.now()-a)})}catch(d){console.error("[PrintPlugin] Print failed:",d),this.emit("print-complete",{success:!1,rowCount:0,duration:Math.round(performance.now()-a)})}finally{this.#g(),this.#i=!1}}#p(t){const i=this.gridElement;if(i){if(this.#o=document.createElement("div"),this.#o.className="tbw-print-header",t.includeTitle){const i=t.title||this.grid.effectiveConfig?.shell?.header?.title||"Grid Data",e=document.createElement("div");e.className="tbw-print-header-title",e.textContent=i,this.#o.appendChild(e)}if(t.includeTimestamp){const t=document.createElement("div");t.className="tbw-print-header-timestamp",t.textContent=`Printed: ${/* @__PURE__ */(new Date).toLocaleString()}`,this.#o.appendChild(t)}i.insertBefore(this.#o,i.firstChild),this.#s=document.createElement("div"),this.#s.className="tbw-print-footer",this.#s.textContent=`Page generated from ${window.location.hostname}`,i.appendChild(this.#s)}}async#h(){const t=this.#d;if(!t._virtualization)return;const i=this.rows.length;t._virtualization.bypassThreshold=i+100,t.refreshVirtualWindow(!0),await new Promise(t=>setTimeout(t,100))}async#c(){return new Promise(t=>{const i=()=>{window.removeEventListener("afterprint",i),t()};window.addEventListener("afterprint",i),window.print(),setTimeout(()=>{"undefined"!=typeof window&&window.removeEventListener("afterprint",i),t()},1e3)})}async#m(t){const e=this.gridElement;e&&await i(e,{orientation:t.orientation})}#l(){const t=this.columns;if(t){this.#e=/* @__PURE__ */new Map;for(const i of t)i.printHidden&&i.field&&(this.#e.set(i.field,!i.hidden),this.grid.setColumnVisible(i.field,!1))}}#u(){if(this.#e){for(const[t,i]of this.#e)this.grid.setColumnVisible(t,i);this.#e=null}}#g(){const t=this.gridElement;if(!t)return;this.#u(),t.classList.remove("print-portrait","print-landscape"),null!==this.#a&&(t.style.transform="",t.style.transformOrigin="",t.style.width="",this.#a=null),this.#o&&(this.#o.remove(),this.#o=null),this.#s&&(this.#s.remove(),this.#s=null);const i=this.#d;this.#n&&i._virtualization&&(i._virtualization.bypassThreshold=this.#n.bypassThreshold,i.refreshVirtualWindow(!0),this.#n=null),null!==this.#r&&(this.grid.rows=this.#r,this.#r=null)}afterRender(){this.config?.button&&!this.#w&&(this.#b(),this.#w=!0)}#w=!1;#b(){const t=this.#d;t.registerToolbarContent?.({id:"print-button",order:900,render:t=>{const i=document.createElement("button");i.className="tbw-toolbar-btn tbw-print-btn",i.title="Print grid",i.type="button";const e=this.resolveIcon("print")||"🖨️";this.setIcon(i,e),i.addEventListener("click",()=>{this.print()},{signal:this.disconnectSignal}),t.appendChild(i)}})}}export{s as PrintPlugin,i as printGridIsolated};
692
2
  //# sourceMappingURL=index.js.map