@ornery/ui-grid-vanilla 0.1.10 → 1.0.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 (62) hide show
  1. package/README.md +196 -0
  2. package/demo/main.ts +0 -1
  3. package/dist/index.d.mts +575 -55
  4. package/dist/index.d.ts +575 -55
  5. package/dist/index.js +4894 -1503
  6. package/dist/index.mjs +4994 -1556
  7. package/package.json +11 -8
  8. package/scss-plugin.ts +65 -0
  9. package/src/adopt-styles.ts +39 -0
  10. package/src/attribute-bridge.ts +291 -0
  11. package/src/auto-height.test.ts +142 -0
  12. package/src/auto-resize.test.ts +206 -0
  13. package/src/cell-nav.test.ts +262 -0
  14. package/src/components/grid-body-cell/grid-body-cell.ts +23 -0
  15. package/src/components/grid-body-cell/index.ts +1 -0
  16. package/src/components/grid-cell-editor/grid-cell-editor.ts +104 -0
  17. package/src/components/grid-cell-editor/index.ts +1 -0
  18. package/src/components/grid-filter-cell/grid-filter-cell.scss +14 -0
  19. package/src/components/grid-filter-cell/grid-filter-cell.template.ts +20 -0
  20. package/src/components/grid-filter-cell/grid-filter-cell.ts +121 -0
  21. package/src/components/grid-filter-cell/index.ts +1 -0
  22. package/src/components/grid-group-row/grid-group-row.scss +24 -0
  23. package/src/components/grid-group-row/grid-group-row.template.ts +23 -0
  24. package/src/components/grid-group-row/grid-group-row.ts +94 -0
  25. package/src/components/grid-group-row/index.ts +1 -0
  26. package/src/components/grid-header-cell/grid-header-cell.ts +17 -0
  27. package/src/components/grid-header-cell/index.ts +1 -0
  28. package/src/components/grid-pagination/grid-pagination.scss +39 -0
  29. package/src/components/grid-pagination/grid-pagination.template.ts +40 -0
  30. package/src/components/grid-pagination/grid-pagination.ts +204 -0
  31. package/src/components/grid-pagination/index.ts +1 -0
  32. package/src/components/grid-template/grid-template.ts +166 -0
  33. package/src/components/grid-template/index.ts +1 -0
  34. package/src/events.ts +815 -0
  35. package/src/exporter.test.ts +186 -0
  36. package/src/focus.ts +353 -0
  37. package/src/framework-slots.ts +440 -0
  38. package/src/grid-controller.ts +1402 -18
  39. package/src/icons.ts +88 -0
  40. package/src/importer.test.ts +147 -0
  41. package/src/index.integration.test.ts +2 -2
  42. package/src/index.ts +9 -0
  43. package/src/infinite-scroll.test.ts +220 -0
  44. package/src/keyboard-navigation.test.ts +579 -0
  45. package/src/patch.ts +722 -0
  46. package/src/render.ts +465 -0
  47. package/src/row-edit.test.ts +191 -0
  48. package/src/row-selection.test.ts +550 -0
  49. package/src/save-state.test.ts +235 -0
  50. package/src/scss.d.ts +4 -0
  51. package/src/templates/index.ts +62 -0
  52. package/src/test-setup.ts +23 -0
  53. package/src/ui-grid-empty.template.ts +23 -0
  54. package/src/ui-grid-shell.scss +25 -0
  55. package/src/ui-grid-shell.template.ts +45 -0
  56. package/src/ui-grid-standalone.element.ts +741 -1112
  57. package/src/utils/cell-class.ts +70 -0
  58. package/src/utils/display-items.ts +15 -0
  59. package/src/utils/dom.ts +64 -0
  60. package/src/validate.test.ts +170 -0
  61. package/tsup.config.ts +10 -0
  62. package/vite.config.ts +10 -1
package/dist/index.d.mts CHANGED
@@ -1,14 +1,217 @@
1
- import { GridOptions, GridLabels, GridColumnDef, PipelineResult, SortState, PinnedColumnState, GridCellPosition, UiGridApi, GridRecord, DisplayItem, SortDirection, PinDirection, GridRow, GroupItem, BuildGridPipelineContext } from '@ornery/ui-grid-core';
1
+ import { GridCellTemplateContext, GridLabels, GridExpandableTemplateContext, GridColumnDef, GridHeaderTemplateContext, GridRow, GroupItem, GridOptions, PipelineResult, SortState, PinnedColumnState, GridCellPosition, UiGridApi, GridRecord, DisplayItem, SortDirection, PinDirection, GridSelectionState, resolveGridSelectionOptions, GridInfiniteScrollState, GridValidatorRegistry, BuildGridPipelineContext } from '@ornery/ui-grid-core';
2
2
  export { GridOptions, UiGridApi } from '@ornery/ui-grid-core';
3
3
 
