@toolbox-web/grid 2.12.0 → 2.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/all.js +2 -2
  2. package/all.js.map +1 -1
  3. package/custom-elements.json +20 -1
  4. package/index.js +1 -1
  5. package/index.js.map +1 -1
  6. package/lib/core/grid.d.ts +23 -1
  7. package/lib/core/internal/empty.d.ts +9 -2
  8. package/lib/core/internal/focus-manager.d.ts +17 -0
  9. package/lib/core/internal/shell.d.ts +1 -1
  10. package/lib/core/internal/virtualization.d.ts +103 -1
  11. package/lib/core/types.d.ts +18 -7
  12. package/lib/features/pinned-columns.js.map +1 -1
  13. package/lib/plugins/clipboard/index.js.map +1 -1
  14. package/lib/plugins/column-virtualization/index.js.map +1 -1
  15. package/lib/plugins/context-menu/index.js.map +1 -1
  16. package/lib/plugins/editing/EditingPlugin.d.ts +1 -1
  17. package/lib/plugins/editing/index.js +1 -1
  18. package/lib/plugins/editing/index.js.map +1 -1
  19. package/lib/plugins/editing/types.d.ts +9 -1
  20. package/lib/plugins/export/index.js.map +1 -1
  21. package/lib/plugins/filtering/index.js +1 -1
  22. package/lib/plugins/filtering/index.js.map +1 -1
  23. package/lib/plugins/grouping-columns/index.js.map +1 -1
  24. package/lib/plugins/grouping-rows/index.js.map +1 -1
  25. package/lib/plugins/master-detail/index.js.map +1 -1
  26. package/lib/plugins/multi-sort/index.js.map +1 -1
  27. package/lib/plugins/pinned-columns/index.js +1 -1
  28. package/lib/plugins/pinned-columns/index.js.map +1 -1
  29. package/lib/plugins/pinned-rows/index.js.map +1 -1
  30. package/lib/plugins/pivot/index.js.map +1 -1
  31. package/lib/plugins/print/index.js.map +1 -1
  32. package/lib/plugins/reorder-columns/index.js +1 -1
  33. package/lib/plugins/reorder-columns/index.js.map +1 -1
  34. package/lib/plugins/reorder-rows/index.js +1 -1
  35. package/lib/plugins/reorder-rows/index.js.map +1 -1
  36. package/lib/plugins/responsive/index.js +1 -1
  37. package/lib/plugins/responsive/index.js.map +1 -1
  38. package/lib/plugins/row-drag-drop/RowDragDropPlugin.d.ts +1 -1
  39. package/lib/plugins/row-drag-drop/index.js +1 -1
  40. package/lib/plugins/row-drag-drop/index.js.map +1 -1
  41. package/lib/plugins/selection/SelectionPlugin.d.ts +1 -1
  42. package/lib/plugins/selection/index.d.ts +1 -1
  43. package/lib/plugins/selection/index.js.map +1 -1
  44. package/lib/plugins/server-side/ServerSidePlugin.d.ts +1 -1
  45. package/lib/plugins/server-side/index.js.map +1 -1
  46. package/lib/plugins/sticky-rows/index.js.map +1 -1
  47. package/lib/plugins/tooltip/index.js.map +1 -1
  48. package/lib/plugins/tree/index.js.map +1 -1
  49. package/lib/plugins/undo-redo/index.js.map +1 -1
  50. package/lib/plugins/visibility/index.js.map +1 -1
  51. package/package.json +1 -1
  52. package/public.d.ts +9 -0
  53. package/umd/grid.all.umd.js +1 -1
  54. package/umd/grid.all.umd.js.map +1 -1
  55. package/umd/grid.umd.js +1 -1
  56. package/umd/grid.umd.js.map +1 -1
  57. package/umd/plugins/editing.umd.js.map +1 -1
  58. package/umd/plugins/pinned-columns.umd.js +1 -1
  59. package/umd/plugins/pinned-columns.umd.js.map +1 -1
  60. package/umd/plugins/reorder-rows.umd.js.map +1 -1
  61. package/umd/plugins/row-drag-drop.umd.js.map +1 -1
  62. package/umd/plugins/selection.umd.js.map +1 -1
  63. package/umd/plugins/server-side.umd.js.map +1 -1
