@reforgium/data-grid 2.5.4 → 3.1.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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,310 @@
1
+ ## [3.1.0]: 02.04.2026
2
+
3
+ ### Feat:
4
+ - `DataGrid`: added `columnResizeEnd` output (`GridColumnResizeEndEvent`) — emits the column key and final pixel width when the user finishes a resize drag; use this to persist column widths
5
+
6
+ ### Fix:
7
+ - `DataGrid`: multi-sort — `sortOrder` was left stale (`'desc'`) after removing a sort key because `setSortOrder` was only called when `activeOrder` was truthy; now always resets to `'asc'` when no active order
8
+ - `DataGrid`: multi-sort — `sortField` was not cleared after removing the last sort key, leaving a stale pointer to a column with no active sort; `sortField` is now set to `undefined` when `sorts` becomes empty
9
+ - `DataGrid`: column layout — `roundAndFix` could leave a visual gap when all auto-sized columns had reached their `maxWidth`; remaining rounding pixels are now distributed even if `maxWidth` is exceeded by 1 px each (sub-pixel correction, not a layout concern)
10
+ - `DataGrid`: infinity source parallel prefetch — page data captured in `.then()` immediately on response to prevent Angular effect batching from discarding a page when multiple requests settle before the next effect tick
11
+ - `DataGrid`: infinity source — stale `updatePage()` responses no longer corrupt the buffer; a generation counter is bumped on every `clearBuffer()` and `clearQueue()`, and `.then()` skips writing if the generation has advanced (e.g. source version change, source swap, `clearState`)
12
+ - `DataGrid`: tooltip — first `mouseenter` on a column with `tooltip` was silently dropped when the tooltip feature had not yet been lazy-loaded; the hover args are now retained and forwarded once the feature resolves; hover cancelled before load resolves is correctly discarded
13
+ - `DataGrid`: selection reconcile — `selectChange` was not emitted when the grid automatically pruned selected keys after a data update removed rows that were previously selected; the event is now emitted with the surviving keys whenever the selected set shrinks
14
+
15
+ ### Test:
16
+ - added regression tests for all fixes above
17
+
18
+ ---
19
+
20
+ ## [3.0.0]: 17.03.2026
21
+
22
+ ### Feat:
23
+ - `DataGrid`: added global `provideDataGridHeaderTextResolver(...)` DI hook for resolving plain column/group header text (for example via Presentia) without per-table header templates
24
+ - `DataGrid`: expanded `GridPagedDataSource` with source-level sorting and error state (`sort`, `error`, `updateSort(...)`, `updateSorts(...)`)
25
+ - `DataGrid`: source mode now syncs current sort state from `source.sort` and delegates user sorting directly to source hooks when available
26
+ - `DataGrid`: source page-size changes now use `source.updatePageSize(...)` when supported, which improves direct `PagedQueryStore` integration
27
+ - `DataGrid`: added source mode for page-oriented external stores/sources with internal infinity buffering instead of parent-managed accumulated arrays
28
+ - `DataGrid`: added gap-loader support for source/infinity mode to keep scroll continuity while pages are still loading
29
+
30
+ ### Fix:
31
+ - `DataGrid`: template cell rendering now applies column `align` by default (can still be overridden in custom template markup)
32
+ - `DataGrid`: infinity `pageChange` no longer emits when `total` is empty (`0`) before first data load
33
+ - `DataGrid`: header-group `title` now stays available in VM/template context when `titleTemplate` is used
34
+ - `DataGridPaginator`: page buttons now keep base height but expand horizontally to fit content/custom labels
35
+ - `DataGrid`: source infinity prefetch now defaults to sequential page warming for compatibility with latest-wins sources such as `PagedQueryStore`; opt into `prefetchMode: 'parallel'` only for concurrency-safe sources
36
+ - `DataGrid`: fixed empty-grid infinity request behavior before first load
37
+ - `DataGrid`: fixed sub-header template resolution / rendering for grouped headers
38
+ - `DataGrid`: loading presentation was corrected for the current v3 surface
39
+ - `DataGrid`: added practical ARIA improvements for grid sizing, row/cell indices, expander state, resize handles, tooltip linkage, and live loading/empty announcements
40
+ - `DataGrid`: removed lightweight RxJS-only listener plumbing from resize / scroll feature internals
41
+
42
+ ### Docs:
43
+ - `README`: clarified that `source` mode does not auto-fetch on mount; parent should configure filters/params first and then trigger the initial load explicitly
44
+ - `README`: documented direct `PagedQueryStore` compatibility for source mode, including sort delegation and page-size updates
45
+ - `README`: added global header-text resolver docs and expanded source-mode guidance
46
+
47
+ ---
48
+
49
+ ## [2.5.0]: 26.02.2026
50
+
51
+ ### Feat:
52
+ - `DataGrid`: extensible cell `type` support (`GridBuiltInCellType | string`) for custom type names
53
+ - `DataGrid`: global type registries via DI:
54
+ - `provideDataGridTypeTransformers(...)` (`type -> transformer`)
55
+ - `provideDataGridTypeRenderers(...)` (`type -> TemplateRef`)
56
+ - `DataGrid`: type render resolution precedence extended with DI registry (local `reDataGridTypeCell` still has priority over global type renderers)
57
+ - `DataGrid`: callback contexts expanded with row meta (`index`, `col`, `type`, `isPinned`)
58
+ - `DataGrid`: `isPinned` flag added to cell template contexts (`reDataGridTypeCell`, `reDataGridCell`, typed/global templates) and tooltip template context
59
+
60
+ ### DX / API:
61
+ - `GridCellRenderer` (`value`) now supports second argument context: `value(row, ctx)`
62
+ - `GridCellTransformer` standardized as `transformer(row, ctx)`
63
+ - `RenderTemplateData` now includes `isPinned`
64
+ - `GridTooltipContext` now includes `isPinned`
65
+
66
+ ### Docs:
67
+ - `README`: added docs/examples for global type registries, renderer/value precedence, selection guidance for infinity mode, and `isPinned` callback/template context usage
68
+
69
+ ---
70
+
71
+ ## [2.4.0]: 20.02.2026
72
+
73
+ ### Feat:
74
+ - `DataGrid`: added header drag-resize for columns with `resizable` support (`minWidth` / `maxWidth` respected)
75
+ - `DataGrid`: added provider defaults `resizable` and `translations.indexColumnHeader` for global resize behavior and index header text
76
+
77
+ ---
78
+
79
+ ## [2.3.6]: 19.02.2026
80
+
81
+ ### Fix:
82
+ - `ColumnManager`: pin action no longer reorders columns; pin now updates only sticky side while preserving current column order
83
+ - `sort-ic`: added not sorted state
84
+
85
+ ---
86
+
87
+ ## [2.3.5]: 19.02.2026
88
+
89
+ ### Fix:
90
+ - `DataGrid`: fixed expander state reset when `pinnedRows` input changes (expanded groups now stay intact)
91
+
92
+ ---
93
+
94
+ ## [2.3.4]: 19.02.2026
95
+
96
+ ### Fix:
97
+ - `DataGrid`: fixed expander state handling for multiple independent `expandBy` groups (toggling one group no longer resets/opens other groups)
98
+
99
+ ---
100
+
101
+ ## [2.3.3]: 19.02.2026
102
+
103
+ ### Feat:
104
+ - `ColumnManager`: added `showAllLabel` and `hideAllLabel` inputs to customize controls text
105
+
106
+ ### Fix:
107
+ - `ColumnManager`: column visibility now toggles only on eye icon click (row/title click no longer toggles visibility)
108
+
109
+ ---
110
+
111
+ ## [2.3.1]: 18.02.2026
112
+
113
+ ### Fix:
114
+ - `ColumnManager`: custom trigger now supports `ng-template[reDataGridColumnManagerTrigger]` and works correctly with interactive projected content
115
+
116
+ ---
117
+
118
+ ## [2.3.0]: 18.02.2026
119
+
120
+ ### Feat:
121
+ - `ColumnManager`: pin action redesigned into compact current-state button with mini popover selection (`left` / `none` / `right`)
122
+ - `ColumnManager`: custom trigger via projected content marker `reDataGridColumnManagerTrigger`
123
+ - `ColumnManager`: custom column title template via projected `ng-template[reDataGridColumnManagerColumnTitle]`
124
+ - `ColumnManager`: panel positioning now clamps to viewport to prevent right-edge overflow
125
+
126
+ ### Refactor:
127
+ - `ColumnManager`: component split into dedicated `.ts` / `.html` / `.scss` files
128
+
129
+ ### Notes:
130
+ - `ColumnManager`: trigger and list title customization APIs moved to projected content (`reDataGridColumnManagerTrigger`, `reDataGridColumnManagerColumnTitle`)
131
+
132
+ ---
133
+
134
+ ## [2.2.3]
135
+ ### Fix:
136
+ - `DataGrid`: fixed declarative columns initialization timing to avoid empty/incorrect state before content refs are ready
137
+
138
+ ---
139
+
140
+ ## [2.2.2]
141
+ ### Fix:
142
+ - `DataGrid`: fixed added missed merge columns helper
143
+
144
+ ---
145
+
146
+ ## [2.2.1]
147
+ ### Feat:
148
+ - `DataGrid`: `sortMode` with multi-sort flow
149
+ - `DataGrid`: public API methods `clearSelection`, `selectAllLoaded`, `resetSort`, `setSort`, `setMultiSort`
150
+ - `DataGrid`: `isRowDisabled` predicate for row-level disabling
151
+ - `DataGrid`: focus ring support
152
+ - `DataGrid`: ARIA improvements
153
+ - `DataGrid`: auto tooltip by value via `tooltip: true`
154
+
155
+ ### Perf/Opt:
156
+ - `DataGrid`: multi-sort order lookup moved to hash map
157
+ - `DataGrid`: selection and select-all optimizations for infinity mode
158
+ - `DataGrid`: reduced infinity prefetch aggressiveness (`PREFETCH_FACTOR`)
159
+ - `DataGrid`: effect/call orchestration cleanup to reduce unnecessary recalculations
160
+
161
+ ### Refactor:
162
+ - `DataGrid`: split large component logic into feature modules (selection/sort/sticky/tooltip/overlay/virtual-scroll/header/infinity orchestration)
163
+ - `DataGrid`: additional orchestration extraction and cleanup in feature layer
164
+
165
+ ### Fix:
166
+ - `DataGrid`: select-all behavior fixes for loaded-only strategy in infinity scenarios
167
+ - `DataGrid`: sorting behavior fixes (single/multi integration and state sync)
168
+ - `DataGrid`: follow-up fixes for selection and scroll-driven flows
169
+
170
+ ### Test:
171
+ - expanded feature-level tests for refactored modules
172
+ - added surface API tests for `DataGrid` sort methods delegation
173
+
174
+ ---
175
+
176
+ ## [2.1.1]: 11.02.2026
177
+
178
+ ### Feat:
179
+ - `DataGrid`: CSS vars for header/body gap and hover rounding
180
+
181
+ ### Fix:
182
+ - `DataGrid`: pinned rows use the same cell renderer pipeline as regular rows (align/sticky/type/templates/tooltip/width)
183
+
184
+ ---
185
+
186
+ ## [2.1.0]: 11.02.2026
187
+
188
+ ### Feat:
189
+ - `merge` declarative columns with column manager state (visible/disabled/sticky prioritized from state)
190
+ - `DataGrid`: `lockVerticalScroll` flag to disable vertical scrolling
191
+ - `DataGrid`: `defer*` flags to defer rendering
192
+ - expanded public API exports for consumer typing
193
+
194
+ ### Fix:
195
+ - `DataGrid`: fix header width rendering
196
+ - `ColumnManager`: fix hidden state color
197
+
198
+ ### Test:
199
+ - added unit tests for column merge helpers
200
+
201
+ ### Docs:
202
+ - updated README with missing CSS variables (grid + column manager), column manager inputs, and lock/defer inputs
203
+
204
+ ---
205
+
206
+ ## [2.0.1]: 09.02.2026
207
+ ### Chore:
208
+ - excluded source maps from the npm package
209
+
210
+ ---
211
+
212
+ ## [2.0.0]: 09.02.2026
213
+
214
+ ### Feat:
215
+ - Header groups (two-level headers)
216
+ - Declarative columns DSL (`<re-dg-column>`)
217
+ - Skeleton loading mode (pagination + infinity)
218
+ - Sticky row + custom row templates
219
+ - Row/cell events: click, context menu, double click
220
+ - Column manager dropdown (reorder, show/hide, pin) + trigger template
221
+ - Tooltip popover per column (`tooltip`: string, fn, or `TemplateRef`)
222
+ - Paginator improvements:
223
+ - templates for pages
224
+ - first/last controls
225
+ - per-page dropdown
226
+ - Grid defaults provider via DI
227
+ - Default value support for cells
228
+
229
+ ### Perf/Opt:
230
+ - Sticky row/column calculations
231
+ - Virtual scrolling performance
232
+ - Resize handling
233
+ - Render / change detection optimizations
234
+
235
+ ### Fix:
236
+ - Sticky column interface (left/right)
237
+ - Type merge issues
238
+ - Header cell template rendering (icons included)
239
+ - Overlay loading mode (spinner) rework
240
+ - Column visibility toggle now switches on first click
241
+
242
+ ### Breaking:
243
+ - `GridColumn.tooltip` is now opt-in popover content (string / fn / TemplateRef), replaces previous implicit tooltip behavior.
244
+ - Column manager introduces new secondary entrypoint `@reforgium/data-grid/column-manager` (no runtime breaking changes, but new public API).
245
+
246
+ ### Docs:
247
+ - updated README (column manager + tooltip)
248
+
249
+ ---
250
+
251
+ ## [1.1.0]: 30.01.2026
252
+
253
+ ### Feat:
254
+ - improved height feature
255
+ - `reDataGridSortIcon`, `reDataGridExpanderIcon`: sort & expander icons ref
256
+ - `reDataGridCell`: data cell ref
257
+ - `DataGridPaginator`: new component
258
+ - `DataGridColumn`: added `typeParams` to typed column's cell
259
+ - `CSS customization`: added expander styling
260
+ - `CSS customization`: added css variables: empty surface
261
+
262
+ ### Fix:
263
+ - code cleanup
264
+ - rewrote sandbox – minimized
265
+ - height setting
266
+ - improved loading and empty state layout
267
+ - updated (corrected) readme
268
+
269
+ ---
270
+
271
+ ## [1.0.3]: 11.01.2026
272
+
273
+ ### Fix:
274
+ - fixed prefixes of directives
275
+
276
+ ### Chore:
277
+ - added more documentation
278
+
279
+ ---
280
+
281
+ ## [1.0.2]: 24.12.2025
282
+
283
+ ### Fix:
284
+ - fixed index forwarding in the template cell
285
+
286
+ ---
287
+
288
+ ## [1.0.1]: 24.12.2025
289
+
290
+ ### Fix:
291
+ - fixed transform types of inputs
292
+
293
+ ---
294
+
295
+ ## [1.0.0]: 24.12.2025
296
+
297
+ ### Feat:
298
+ - added base data grid functionality
299
+ - implemented virtual scrolling for efficient work with large datasets
300
+ - added single-column sorting
301
+ - added pagination and infinite scroll navigation
302
+ - added customizable templates for cells and headers
303
+ - added the ability to pin rows to top and bottom
304
+ - added support for sticky columns on the left and right
305
+ - added text alignment configuration per column
306
+ - added min/max width configuration per column
307
+ - added single and multiple row selection
308
+ - added index column support
309
+ - added customization for empty and loading states
310
+ - added flexible data typing system
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 rtommievich
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -59,6 +59,41 @@ export class SomeComponent {
59
59
  (pageChange)="loadMore($event)"
60
60
  />
61
61
  ```
62
+
63
+ For small and medium lists, `data` remains the simplest contract.
64
+
65
+ For server-paged lists, you can now pass a page-oriented source instead:
66
+
67
+ ```ts
68
+ import type { GridPagedDataSource } from '@reforgium/data-grid';
69
+
70
+ type User = { id: number; name: string };
71
+
72
+ declare const usersSource: GridPagedDataSource<User>;
73
+ ```
74
+
75
+ ```html
76
+ <re-data-grid
77
+ mode="infinity"
78
+ [source]="usersSource"
79
+ [columns]="columns"
80
+ [pageSize]="20"
81
+ />
82
+ ```
83
+
84
+ In `source` mode, the grid keeps its own internal page buffer for infinity scrolling, so the parent does not need to maintain one ever-growing accumulated `data[]`.
85
+
86
+ Important:
87
+
88
+ - The grid does not auto-fetch on mount in `source` mode.
89
+ - The parent should first apply filters / route params / query state and then call the source explicitly (`fetch(...)`, `updatePage(0)`, etc.).
90
+ - After the source is initialized, the grid may call `source.updatePage(...)` for user-driven page changes and infinity buffering.
91
+ - If the source exposes `updatePageSize(...)`, the grid uses it when the page size changes.
92
+ - If the source exposes `sort`, `updateSort(...)`, or `updateSorts(...)`, the grid keeps sort state in sync and can delegate user sorting directly to the source.
93
+ - Infinity buffering uses `prefetchMode: 'sequential'` by default for compatibility with latest-wins sources such as `PagedQueryStore`.
94
+ - Set `prefetchMode: 'parallel'` only for sources that safely support concurrent `updatePage(...)` calls.
95
+
96
+ `PagedQueryStore` from `@reforgium/statum` fits this contract directly, so `[source]="store"` is enough for the common server-table case.
62
97
  ---
63
98
 
64
99
  ## Configuration
@@ -107,6 +142,38 @@ Supported default fields:
107
142
 
108
143
  `translations` supports: `emptyState`, `itemsPerPageLabel`, `nextPageLabel`, `prevPageLabel`, `indexColumnHeader`.
109
144
 
145
+ ### Global header text resolver
146
+
147
+ If your app keeps column headers as i18n keys, you can resolve them globally via DI without wiring `headerTemplate` for every table.
148
+
149
+ - `provideDataGridHeaderTextResolver(...)` registers `(text, ctx) => string | Signal<string>`
150
+ - The resolver applies to plain column headers and header-group titles
151
+ - `headerTemplate` / `titleTemplate` still win for markup, but receive already-resolved text in `$implicit`
152
+ - `provideDataGridHeaderTextResolverWithParent(...)` is available for advanced parent-resolver composition
153
+
154
+ ```ts
155
+ import { inject } from '@angular/core';
156
+ import { provideDataGridHeaderTextResolver } from '@reforgium/data-grid';
157
+ import { LangService } from '@reforgium/presentia';
158
+
159
+ export const appConfig: ApplicationConfig = {
160
+ providers: [
161
+ provideDataGridHeaderTextResolver(() => {
162
+ const lang = inject(LangService);
163
+
164
+ return (text) => (text.includes('.') ? lang.observe(text) : text);
165
+ }),
166
+ ],
167
+ };
168
+ ```
169
+
170
+ ```ts
171
+ columns = [
172
+ { key: 'name', header: 'users.columns.name' },
173
+ { key: 'email', header: 'users.columns.email' },
174
+ ];
175
+ ```
176
+
110
177
  ### Type registries (global + local)
111
178
 
112
179
  You can register type-based transformers and renderers globally via DI.
@@ -200,6 +267,7 @@ columns = [
200
267
  | Parameter | Type | Default | Description | |
201
268
  |--------------------|--------------------------------------------------|--------------------|-------------------------------------------------------------------------------------------------|------------------|
202
269
  | data | `T[]` | `[]` | Data array to render | |
270
+ | source | `GridPagedDataSource<T> \| null` | `null` | Optional page-oriented source for pagination/infinity flows | *[NEW in 3.x]* |
203
271
  | columns | `GridColumn<T>[]` | `[]` | Programmatic column configuration (used when no declarative columns are provided) | |
204
272
  | headerGroups | `GridHeaderGroup<T>[]` | `[]` | Optional top header row groups (`from`..`to`) for 2-level headers | *[NEW in 2.0.0]* |
205
273
  | pinnedRows | `GridPinnedRow<T>[]` | `[]` | Top / bottom pinned rows | |
@@ -230,6 +298,19 @@ Selection mode guidance:
230
298
  - Prefer using row selection with `mode="pagination"` for clearer UX and predictable “select all” behavior.
231
299
  - In `mode="infinity"`, selection typically applies only to currently loaded rows, so use it only when that behavior is acceptable.
232
300
 
301
+ Data source guidance:
302
+
303
+ - Use `data` when the parent already owns the full rendered collection.
304
+ - Use `source` for server-driven pagination or infinity flows where the parent should expose only the current page.
305
+ - In `mode="infinity"` + `source`, the grid uses `totalElements` for scroll height when available and stores loaded page chunks internally instead of forcing the parent to append rows into one large array.
306
+ - `GridPagedDataSource.totalElements` is optional for infinity sources. If omitted, the grid keeps requesting pages until it receives a short page (`items.length < pageSize`) and then treats that page as the end of the dataset.
307
+ - In `source` mode, initialize the source from the parent after all filters / params are ready; the grid does not perform an automatic first fetch on mount.
308
+ - `GridPagedDataSource.error` is optional and can be used by consumers that want to project source-level error UI near the grid.
309
+ - `GridPagedDataSource.sort`, `updateSort(...)`, and `updateSorts(...)` let the grid use source-owned sort state instead of forcing a separate parent bridge.
310
+ - `GridPagedDataSource.prefetchMode` defaults to `sequential`; keep that default for `PagedQueryStore` and similar latest-wins sources.
311
+ - Use `prefetchMode: 'parallel'` only when the source supports multiple concurrent `updatePage(...)` requests without canceling each other.
312
+ - When `source.version` changes or a new source is assigned, the grid invalidates its internal buffer immediately; any in-flight `updatePage()` responses that arrive after invalidation are discarded and do not overwrite the fresh buffer.
313
+
233
314
  Feature lazy-loading behavior (runtime `import()`):
234
315
 
235
316
  - Selection feature is loaded when `selection.mode !== 'none'`.
@@ -281,6 +362,7 @@ Row templates:
281
362
  | rowClick | `GridRowClickEvent<T>` | Emitted when a row is clicked (includes native event) | *[UPD in 2.0.0]* |
282
363
  | rowContext | `GridRowContextEvent<T>` | Emitted on row context menu | *[NEW in 2.0.0]* |
283
364
  | rowDoubleClick | `GridRowDoubleClickEvent<T>` | Emitted when a row is double-clicked | *[NEW in 2.0.0]* |
365
+ | columnResizeEnd | `GridColumnResizeEndEvent<T>` | Emitted when a column resize drag ends (key + px width); use to persist widths | *[NEW in 3.1.0]* |
284
366
  | sortChange | `GridSortEvent<T>` | Emitted when single-sort order changes | |
285
367
  | multiSortChange | `GridMultiSortEvent<T>` | Emitted when multi-sort order changes | *[NEW in 2.2.0]* |
286
368
  | pageChange | `GridPageChangeEvent` | Emitted when requesting data for a new page | |
@@ -289,6 +371,8 @@ Row templates:
289
371
  Notes:
290
372
 
291
373
  - A cell click also triggers the row click event (bubbling), so listen to one or stop propagation if needed.
374
+ - In `source` mode, sort events are still emitted, but if the source implements sort hooks the grid also updates the source directly.
375
+ - `selectChange` is emitted not only on user interaction but also when the grid automatically prunes selected keys after a data update removes rows that were previously selected. Always treat `selectChange` as the authoritative selected state.
292
376
 
293
377
  ### Public API methods
294
378
 
@@ -300,6 +384,28 @@ Notes:
300
384
  - `setSort(event)` - applies single-sort state and emits sort events
301
385
  - `setMultiSort(items)` - applies multi-sort state and emits sort events
302
386
 
387
+ ### GridPagedDataSource<T> reference
388
+
389
+ The optional `source` input accepts a page-oriented contract:
390
+
391
+ | Field / method | Type | Description |
392
+ |---|---|---|
393
+ | `items` | `Signal<T[]>` | Current page items |
394
+ | `loading` | `Signal<boolean>` | Source loading state |
395
+ | `error` | `Signal<unknown \| null>` | Optional source error state |
396
+ | `page` | `number` | Current page index |
397
+ | `pageSize` | `number` | Current page size |
398
+ | `totalElements` | `number \| undefined` | Optional total size |
399
+ | `sort` | `ReadonlyArray<GridSortItem<T>> \| undefined` | Optional source-owned sort state |
400
+ | `version` | `Signal<number> \| undefined` | Dataset reset marker for local buffer invalidation |
401
+ | `updatePage(page)` | `(page: number) => Promise<unknown>` | Load a page |
402
+ | `updatePageSize(size)` | `((size: number) => Promise<unknown>) \| undefined` | Optional page-size handler |
403
+ | `updateSort(sort)` | `((sort?: GridSortItem<T> \| null) => Promise<unknown>) \| undefined` | Optional single-sort handler |
404
+ | `updateSorts(sort)` | `((sort?: ReadonlyArray<GridSortItem<T>> \| null) => Promise<unknown>) \| undefined` | Optional multi-sort handler |
405
+ | `prefetchMode` | `'sequential' \| 'parallel' \| undefined` | Infinity prefetch strategy |
406
+
407
+ This contract is intentionally grid-like, not `statum`-specific, but `PagedQueryStore` already matches it well enough to be used directly.
408
+
303
409
  ### GridColumn<T> reference
304
410
 
305
411
  `columns` accepts `GridColumn<T>[]`, where `GridColumn<T>` is a union of three column variants:
@@ -323,11 +429,11 @@ Common (base) fields:
323
429
 
324
430
  Renderer-specific fields:
325
431
 
326
- | Variant | Fields | Notes |
327
- |-------------------|-----------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------|
432
+ | Variant | Fields | Notes |
433
+ |--------------------------|----------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------|
328
434
  | Built-in / type renderer | `type?: GridBuiltInCellType \| string`, `typeParams?: any`, `defaultValue?: any` | Built-in types are `plain/date/number/index/checkbox`; custom string types can be handled by local/DI type registries. |
329
- | Value renderer | `value: (row: T, ctx) => string \| number`, `track: (row: T) => string` | `track` is required for stable row identity and efficient updates. `ctx` includes `col`, `index`, `isPinned`. |
330
- | Template renderer | `renderTemplate: TemplateRef<...>`, `track: (row: T) => string` | `track` is required when rendering through Angular templates. |
435
+ | Value renderer | `value: (row: T, ctx) => string \| number`, `track: (row: T) => string` | `track` is required for stable row identity and efficient updates. `ctx` includes `col`, `index`, `isPinned`. |
436
+ | Template renderer | `renderTemplate: TemplateRef<...>`, `track: (row: T) => string` | `track` is required when rendering through Angular templates. |
331
437
 
332
438
  Cell text wrapping/clamp behavior:
333
439