4
+ /**
5
+ * Framework-rendered slot bridge for the vanilla grid element.
6
+ *
7
+ * The vanilla grid renders every cell / header / filter / group / expandable
8
+ * / empty-state via string-interpolated templates by default. When a
9
+ * framework wrapper (Angular, React, or any other consumer) wants to own
10
+ * rendering of a slot with its own templating engine, it calls
11
+ * `setFrameworkRenderedSlots()` on the element to flip slot kinds into
12
+ * "slot placeholder" mode.
13
+ *
14
+ * In that mode the element emits `<slot name="…">` placeholders at the
15
+ * usual render sites. The wrapper listens for the `*SlotsChanged` event
16
+ * this module dispatches and inserts light-DOM nodes with matching `slot`
17
+ * attributes carrying its framework-rendered content. Slot projection then
18
+ * composes the wrapper's view back into the shadow tree without the
19
+ * wrapper ever touching the shadow DOM directly.
20
+ *
21
+ * Slot naming conventions:
22
+ * - `cell-<columnName>-<rowId>` per-cell (one slot per rendered body cell)
23
+ * - `header-<columnName>` per-column, singleton
24
+ * - `filter-<columnName>` per-column, singleton
25
+ * - `group-row-<groupId>` per-group
26
+ * - `expandable-row-<rowId>` per-row
27
+ * - `empty` singleton
28
+ */
29
+
30
+ /** Which slot kinds the element should render as `<slot>` placeholders. */
31
+ interface FrameworkRenderedSlotsConfig {
32
+ /** Column names whose body cells are framework-rendered. Everything else
33
+ * continues to use the vanilla interpolation path. */
34
+ cells?: readonly string[];
35
+ /** Column names whose header cells are framework-rendered. */
36
+ headers?: readonly string[];
37
+ /** Column names whose filter cells are framework-rendered. */
38
+ filters?: readonly string[];
39
+ /** When true, group rows are emitted as slot placeholders. */
40
+ groupRow?: boolean;
41
+ /** When true, expandable detail rows are emitted as slot placeholders. */
42
+ expandableRow?: boolean;
43
+ /** When true, the empty-state panel is emitted as a slot placeholder. */
44
+ emptyState?: boolean;
45
+ }
46
+ interface FrameworkCellSlot {
47
+ slotName: string;
48
+ columnName: string;
49
+ rowId: string;
50
+ rowIndex: number;
51
+ context: GridCellTemplateContext;
52
+ }
53
+ interface FrameworkHeaderSlot {
54
+ slotName: string;
55
+ columnName: string;
56
+ context: GridHeaderTemplateContext;
57
+ }
58
+ interface FrameworkFilterSlot {
59
+ slotName: string;
60
+ columnName: string;
61
+ value: string;
62
+ placeholder: string;
63
+ disabled: boolean;
64
+ column: GridColumnDef;
65
+ }
66
+ interface FrameworkGroupRowSlot {
67
+ slotName: string;
68
+ groupId: string;
69
+ field: string;
70
+ label: string;
71
+ count: number;
72
+ depth: number;
73
+ collapsed: boolean;
74
+ }
75
+ interface FrameworkExpandableRowSlot {
76
+ slotName: string;
77
+ rowId: string;
78
+ rowIndex: number;
79
+ context: GridExpandableTemplateContext;
80
+ }
81
+ interface FrameworkEmptyStateSlot {
82
+ slotName: 'empty';
83
+ heading: string;
84
+ description: string;
85
+ labels: GridLabels;
86
+ }
87
+ interface FrameworkSlotDelta<TSlot> {
88
+ added: TSlot[];
89
+ removed: TSlot[];
90
+ }
91
+ /**
92
+ * Bridge state + behavior. The element creates one instance, reads the
93
+ * `has*` methods during render to decide which path to take, calls the
94
+ * `stage*` methods as it emits slot placeholders, and calls `flush()` at
95
+ * the end of each render pass to emit the add/remove delta.
96
+ */
97
+ declare class FrameworkSlotBridge {
98
+ private readonly host;
99
+ private flaggedCells;
100
+ private flaggedHeaders;
101
+ private flaggedFilters;
102
+ private flaggedGroupRow;
103
+ private flaggedExpandableRow;
104
+ private flaggedEmptyState;
105
+ private lastCellSlots;
106
+ private lastExpandableRowSlots;
107
+ private lastGroupRowSlots;
108
+ private lastHeaderSlots;
109
+ private lastFilterSlots;
110
+ private lastEmptyStateSlot;
111
+ private pendingCellSlots;
112
+ private pendingExpandableRowSlots;
113
+ private pendingGroupRowSlots;
114
+ private pendingHeaderSlots;
115
+ private pendingFilterSlots;
116
+ private pendingEmptyStateSlot;
117
+ constructor(host: EventTarget);
118
+ /**
119
+ * Apply a slots config. Returns true when the structural set changed (and
120
+ * therefore a re-render is required), false when the config is a no-op.
121
+ */
122
+ configure(config: FrameworkRenderedSlotsConfig): boolean;
123
+ hasCell(columnName: string): boolean;
124
+ hasHeader(columnName: string): boolean;
125
+ hasFilter(columnName: string): boolean;
126
+ hasGroupRow(): boolean;
127
+ hasExpandableRow(): boolean;
128
+ hasEmptyState(): boolean;
129
+ stageCell(column: GridColumnDef, row: GridRow, rowIndex: number): void;
130
+ stageExpandableRow(row: GridRow, rowIndex: number): void;
131
+ stageGroupRow(group: GroupItem): void;
132
+ stageHeader(column: GridColumnDef): void;
133
+ stageFilter(column: GridColumnDef, value: string, placeholder: string, disabled: boolean): void;
134
+ stageEmptyState(heading: string, description: string, labels: GridLabels): void;
135
+ /**
136
+ * Diff pending vs. last, dispatch `*SlotsChanged` events for every kind
137
+ * that moved, then swap last = pending and reset pending. Called at the
138
+ * end of every render pass.
139
+ */
140
+ flush(): void;
141
+ /**
142
+ * Data-only patch variant: merge pending entries into last without firing
143
+ * events, since the slot structure hasn't changed (only cell contexts
144
+ * refreshed). Wrappers that need context updates subscribe to
145
+ * `rowsVisibleChanged`.
146
+ */
147
+ mergePendingIntoLast(): void;
148
+ /**
149
+ * On element teardown, emit `removed` for every remaining slot so wrappers
150
+ * can destroy their views.
151
+ */
152
+ flushRemovals(): void;
153
+ private diffAndEmit;
154
+ private diffAndEmitSingleton;
155
+ }
156
+
157
+ /**
158
+ * Icon registry for the vanilla grid element.
159
+ *
160
+ * The grid draws its own sort / group / tree / expand / pin / pagination
161
+ * icons via inline SVGs. Consumers can override any individual icon by
162
+ * passing an `iconOverrides` map in GridOptions or by writing to the
163
+ * element's `controlIcons` property at runtime. This module owns the
164
+ * icon registry class plus the SVG markup helpers so the element file
165
+ * doesn't have to.
166
+ */
167
+ type UiGridControlIconKey = 'sortNone' | 'sortAsc' | 'sortDesc' | 'group' | 'groupExpanded' | 'groupCollapsed' | 'treeExpanded' | 'treeCollapsed' | 'expandExpanded' | 'expandCollapsed' | 'pin' | 'pinLeft' | 'pinRight' | 'paginationPrev' | 'paginationNext';
168
+ interface UiGridIconDefinition {
169
+ viewBox?: string;
170
+ path: string;
171
+ }
172
+ type UiGridIconOverrides = Partial<Record<UiGridControlIconKey, UiGridIconDefinition>>;
173
+ /**
174
+ * Holds the active set of icons (defaults merged with consumer overrides)
175
+ * and renders the inline SVG markup for each one. A single instance lives
176
+ * on the element.
177
+ */
178
+ declare class IconRegistry {
179
+ private overrides;
180
+ private resolved;
181
+ getOverrides(): UiGridIconOverrides;
182
+ setOverrides(value: UiGridIconOverrides): void;
183
+ resolve(key: UiGridControlIconKey): UiGridIconDefinition;
184
+ /** Render the SVG markup for a control icon (sort, group, pin, …). */
185
+ renderControlIcon(key: UiGridControlIconKey): string;
186
+ /** Render the SVG markup with an arbitrary wrapper class. */
187
+ renderIconWithClass(svgClass: string, key: UiGridControlIconKey): string;
188
+ }
189
+
190
+ /**
191
+ * Complete grid save state — parity with the old `ui.grid.saveState`
192
+ * module. Every field is optional so partial saves / restores work too.
193
+ *
194
+ * Opt-in/out behaviour mirrors the old module: by default save() serialises
195
+ * everything; consumers can disable per-field capture via
196
+ * `saveSort`/`saveFilter`/... options on `GridOptions`. Restore tolerates
197
+ * missing fields.
198
+ */
4
199
  interface GridSaveState {
5
- sortState: SortState;
6
- activeFilters: Record<string, string>;
7
- groupByColumns: string[];
8
- pinnedColumns: PinnedColumnState;
9
- columnOrder: string[];
10
- currentPage: number;
11
- pageSize: number;
200
+ sortState?: SortState;
201
+ activeFilters?: Record<string, string>;
202
+ groupByColumns?: string[];
203
+ collapsedGroups?: Record<string, boolean>;
204
+ pinnedColumns?: PinnedColumnState;
205
+ columnOrder?: string[];
206
+ columnWidthOverrides?: Record<string, string>;
207
+ currentPage?: number;
208
+ pageSize?: number;
209
+ selectedRowIds?: string[];
210
+ focusedCell?: GridCellPosition | null;
211
+ expandedRows?: Record<string, boolean>;
212
+ expandedTreeRows?: Record<string, boolean>;
213
+ scrollTop?: number;
214
+ scrollLeft?: number;
12
215
  }