@@ -333,7 +333,7 @@ export declare class DataGridElement<T = any> extends HTMLElement implements Int
333
333
  * const editing = grid.getPluginByName('editing');
334
334
  * ```
335
335
  *
336
- * @param name - The plugin name (matches {@link BaseGridPlugin.name}).
336
+ * @param name - The plugin name (matches `BaseGridPlugin.name`).
337
337
  * @returns The plugin instance, or `undefined` if not registered.
338
338
  * @group Plugin Communication
339
339
  */
@@ -1728,6 +1728,28 @@ export declare class DataGridElement<T = any> extends HTMLElement implements Int
1728
1728
  * ```
1729
1729
  */
1730
1730
  containsFocus(node?: Node | null): boolean;
1731
+ /**
1732
+ * The last meaningful element inside the grid host's light-DOM subtree
1733
+ * that received user focus. Excludes the grid host itself (synthetic
1734
+ * `tabindex=0` focus during keyboard navigation), bare `.cell` elements
1735
+ * (whose focus is virtual), and descendants of registered external focus
1736
+ * containers (overlays/datepickers/dropdowns). Returns `null` when the
1737
+ * user has not yet interacted with any tracked descendant.
1738
+ *
1739
+ * @group Focus Management
1740
+ */
1741
+ get lastFocusedElement(): HTMLElement | null;
1742
+ /**
1743
+ * Restore focus to the last meaningful user-focused element inside the
1744
+ * grid (see {@link lastFocusedElement} for what counts as "meaningful").
1745
+ * The grid invokes this automatically whenever focus is bounced to
1746
+ * `<body>` (e.g. when a body-level overlay is removed). Exposed publicly
1747
+ * so application code can re-anchor focus on demand.
1748
+ *
1749
+ * @group Focus Management
1750
+ * @returns `true` if a tracked element existed and was refocused; `false` otherwise.
1751
+ */
1752
+ restoreLastFocus(): boolean;
1731
1753
  /**
1732
1754
  * Re-parse light DOM column elements and refresh the grid.
1733
1755
  * Call this after framework adapters have registered their templates.
@@ -47,12 +47,19 @@ export declare function shouldShowEmpty(loading: boolean, renderedRowCount: numb
47
47
  * VIRTUALIZATION during scroll) can skip the recreate when nothing observable
48
48
  * has changed. Without this guard we'd churn DOM and could leak
49
49
  * framework-adapter portals returned from user renderers.
50
+ *
51
+ * `sourceRows` is the array reference (not just its length): reassigning
52
+ * `grid.rows` produces a new reference and busts the cache, even when the
53
+ * length is unchanged (e.g. `grid.rows = []` after a previous `grid.rows = []`).
54
+ * That is the user's explicit "something changed" signal, and it lets custom
55
+ * renderers that close over external state (e.g. an `errorMessage` variable)
56
+ * re-render without forcing them to mint a new renderer function.
50
57
  */
51
58
  export interface EmptyOverlayState {
52
59
  el?: HTMLElement;
53
60
  renderer?: GridConfig['emptyRenderer'];
54
61
  target?: EmptyOverlay;
55
- sourceRowCount?: number;
62
+ sourceRows?: readonly unknown[];
56
63
  filteredOut?: boolean;
57
64
  }
58
65
  /**
@@ -63,4 +70,4 @@ export interface EmptyOverlayState {
63
70
  * The `state` object is mutated in place — callers store a single reference
64
71
  * and pass it back on every tick.
65
72
  */