13
216
  interface GridControllerSnapshot {
14
217
  options: GridOptions;
@@ -28,6 +231,13 @@ interface GridControllerSnapshot {
28
231
  pageSize: number;
29
232
  editingCell: GridCellPosition | null;
30
233
  editingValue: string;
234
+ /** Row ids currently selected. A ReadonlySet for surgical DOM updates —
235
+ * the renderer toggles the .ui-grid-row-selected class on every cell
236
+ * whose data-row matches one of these ids. */
237
+ selectedRowIds: ReadonlySet<string>;
238
+ selectAll: boolean;
239
+ focusedRowId: string | null;
240
+ selectedCount: number;
31
241
  }
32
242
  type GridControllerSubscriber = (snapshot: GridControllerSnapshot) => void;
33
243
  declare class VanillaGridController {
@@ -46,6 +256,21 @@ declare class VanillaGridController {
46
256
  private pageSize;
47
257
  private editingCell;
48
258
  private editingValue;
259
+ private exporterOverrides;
260
+ private validatorRegistry;
261
+ private rowEditState;
262
+ private rowEditTimers;
263
+ private rowEditSavePromiseOverrides;
264
+ private selectionState;
265
+ /** cellNav state. Persists the last focused cell so the public API's
266
+ * getFocusedCell returns the right value across re-renders, and tracks
267
+ * the focused-cells history for getCurrentSelection. */
268
+ private cellNavLastRowCol;
269
+ private cellNavFocusedCells;
270
+ /** Infinite-scroll state. Direction flags seed from options; dataLoading
271
+ * + previousVisibleRows are runtime bookkeeping for saveScrollPercentage /
272
+ * dataRemovedTop / dataRemovedBottom. */
273
+ private infiniteScrollState;
49
274
  private pipeline;
50
275
  private labels;
51
276
  private visibleColumns;
@@ -53,6 +278,11 @@ declare class VanillaGridController {
53
278
  private readonly subscribers;
54
279
  readonly gridApi: UiGridApi;
55
280
  constructor(options: GridOptions);
281
+ /** Disposer for the i18n language-change listener — cleared when the
282
+ * controller is torn down (matches the pattern used for other global
283
+ * subscriptions). */
284
+ private disposeLanguageListener;
285
+ dispose(): void;
56
286
  setOptions(options: GridOptions): void;
57
287
  /**
58
288
  * Fast-path: swap only the row data and rebuild the pipeline without
@@ -81,10 +311,81 @@ declare class VanillaGridController {
81
311
  expandAllTreeRows(): void;
82
312
  collapseAllTreeRows(): void;
83
313
  setTreeRowExpanded(row: GridRow | GridRecord | string, expanded: boolean): void;
84
- beginCellEdit(row: GridRow | GridRecord | string, columnName: string, triggerEvent?: Event | KeyboardEvent | null): void;
314
+ beginCellEdit(row: GridRow | GridRecord | string, columnName: string, triggerEvent?: Event | KeyboardEvent | null, initialValue?: string): void;
85
315
  updateEditingValue(value: string): void;
86
316
  commitCellEdit(): void;
87
317
  cancelCellEdit(): void;
318
+ /** Internal: run a core selection mutation, raise events, trigger a render. */
319
+ private applySelectionChange;
320
+ private allGridRows;
321
+ toggleRowSelectionByEntity(rowEntity: GridRecord, evt?: Event | null): void;
322
+ selectRow(rowEntity: GridRecord, evt?: Event | null): void;
323
+ selectRowByVisibleIndex(rowNum: number, evt?: Event | null): void;
324
+ selectRowByKey(isInEntity: boolean, key: string, comparator: unknown, evt?: Event | null, lookInRows?: readonly GridRow[]): void;
325
+ unSelectRow(rowEntity: GridRecord, evt?: Event | null): void;
326
+ unSelectRowByVisibleIndex(rowNum: number, evt?: Event | null): void;
327
+ unSelectRowByKey(isInEntity: boolean, key: string, comparator: unknown, evt?: Event | null, lookInRows?: readonly GridRow[]): void;
328
+ selectAllRows(evt?: Event | null): void;
329
+ selectAllVisibleRows(evt?: Event | null): void;
330
+ clearSelectedRows(evt?: Event | null): void;
331
+ shiftSelectRow(rowEntity: GridRecord, evt?: Event | null): void;
332
+ getSelectedRows(): GridRecord[];
333
+ getUnSelectedRows(): GridRecord[];
334
+ getSelectedGridRows(): GridRow[];
335
+ getUnSelectedGridRows(): GridRow[];
336
+ setMultiSelect(multiSelect: boolean): void;
337
+ setModifierKeysToMultiSelect(value: boolean): void;
338
+ /** Focus a row by id — the old module's `row.setFocused(true)` with all
339
+ * the book-keeping. Raises rowFocusChanged and refreshes. */
340
+ setRowFocused(rowId: string, focused: boolean, evt?: Event | null): void;
341
+ getRowSelectionState(): Readonly<GridSelectionState>;
342
+ /** Resolved/defaulted selection options — exposed for the element layer
343
+ * so it can decide whether to wire pointer handlers without duplicating
344
+ * default logic. */
345
+ getResolvedSelectionOptions(): ReturnType<typeof resolveGridSelectionOptions>;
346
+ /** Public analogue of findRowById — the element uses this during drag
347
+ * painting to resolve a row-id back to its entity without calling the
348
+ * selection API methods that would double-raise events. */
349
+ findRowByIdPublic(rowId: string): GridRow | null;
350
+ /** Record a navigation event. Pushes to the focused-cells history,
351
+ * updates lastRowCol, and raises cellNav.navigate on the public API. */
352
+ setCellNavFocus(rowId: string | null, columnName: string | null, opts?: {
353
+ appendToSelection?: boolean;
354
+ }): void;
355
+ /** Raise viewPortKeyDown / viewPortKeyPress on behalf of the element.
356
+ * Element gates this with `options.keyDownOverrides`. */
357
+ raiseCellNavKeyEvent(type: 'keydown' | 'keypress', event: KeyboardEvent): void;
358
+ private cellNavGetFocusedCell;
359
+ private cellNavGetCurrentSelection;
360
+ private cellNavRowColSelectIndex;
361
+ private resolveCellNavRowCol;
362
+ /** cellNav.scrollToFocus — resolves rowEntity + colDef into a logical
363
+ * focus and delegates to the element (if attached) for actual scrolling.
364
+ * When the element isn't wired (e.g. bare controller in tests), the
365
+ * state still updates and the promise resolves immediately. */
366
+ private cellNavScrollToFocus;
367
+ /** Element registers its scroll-and-focus handler here so the API
368
+ * binding above can delegate DOM work. Controller stays DOM-free. */
369
+ private cellNavScrollRequest;
370
+ setCellNavScrollHandler(handler: ((rowId: string | null, columnName: string | null) => void) | null): void;
371
+ /** Read-only accessor for the current options. Used by the element to
372
+ * inspect keyDownOverrides / modifierKeysToMultiSelectCells without
373
+ * coupling it to a private field. */
374
+ getOptions(): GridOptions;
375
+ /** Evaluate whether the current scroll position should request more
376
+ * data at the top or bottom. Called from the element's scroll frame. */
377
+ evaluateInfiniteScroll(startIndex: number, visibleRows: number, viewportRows: number): void;
378
+ private infiniteScrollDataLoaded;
379
+ private infiniteScrollResetScroll;
380
+ private infiniteScrollSavePercentage;
381
+ private infiniteScrollDataRemovedTop;
382
+ private infiniteScrollDataRemovedBottom;
383
+ private infiniteScrollSetDirections;
384
+ /** Element registers a scroll-to-top handler here so resetScroll can
385
+ * bring the viewport back to 0. Keeps the controller DOM-free. */
386
+ private infiniteScrollScrollToTopRequest;
387
+ setInfiniteScrollResetHandler(handler: (() => void) | null): void;
388
+ getInfiniteScrollState(): Readonly<GridInfiniteScrollState>;
88
389
  canResizeColumns(): boolean;
89
390
  setColumnWidthOverride(columnName: string, widthPx: number): void;
90
391
  /** Returns the grid-template-columns string as if `columnName` had the given width,
@@ -106,6 +407,12 @@ declare class VanillaGridController {
106
407
  getSortDirection(column: GridColumnDef): SortDirection;
107
408
  isPinningEnabled(): boolean;
108
409
  isColumnPinnable(column: GridColumnDef): boolean;
410
+ /** True when the given column is editable in principle — does NOT evaluate
411
+ * a per-row `cellEditableCondition`. Use isCellEditable for that. */
412
+ isColumnEditable(column: GridColumnDef): boolean;
413
+ /** True when the (row, column) cell is editable, respecting both the
414
+ * column's `enableCellEdit` flag and any `cellEditableCondition`. */
415
+ isCellEditable(row: GridRow, column: GridColumnDef): boolean;
109
416
  isPinned(column: GridColumnDef): boolean;
110
417
  isPinnedLeftLast(column: GridColumnDef): boolean;
111
418
  isPinnedRightFirst(column: GridColumnDef): boolean;
@@ -124,8 +431,26 @@ declare class VanillaGridController {
124
431
  expandToggleLabel(row: GridRow): string;
125
432
  editorInputType(column: GridColumnDef): string;
126
433
  isEditingCell(rowId: string, columnName: string): boolean;
434
+ /**
435
+ * Serialise the current grid state. Mirrors `ui.grid.saveState.save`:
436
+ * every field is gated by an `options.save*` flag with the old grid's
437
+ * defaults. A missing flag keeps the matching field out of the result.
438
+ */
127
439
  getState(): GridSaveState;
440
+ /**
441
+ * Apply a previously saved state. All fields are optional — missing
442
+ * fields are left at whatever the controller currently has. Matches the
443
+ * old grid's `ui.grid.saveState.restore`.
444
+ */
128
445
  setState(state: Partial<GridSaveState>): void;
446
+ /** Element injects its scroll accessor so save() can capture scrollTop/
447
+ * scrollLeft. Controller stays DOM-free. */
448
+ private saveStateScrollSnapshot;
449
+ private saveStateScrollRestore;
450
+ setSaveStateScrollHandlers(snapshot: (() => {
451
+ scrollTop: number;
452
+ scrollLeft: number;
453
+ }) | null, restore: ((scrollTop: number, scrollLeft: number) => void) | null): void;
129
454
  private refresh;
130
455
  private emit;
131
456
  private findRowById;
@@ -141,6 +466,67 @@ declare class VanillaGridController {
141
466
  private setRowInvisible;
142
467
  private clearRowInvisible;
143
468
  private benchmark;
469
+ /** Returns the rows that match the requested exporter row-type. Mirrors
470
+ * the old grid's row selection logic: `all` uses the full data set (or
471
+ * `exporterAllDataFn` when provided), `visible` uses the current pipeline
472
+ * output, `selected` uses the selection state. */
473
+ private exporterRowsFor;
474
+ private resolveExporterOptions;
475
+ private buildCsv;
476
+ private exportCsv;
477
+ private buildPdfDocDefinition;
478
+ private exportPdf;
479
+ private buildExcelSheetData;
480
+ private exportExcel;
481
+ /** Runs the validators declared on `colDef.validators` against the new
482
+ * value and flips `$$invalid<col>` / `$$errors<col>` on the entity. On
483
+ * failure raises `validate.validationFailed`. Async validators are
484
+ * awaited so the caller can `await` for complete results. */
485
+ private validateRunValidators;
486
+ private validateGetFormattedErrors;
487
+ private validateGetTitleFormattedErrors;
488
+ private validateGetInvalidRows;
489
+ /** Test hook — exposes the validator registry so consumers can inspect or
490
+ * extend it outside of the gridApi surface (e.g. from a controller-level
491
+ * plugin). */
492
+ getValidatorRegistry(): GridValidatorRegistry;
493
+ /** Set by the element so `importAFile()` can trigger its file-picker flow.
494
+ * Controller stays DOM-free: it just invokes whatever handler was wired. */
495
+ private importerFilePickerRequest;
496
+ setImporterFilePickerHandler(handler: (() => void) | null): void;
497
+ private resolveImporterOptions;
498
+ private importerRequestFile;
499
+ private importerDispatchObjects;
500
+ private importerImportText;
501
+ private importerImportThisFile;
502
+ private buildExporterMenuItems;
503
+ /** Menu items the importer contributes. Just the single "Import"
504
+ * entry when the feature is enabled; returns an empty array otherwise. */
505
+ private buildImporterMenuItems;
506
+ /** Menu items row-edit contributes: "Save changes" + "Retry errored
507
+ * rows". Each entry is gated by both a gridOption flag and a runtime
508
+ * predicate (`hasDirtyRows` / `hasErrorRows`) so the menu only surfaces
509
+ * actions that have something to do. */
510
+ private buildRowEditMenuItems;
511
+ private rowEditSetSavePromise;
512
+ private rowEditGetDirtyRows;
513
+ private rowEditGetErrorRows;
514
+ private rowEditFlushDirtyRows;
515
+ /** Re-fires `saveRow` for every row currently in the error state. The
516
+ * "Retry errored rows" menu entry funnels through here. Returns a
517
+ * promise that resolves once every retry promise has settled. */
518
+ private rowEditRetryErroredRows;
519
+ private rowEditSetRowsDirty;
520
+ private rowEditSetRowsClean;
521
+ private considerSetRowEditTimer;
522
+ private cancelRowEditTimer;
523
+ private runRowEditSave;
524
+ /** Test hook — reports the current row-edit dirty / error / saving state. */
525
+ getRowEditState(): {
526
+ dirtyRowIds: string[];
527
+ errorRowIds: string[];
528
+ savingRowIds: string[];
529
+ };
144
530
  }
145
531
  declare function createVanillaGridController(options: GridOptions): VanillaGridController;
146
532
 
@@ -155,49 +541,86 @@ type VanillaUiGridElement = HTMLElement & {
155
541
  */
156
542
  setData(rows: GridRecord[]): void;
157
543
  };
158
- type UiGridControlIconKey = 'sortNone' | 'sortAsc' | 'sortDesc' | 'group' | 'groupExpanded' | 'groupCollapsed' | 'treeExpanded' | 'treeCollapsed' | 'expandExpanded' | 'expandCollapsed' | 'pin' | 'pinLeft' | 'pinRight' | 'paginationPrev' | 'paginationNext';
159
- interface UiGridIconDefinition {
160
- viewBox?: string;
161
- path: string;
162
- }
163
- type UiGridIconOverrides = Partial<Record<UiGridControlIconKey, UiGridIconDefinition>>;
544
+ type VanillaGridOptions = GridOptions & {
545
+ iconOverrides?: UiGridIconOverrides;
546
+ };
164
547
  declare class UiGridStandaloneElement extends HTMLElement {
165
- private controller;
166
- private snapshot;
167
- private unsubscribe;
168
- private activeOptions;
169
- private attributeOptions;
170
- private attributeSyncScheduled;
171
- private iconOverrides;
172
- private templateObserver;
173
- private openPinMenuColumn;
174
- private focusedCell;
175
- private draggedColumnName;
176
- private dropTargetColumnName;
177
- private scrollPosition;
178
- private horizontalScrollPosition;
179
- private scrollFrame;
180
- private suppressScrollEvent;
181
- private lastVirtualStartIndex;
182
- private measuredHeaderStickyHeight;
183
- private measuredFilterStickyHeight;
184
- private stickyHeightRelayoutQueued;
185
- private benchmarkAverage;
186
- private skipNextRender;
187
- private dataFrame;
188
- private pendingPatchedRowIds;
189
- private pendingDataRefreshMode;
190
- private lastScrollActivityAt;
548
+ /** @internal */ controller: VanillaGridController | null;
549
+ /** @internal */ snapshot: GridControllerSnapshot | null;
550
+ /** @internal */ unsubscribe: (() => void) | null;
551
+ /** @internal */ activeOptions: VanillaGridOptions | null;
552
+ /** @internal */ attributeOptions: Partial<GridOptions>;
553
+ /** @internal */ attributeSyncScheduled: boolean;
554
+ /** @internal Owns the SVG-icon registry: defaults merged with consumer
555
+ * overrides plus rendering helpers. See `./icons.ts`. */
556
+ icons: IconRegistry;
557
+ /** @internal */ templateObserver: MutationObserver | null;
558
+ /** @internal */ openPinMenuColumn: string | null;
559
+ /** @internal */ focusedCell: {
560
+ rowId: string;
561
+ columnName: string;
562
+ } | null;
563
+ /** @internal */ draggedColumnName: string | null;
564
+ /** @internal */ dropTargetColumnName: string | null;
565
+ /** @internal */ scrollPosition: number;
566
+ /** @internal */ horizontalScrollPosition: number;
567
+ /** @internal */ scrollFrame: number | null;
568
+ /** @internal */ suppressScrollEvent: boolean;
569
+ /** @internal */ lastVirtualStartIndex: number;
570
+ /** @internal */ measuredHeaderStickyHeight: number;
571
+ /** @internal */ measuredFilterStickyHeight: number;
572
+ /** @internal */ stickyHeightRelayoutQueued: boolean;
573
+ /** @internal */ benchmarkAverage: string;
574
+ /** @internal */ skipNextRender: boolean;
575
+ /** @internal */ autoResizeObserver: ResizeObserver | null;
576
+ /** @internal */ autoResizeDebounceHandle: number | null;
577
+ gridTitle: string;
578
+ gridTableStyle: string;
579
+ bodyViewportStyle: string;
580
+ templateColumns: string;
581
+ slotRegistry: string;
582
+ headerContent: string;
583
+ filterRowContent: string;
584
+ bodyContent: string;
585
+ paginationContent: string;
586
+ /** @internal */ dataFrame: number | null;
587
+ /** @internal */ pendingPatchedRowIds: Set<string> | null;
588
+ /** @internal */ pendingDataRefreshMode: 'patch' | 'virtual' | 'full' | null;
589
+ /** @internal */ lastScrollActivityAt: number;
590
+ /** @internal */ lastStructureKey: string | null;
591
+ /** @internal */ lastItemsFingerprint: string | null;
592
+ /** @internal */ lastVirtualOffset: number;
593
+ /** @internal */ lastTotalVirtualHeight: number;
594
+ /**
595
+ * @internal Framework-rendered slot bridge. See `./framework-slots.ts`.
596
+ */
597
+ frameworkSlots: FrameworkSlotBridge;
191
598
  static get observedAttributes(): string[];
192
599
  get options(): GridOptions;
193
600
  set options(value: GridOptions);
601
+ /**
602
+ * Declare which slots are framework-rendered. When a slot is flagged, the
603
+ * element renders a `<slot name="…">` placeholder at the usual site instead
604
+ * of running its string-interpolation template, and dispatches
605
+ * `*SlotsChanged` events so a wrapper can insert light-DOM content with a
606
+ * matching `slot` attribute.
607
+ *
608
+ * Pass an empty object (or `{}`) to revert every slot kind back to the
609
+ * default interpolation path. Individual fields behave the same way:
610
+ * omitting `cells` means the cells list is unchanged; passing
611
+ * `cells: []` clears it.
612
+ *
613
+ * This is the bridge the Angular / React wrappers use to project native
614
+ * templates (ng-template, JSX render props) into the grid. Consumers of
615
+ * the web component directly can ignore this method — the default
616
+ * `<template slot="cell-columnName">` workflow still works.
617
+ */
618
+ setFrameworkRenderedSlots(config: FrameworkRenderedSlotsConfig): void;
194
619
  attributeChangedCallback(_name: string, _oldValue: string | null, _newValue: string | null): void;
195
620
  private syncAttributesToOptions;
196
- private parseBooleanAttribute;
197
- private parseNumberAttribute;
198
- private parseJsonAttribute;
199
621
  get controlIcons(): UiGridIconOverrides;
200
622
  set controlIcons(value: UiGridIconOverrides);
623
+ private resolveIcon;
201
624
  get gridId(): string;
202
625
  set gridId(value: string);
203
626
  get title(): string;
@@ -206,8 +629,6 @@ declare class UiGridStandaloneElement extends HTMLElement {
206
629
  set rowHeight(value: number);
207
630
  get headerRowHeight(): number;
208
631
  set headerRowHeight(value: number);
209
- get viewportHeight(): number;
210
- set viewportHeight(value: number);
211
632
  get paginationPageSize(): number;
212
633
  set paginationPageSize(value: number);
213
634
  get paginationCurrentPage(): number;
@@ -289,21 +710,87 @@ declare class UiGridStandaloneElement extends HTMLElement {
289
710
  private patchCells;
290
711
  connectedCallback(): void;
291
712
  disconnectedCallback(): void;
292
- private ensureShadowRoot;
293
- private ensureController;
713
+ /** @internal */
714
+ ensureShadowRoot(): ShadowRoot;
715
+ /** @internal */
716
+ ensureController(options: VanillaGridOptions): void;
717
+ /** Hidden file input used by `gridApi.importer.importAFile()`. Lazily
718
+ * created on first use and reused thereafter. */
719
+ private importerFileInput;
720
+ private openImporterFilePicker;
294
721
  private observeTemplateSlots;
295
- private buildEffectiveOptions;
296
722
  private bindEvents;
723
+ private syncHeaderHorizontalScroll;
724
+ private maybeTriggerInfiniteScroll;
725
+ /** @internal */
726
+ buildEffectiveOptions(options: VanillaGridOptions): VanillaGridOptions;
727
+ /**
728
+ * Ask the controller to evaluate whether the current scroll position
729
+ * should request more data at the top or bottom — ports the old grid's
730
+ * handleScroll → loadData check. The controller does the actual
731
+ * needLoadMoreData / needLoadMoreDataTop raise.
732
+ */
733
+ /**
734
+ * Mirror horizontal scroll from the body viewport onto the header + filter
735
+ * strips by setting `scrollLeft` on them. Because those strips are real
736
+ * scroll containers (with their scrollbars hidden), `position: sticky` on
737
+ * pinned header / filter cells anchors to the strip's scroll position the
738
+ * same way body cells anchor to `.grid-body-viewport`. This is the
739
+ * imperative sync strategy the old ui-grid used in ui-grid-viewport.js.
740
+ */
297
741
  /**
298
742
  * Targeted update for virtual scroll: replaces only the rendered rows inside
299
743
  * the existing `.grid-virtual-body` without touching the scroll container.
300
744
  * This preserves momentum/inertia scrolling because the `.grid-table` element
301
745
  * is never destroyed.
302
746
  */
303
- private renderVirtualBody;
304
- private render;
747
+ /** @internal */
748
+ renderVirtualBody(): void;
749
+ /** @internal */
750
+ render(): void;
751
+ /** @internal */
752
+ private inAutoAdjust;
753
+ private lastAutoHeight;
754
+ private autoAdjustHeight;
755
+ measuredPaginationHeight(): number;
756
+ /** @internal */
757
+ setupAutoResize(): void;
758
+ /** @internal */
759
+ teardownAutoResize(): void;
760
+ private buildRenderPlan;
761
+ private renderFull;
762
+ private renderPatch;
763
+ private reconcileBodyRoot;
764
+ private reconcilePagination;
765
+ private patchFilterCells;
766
+ private fingerprintItems;
767
+ private patchExistingRows;
768
+ private patchGroupRow;
769
+ private patchBodyCell;
770
+ private patchPagination;
305
771
  private renderHeaderCell;
306
772
  private renderFilterCell;
773
+ /**
774
+ * Commit the in-flight edit, then move keyboard focus relative to the
775
+ * committed cell. Splits commit + focus across two paint frames: commit
776
+ * runs synchronously (editor unmounts, cell re-renders with the new value),
777
+ * then focus moves to the adjacent cell. Scheduling the focus in a
778
+ * microtask avoids racing the blur handler's re-entry guard and ensures
779
+ * the destination cell is fully reattached in the DOM.
780
+ *
781
+ * If the source cell was being edited, the destination cell enters edit
782
+ * mode too (provided the destination column is editable). This keeps
783
+ * Tab/Enter nav inside an edit session continuous — you keep editing as
784
+ * you move. Arrow nav outside an edit session does NOT auto-enter edit.
785
+ */
786
+ private commitAndMove;
787
+ private moveGridFocus;
788
+ private scrollFocusedRowIntoView;
789
+ private handleRowSelectionClick;
790
+ private handleRowHeaderCheckboxClick;
791
+ private matchesKeyOverride;
792
+ private applyFocusedCellClass;
793
+ private focusCellElement;
307
794
  private measureAutoColumnWidth;
308
795
  private renderDisplayItem;
309
796
  private renderBodyCell;
@@ -313,18 +800,51 @@ declare class UiGridStandaloneElement extends HTMLElement {
313
800
  private renderCellTemplateFromMarkup;
314
801
  private renderExpandableTemplate;
315
802
  private getTemplateMarkup;
316
- private interpolateTemplate;
317
- private resolveTemplateValue;
803
+ /** @internal Used by the render module to interpolate consumer templates. */
804
+ interpolateTemplate(templateMarkup: string, context: Record<string, unknown>): string;
318
805
  private rowIndexFor;
319
806
  private classifyDataRefresh;
320
807
  private getRenderedDisplayWindow;
321
808
  private cellSlotName;
322
- private downloadCsv;
809
+ private renderEmptyState;
810
+ /** @internal */
811
+ downloadCsv(rowType?: 'all' | 'visible' | 'selected', colType?: 'all' | 'visible'): void;
323
812
  private renderControlIcon;
324
813
  private renderIconWithClass;
325
814
  }
326
815
  declare function defineStandaloneUiGridElement(tagName?: string): Promise<void>;
327
816
 
817
+ /**
818
+ * A declarative template element for defining reusable cell/row templates
819
+ * without writing JavaScript.
820
+ *
821
+ * Supports both `${this.prop}` and `{{prop}}` binding syntax.
822
+ *
823
+ * @example
824
+ * ```html
825
+ * <template is="ui-grid-template" name="ui-status-badge" status="unknown">
826
+ * <style>
827
+ * :host { display: inline-block; }
828
+ * .badge { padding: 2px 8px; border-radius: 4px; font-size: 0.8em; }
829
+ * .badge-active { background: #d1fae5; color: #065f46; }
830
+ * .badge-inactive { background: #fee2e2; color: #991b1b; }
831
+ * </style>
832
+ * <span class="badge badge-${this.status}">${this.status}</span>
833
+ * </template>
834
+ *
835
+ * <ui-grid-element>
836
+ * <template slot="cell-status">
837
+ * <ui-status-badge status="{{value}}"></ui-status-badge>
838
+ * </template>
839
+ * </ui-grid-element>
840
+ * ```
841
+ */
842
+ declare class UIGridTemplate extends HTMLTemplateElement {
843
+ static readonly TAG = "ui-grid-template";
844
+ connectedCallback(): void;
845
+ static define(tagName?: string): void;
846
+ }
847
+
328
848
  interface UiGridRustWebModule {
329
849
  default(input?: unknown): Promise<unknown>;
330
850
  build_pipeline_js(context: BuildGridPipelineContext): PipelineResult;
@@ -333,4 +853,4 @@ interface UiGridRustWebModule {
333
853
  declare function registerVanillaUiGridRustModule(module: UiGridRustWebModule, input?: unknown): Promise<void>;
334
854
  declare function mountVanillaUiGrid(target: Element, options: GridOptions, rustModule?: UiGridRustWebModule, tagName?: string): Promise<VanillaUiGridElement>;
335
855
 
336
- export { type GridControllerSnapshot, type GridSaveState, type UiGridControlIconKey, type UiGridIconDefinition, type UiGridIconOverrides, type UiGridRustWebModule, UiGridStandaloneElement, VanillaGridController, type VanillaUiGridElement, createVanillaGridController, defineStandaloneUiGridElement, defineStandaloneUiGridElement as defineUiGridElement, mountVanillaUiGrid, registerVanillaUiGridRustModule };
856
+ export { type FrameworkCellSlot, type FrameworkEmptyStateSlot, type FrameworkExpandableRowSlot, type FrameworkFilterSlot, type FrameworkGroupRowSlot, type FrameworkHeaderSlot, type FrameworkRenderedSlotsConfig, type FrameworkSlotDelta, type GridControllerSnapshot, type GridSaveState, UIGridTemplate, type UiGridControlIconKey, type UiGridIconDefinition, type UiGridIconOverrides, type UiGridRustWebModule, UiGridStandaloneElement, VanillaGridController, type VanillaUiGridElement, createVanillaGridController, defineStandaloneUiGridElement, defineStandaloneUiGridElement as defineUiGridElement, mountVanillaUiGrid, registerVanillaUiGridRustModule };