66
- export declare function updateEmptyOverlay(gridRoot: Element | null, loading: boolean, renderedRowCount: number, sourceRowCount: number, renderer: GridConfig['emptyRenderer'] | undefined, target: EmptyOverlay, state: EmptyOverlayState): void;
73
+ export declare function updateEmptyOverlay(gridRoot: Element | null, loading: boolean, renderedRowCount: number, sourceRows: readonly unknown[], renderer: GridConfig['emptyRenderer'] | undefined, target: EmptyOverlay, state: EmptyOverlayState): void;
@@ -41,6 +41,23 @@ export declare class FocusManager<T = any> {
41
41
  * Check whether a node is inside any registered external focus container.
42
42
  */
43
43
  isInExternalFocusContainer(node: Node): boolean;
44
+ /**
45
+ * Restore focus to the last tracked user-focused element if it is still
46
+ * connected and focusable. Returns `true` on success.
47
+ *
48
+ * Stale references (element removed from DOM during a re-render) are
49
+ * silently dropped so callers can fall back to their own restoration logic.
50
+ */
51
+ restoreLastFocus(): boolean;
52
+ /**
53
+ * Currently tracked last-user-focused element inside the grid host's
54
+ * light-DOM subtree, if any. Excludes the grid host itself, bare `.cell`
55
+ * elements, and descendants of registered external focus containers.
56
+ * Exposed so plugins (notably EditingPlugin) can defer to the unified
57
+ * trap before falling back to plugin-specific restoration logic.
58
+ * @internal
59
+ */
60
+ get lastFocusedElement(): HTMLElement | null;
44
61
  /**
45
62
  * Clean up all external focus container listeners.
46
63
  * Called when the grid disconnects.
@@ -204,7 +204,7 @@ export declare function renderHeaderContent(renderRoot: Element, state: ShellSta
204
204
  * Render content for expanded accordion sections.
205
205
  * @param icons - Optional icons for expand/collapse chevrons (from grid config)
206
206
  */
207
- export declare function renderPanelContent(renderRoot: Element, state: ShellState, icons?: {
207
+ export declare function renderPanelContent(renderRoot: Element, state: ShellState, _icons?: {
208
208
  expand?: IconValue;
209
209
  collapse?: IconValue;
210
210
  }): void;
@@ -173,6 +173,84 @@ export declare function computeAverageExcludingPluginRows<T>(cache: RowPosition[
173
173
  measuredCount: number;
174
174
  averageHeight: number;
175
175
  };
176
+ /**
177
+ * Maximum spacer element height (px) before browsers cap the rendered height.
178
+ *
179
+ * Chromium's hard cap is `2^25 = 33,554,432` px; Firefox/Safari are similar or larger.
180
+ * We pick `33,500,000` — ~54 KB of headroom under Chromium's cap so that floating-point
181
+ * drift in the fractional mapping math (`scrollTop * rMax / sMax`) cannot push a computed
182
+ * spacer position past the engine cap and silently truncate.
183
+ *
184
+ * For datasets where `totalRows * rowHeight` exceeds this value, the faux-vscroll
185
+ * spacer would silently truncate, making the tail of the dataset unreachable via
186
+ * the native scrollbar / `Ctrl+End`. The grid switches to fractional scroll mapping
187
+ * above this threshold — see {@link computeScrollMapping}.
188
+ *
189
+ * @category Plugin Development
190
+ * @since 2.13.0
191
+ */
192
+ export declare const MAX_ELEMENT_HEIGHT_PX = 33500000;
193
+ /**
194
+ * Mapping between native `scrollTop` (clamped spacer space) and "virtual" scroll
195
+ * position (raw row-content space). See {@link computeScrollMapping}.
196
+ *
197
+ * @category Plugin Development
198
+ * @since 2.13.0
199
+ */
200
+ export interface ScrollMapping {
201
+ /** Raw row-content height in pixels (totalRows * rowHeight or sum of variable heights). */
202
+ rawContentHeight: number;
203
+ /** Effective spacer DOM height — `min(rawContentHeight, maxSpacerHeight)`. */
204
+ spacerHeight: number;
205
+ /** Viewport height used for computing scrollable extents. */
206
+ viewportHeight: number;
207
+ /** True when `rawContentHeight > maxSpacerHeight` — fractional mapping is active. */
208
+ capped: boolean;
209
+ }
210
+ /**
211
+ * Compute a mapping between the spacer's native scrollTop and the virtual
212
+ * row-content scroll offset.
213
+ *
214
+ * For datasets within `maxSpacerHeight`, mapping is identity (no transform).
215
+ * Above the cap, the spacer is clamped and `scrollTop` units no longer equal
216
+ * row-content units — call {@link toVirtualScrollTop} / {@link fromVirtualScrollTop}
217
+ * to translate between the two coordinate spaces.
218
+ *
219
+ * @category Plugin Development
220
+ * @since 2.13.0
221
+ */
222
+ export declare function computeScrollMapping(rawContentHeight: number, viewportHeight: number, maxSpacerHeight?: number): ScrollMapping;
223
+ /**
224
+ * Translate a native `scrollTop` (spacer space) into a virtual scroll offset
225
+ * in raw row-content space. Identity when the mapping is not capped.
226
+ *
227
+ * The result is clamped to `[0, rawContentHeight - viewportHeight]` so callers
228
+ * never index past the dataset, even when the spacer's actual scrollable extent
229
+ * slightly exceeds `spacerHeight - viewportHeight` (e.g. when a horizontal scrollbar
230
+ * adds bottom padding to the faux spacer).
231
+ *
232
+ * @category Plugin Development
233
+ * @since 2.13.0
234
+ */
235
+ export declare function toVirtualScrollTop(scrollTop: number, mapping: ScrollMapping): number;
236
+ /**
237
+ * Translate a virtual scroll offset (raw row-content space) into a native
238
+ * `scrollTop` value (spacer space). Identity when the mapping is not capped.
239
+ *
240
+ * @category Plugin Development
241
+ * @since 2.13.0
242
+ */
243
+ export declare function fromVirtualScrollTop(virtualTop: number, mapping: ScrollMapping): number;
244
+ /**
245
+ * Identity scroll mapping (no cap applied). Useful as a default in places that
246
+ * don't yet know the dataset extents. `viewportHeight` is `0` until the first
247
+ * `calculateTotalSpacerHeight` call populates the real extents — safe because
248
+ * `capped: false` short-circuits both translation helpers before they read it.
249
+ *
250
+ * @category Plugin Development
251
+ * @since 2.13.0
252
+ */
253
+ export declare function createIdentityScrollMapping(): ScrollMapping;
176
254
  /**
177
255
  * Result of computing a virtual window for fixed-height rows.
178
256
  * Used by plugins like FilteringPlugin for virtualized dropdowns.
@@ -184,8 +262,19 @@ export interface VirtualWindow {
184
262
  end: number;
185
263
  /** Pixel offset to apply to the rows container (translateY) */
186
264
  offsetY: number;
187
- /** Total height of the scrollable content */
265
+ /**
266
+ * Effective spacer DOM height — use this for the scrollable spacer element.
267
+ * Equal to `min(totalRows * rowHeight, maxSpacerHeight)`. Above the cap this
268
+ * is clamped; use {@link VirtualWindow.rawContentHeight} for the un-clamped value.
269
+ */
188
270
  totalHeight: number;
271
+ /**
272
+ * Raw, un-clamped row-content height (`totalRows * rowHeight`). Equal to
273
+ * `totalHeight` when the dataset is below `maxSpacerHeight`.
274
+ *
275
+ * @since 2.13.0
276
+ */
277
+ rawContentHeight: number;
189
278
  }
190
279
  /** Parameters for computing the virtual window */
191
280
  export interface VirtualWindowParams {
@@ -199,11 +288,24 @@ export interface VirtualWindowParams {
199
288
  rowHeight: number;
200
289
  /** Number of extra rows to render above/below viewport */
201
290
  overscan: number;
291
+ /**
292
+ * Optional cap on the spacer's DOM height. When `totalRows * rowHeight` exceeds
293
+ * this value, `scrollTop` is treated as a spacer-space position and translated
294
+ * to virtual row-content space via fractional mapping. Defaults to {@link MAX_ELEMENT_HEIGHT_PX}.
295
+ * Pass `Infinity` to disable.
296
+ *
297
+ * @since 2.13.0
298
+ */
299
+ maxSpacerHeight?: number;
202
300
  }
203
301
  /**
204
302
  * Compute the virtual row window based on scroll position and viewport.
205
303
  * Simple fixed-height implementation for plugins needing basic virtualization.
206
304
  *
305
+ * For datasets where `totalRows * rowHeight > maxSpacerHeight`, scroll position
306
+ * is mapped fractionally so the entire dataset stays reachable past the browser's
307
+ * single-element height cap (Chromium ~33.5M px). See {@link computeScrollMapping}.
308
+ *
207
309
  * @param params - Parameters for computing the window
208
310
  * @returns VirtualWindow with start/end indices and transforms
209
311
  */
@@ -1,6 +1,6 @@
1
1
  import { RenderPhase } from './internal/render-scheduler';
2
2
  import { ShellState } from './internal/shell';
3
- import { RowPosition } from './internal/virtualization';
3
+ import { RowPosition, ScrollMapping } from './internal/virtualization';
4
4
  import { AfterCellRenderContext, AfterRowRenderContext, CellMouseEvent } from './plugin/types';
5
5
  /**
6
6
  * Position entry for a single row in the position cache.
@@ -1887,6 +1887,17 @@ export interface VirtualState {
1887
1887
  cachedScrollAreaHeight: number;
1888
1888
  /** Cached reference to .tbw-scroll-area element. Set during scroll listener setup. */
1889
1889
  scrollAreaEl: HTMLElement | null;
1890
+ /**
1891
+ * Active scroll mapping between native `scrollTop` (clamped spacer space) and
1892
+ * "virtual" row-content space. Identity (`capped: false`) for datasets within
1893
+ * the browser's max-element-height cap (Chromium ~33.5M px). For larger datasets,
1894
+ * the spacer height is clamped and `scrollTop` must be translated via this
1895
+ * mapping before computing the visible window. Updated by `calculateTotalSpacerHeight`.
1896
+ *
1897
+ * @see {@link computeScrollMapping}
1898
+ * @since 2.13.0
1899
+ */
1900
+ scrollMapping: ScrollMapping;
1890
1901
  }
1891
1902
  /**
1892
1903
  * Union type for input-like elements that have a `value` property.
@@ -2438,7 +2449,7 @@ export interface GridConfig<TRow = any> {
2438
2449
  * instead. It is honored by every sort code path in the grid (core, multi-sort,
2439
2450
  * tree, server-side) and is composable across columns.
2440
2451
  *
2441
- * For server-side sort, prefer {@link ServerSideConfig.dataSource} — the
2452
+ * For server-side sort, prefer `ServerSideConfig.dataSource` — the
2442
2453
  * `sortModel` is shipped to your `getRows` handler so the backend can return
2443
2454
  * pre-sorted blocks.
2444
2455
  * :::
@@ -2461,7 +2472,7 @@ export interface GridConfig<TRow = any> {
2461
2472
  * ```
2462
2473
  *
2463
2474
  * @see {@link BaseColumnConfig.sortComparator} — recommended per-column override
2464
- * @see {@link ServerSideConfig.dataSource} — recommended server-side sort path
2475
+ * @see `ServerSideConfig.dataSource` — recommended server-side sort path
2465
2476
  */
2466
2477
  sortHandler?: SortHandler<TRow>;
2467
2478
  /**
@@ -2477,8 +2488,8 @@ export interface GridConfig<TRow = any> {
2477
2488
  * };
2478
2489
  * ```
2479
2490
  *
2480
- * @see {@link DataGrid.sort} for runtime sorting
2481
- * @see {@link DataGrid.sortModel} for reading current sort state
2491
+ * @see {@link DataGridElement.sort} for runtime sorting
2492
+ * @see {@link DataGridElement.sortModel} for reading current sort state
2482
2493
  */
2483
2494
  initialSort?: {
2484
2495
  field: string;
@@ -2776,7 +2787,7 @@ export interface SortState {
2776
2787
  * active sort field. For per-column custom sort logic that survives every
2777
2788
  * sort code path (core, multi-sort, tree, server-side), set `sortComparator`
2778
2789
  * on the relevant columns instead. For server-side sort, use
2779
- * {@link ServerSideConfig.dataSource}.
2790
+ * `ServerSideConfig.dataSource`.
2780
2791
  * :::
2781
2792
  *
2782
2793
  * Use `SortHandler` only when you need to replace the grid's sort engine
@@ -3405,7 +3416,7 @@ export interface ShellHeaderConfig {
3405
3416
  *
3406
3417
  * Set to `false` when you want to provide your own toggle button (e.g. a
3407
3418
  * design-system button styled to match your application). Wire your button
3408
- * to call {@link Grid.toggleToolPanel} (or `toggleToolPanelSection(id)` for
3419
+ * to call {@link DataGridElement.toggleToolPanel} (or `toggleToolPanelSection(id)` for
3409
3420
  * a specific section). All tool panels remain functional; only the
3410
3421
  * built-in toggle button and adjacent separator are suppressed.
3411
3422
  *
@@ -1 +1 @@
1
- {"version":3,"file":"pinned-columns.js","sources":["../../../../../libs/grid/src/lib/features/pinned-columns.ts"],"sourcesContent":["/**\n * Pinned Columns feature for @toolbox-web/grid\n *\n * @example\n * ```typescript\n * import '@toolbox-web/grid/features/pinned-columns';\n *\n * grid.gridConfig = { features: { pinnedColumns: true } };\n * ```\n */\n\nimport { PinnedColumnsPlugin } from '../plugins/pinned-columns';\nimport { registerFeature } from './registry';\n\ndeclare module '../core/types' {\n interface FeatureConfig {\n /** Enable column pinning (left/right). */\n pinnedColumns?: boolean;\n }\n}\n\nregisterFeature('pinnedColumns', (config) => {\n return new PinnedColumnsPlugin();\n});\n\n/** @internal Type anchor — forces bundlers to preserve this module's FeatureConfig augmentation when re-exported. */\nexport type _Augmentation = true;\n"],"names":["registerFeature","config","PinnedColumnsPlugin"],"mappings":"qJAqBAA,EAAgB,gBAAkBC,GACzB,IAAIC"}
1
+ {"version":3,"file":"pinned-columns.js","sources":["../../../../../libs/grid/src/lib/features/pinned-columns.ts"],"sourcesContent":["/**\n * Pinned Columns feature for @toolbox-web/grid\n *\n * @example\n * ```typescript\n * import '@toolbox-web/grid/features/pinned-columns';\n *\n * grid.gridConfig = { features: { pinnedColumns: true } };\n * ```\n */\n\nimport { PinnedColumnsPlugin } from '../plugins/pinned-columns';\nimport { registerFeature } from './registry';\n\ndeclare module '../core/types' {\n interface FeatureConfig {\n /** Enable column pinning (left/right). */\n pinnedColumns?: boolean;\n }\n}\n\nregisterFeature('pinnedColumns', (_config) => {\n return new PinnedColumnsPlugin();\n});\n\n/** @internal Type anchor — forces bundlers to preserve this module's FeatureConfig augmentation when re-exported. */\nexport type _Augmentation = true;\n"],"names":["registerFeature","_config","PinnedColumnsPlugin"],"mappings":"qJAqBAA,EAAgB,gBAAkBC,GACzB,IAAIC"}