@thkl/agrid 0.1.11 → 0.1.14

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/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # @thkl/agrid
2
2
 
3
- A signal-based, standalone data grid for Angular 21 with virtual scrolling, editing,
3
+ A signal-based, standalone data grid for Angular 21 and 22 with virtual scrolling, editing,
4
4
  filtering, sorting, grouping, tree data, pinned columns, selection, clipboard operations,
5
- and pagination.
5
+ pagination, and linked SVG charts/graphs.
6
6
 
7
7
  ## Install
8
8
 
@@ -10,6 +10,16 @@ and pagination.
10
10
  npm install @thkl/agrid @angular/cdk
11
11
  ```
12
12
 
13
+ ## Feature Highlights
14
+
15
+ - Virtual rows and virtual columns for large and wide datasets.
16
+ - Text, value, quick, and condition filters with local or server-side workflows.
17
+ - Inline editing, validation, undo/redo, paste, fill, custom editors, and custom renderers.
18
+ - Range selection, selection summaries, row selection, row marking, column marking, and row numbers.
19
+ - Grouping, aggregate footers, tree data with descendant rollups, pivot tables, and master/detail rows.
20
+ - Pinned columns, pinned rows, column reordering, column autosize, and persistable settings.
21
+ - CSV, zero-dependency `.xlsx` export, sparklines, and linked SVG charts/graphs.
22
+
13
23
  ## Usage
14
24
 
15
25
  ```ts
@@ -72,14 +82,119 @@ be dragged.
72
82
  `confirmRowDelete` protects grid delete actions with a localized in-row Yes/No confirmation.
73
83
  Direct calls to `AgridDataSource.removeRow()` remain immediate.
74
84
 
75
- `enableRowMarking` adds a checkbox to each control cell. Marked rows are included in keyboard,
76
- cell-context, and row-context copy operations. Read `grid.markedRowIndices()` or call
85
+ `showRowNumbers` displays 1-based row numbers in the control column for the current filtered and
86
+ sorted row order. When row reordering is enabled, the number replaces the drag-handle glyph while
87
+ the control cell remains draggable.
88
+
89
+ `enableRowMarking` makes each control-cell row header clickable and adds a checkbox. Clicking the
90
+ header outside its nested controls toggles the same mark state and emits `(rowMark)` with the row,
91
+ its original datasource index, and the resulting `marked` state. Marked rows are included in
92
+ keyboard, cell-context, and row-context copy operations. Read `grid.markedRowIndices()` or call
77
93
  `grid.clearMarkedRows()` when the host needs to inspect or reset the copy basket.
78
94
 
79
95
  Marking is independent from row selection. Cell and range copy use the same copied columns for
80
96
  every marked row, while Copy row uses every visible column. Duplicate rows are omitted, and marked
81
97
  rows remain included when filters hide them.
82
98
 
99
+ Selecting numeric cells displays a live status bar with count, sum, average, minimum, and maximum.
100
+ Read the same values from `grid.selectionSummary()`. The signal is `null` when the active selection
101
+ contains no finite numeric values.
102
+
103
+ ## Charts / Graphs
104
+
105
+ Use `AgridChartComponent` with an `AgridChartProvider` to render zero-dependency SVG graphs. The
106
+ same provider pattern as the grid keeps setup predictable:
107
+
108
+ ```ts
109
+ import { AgridChartComponent, AgridChartProvider } from '@thkl/agrid';
110
+
111
+ readonly chartProvider = new AgridChartProvider({
112
+ type: 'column',
113
+ data: {
114
+ categories: ['Q1', 'Q2', 'Q3', 'Q4'],
115
+ series: [
116
+ { name: 'North', values: [120, 145, 138, 162] },
117
+ { name: 'South', values: [98, 110, 134, 128] },
118
+ ],
119
+ },
120
+ height: 300,
121
+ });
122
+ ```
123
+
124
+ ```html
125
+ <agrid-chart [provider]="chartProvider" />
126
+ ```
127
+
128
+ Supported `type` values are `column`, `bar`, `line`, `area`, `pie`, and `donut`. `type`, `height`,
129
+ `showLegend`, `showAxis`, and `palette` are signals, so runtime changes redraw immediately.
130
+
131
+ To link a graph to a grid, pass `provider.visibleRows` as `source` and provide a `transform`. The
132
+ chart follows filtering, sorting, and edits live:
133
+
134
+ ```ts
135
+ readonly chartProvider = new AgridChartProvider<Person>({
136
+ type: 'bar',
137
+ source: this.provider.visibleRows,
138
+ transform: rows => ({
139
+ categories: rows.map(row => row.name),
140
+ series: [{ name: 'Score', values: rows.map(row => Number(row.score ?? 0)) }],
141
+ }),
142
+ });
143
+ ```
144
+
145
+ `visibleRows` is the filtered and sorted row set. It intentionally ignores grouping and pagination;
146
+ aggregate inside `transform` when the graph should show grouped totals or page-specific summaries.
147
+ Pie and donut graphs use the first series and label slices from `categories`.
148
+
149
+ Set `enableColumnMarking: true` to toggle complete-column highlighting by clicking a header outside
150
+ its menu and resize controls. The grid exposes `markedColumnFields()` and emits `(columnMark)`.
151
+
152
+ Append custom commands to one column's menu and handle them through one typed output:
153
+
154
+ ```ts
155
+ { field: 'status', header: 'Status', headerMenuItems: [
156
+ { key: 'archive', label: 'Archive', icon: '⌂' },
157
+ ] }
158
+ ```
159
+
160
+ ```html
161
+ <agrid [provider]="provider" (columnHeaderAction)="onColumnHeaderAction($event)" />
162
+ ```
163
+
164
+ The event contains `{ column, key }`.
165
+
166
+ Use `(firstDataRendered)` when host logic must wait until the grid has completed its first render
167
+ containing datasource rows:
168
+
169
+ ```html
170
+ <agrid [provider]="provider" (firstDataRendered)="onGridReady($event)" />
171
+ ```
172
+
173
+ The event fires once per grid component. An initially empty datasource delays it until rows arrive.
174
+ Server-side loading placeholders do not count as rendered data. The payload contains `rows`,
175
+ `rowCount`, `provider`, and `datasource`.
176
+
177
+ For commands that change `textAlign` at runtime, keep the writable signal in the host instead of
178
+ mutating `event.column`:
179
+
180
+ ```ts
181
+ readonly salaryAlignment = signal<'left' | 'center' | 'right'>('right');
182
+
183
+ // Column definition
184
+ { field: 'salary', header: 'Salary', textAlign: this.salaryAlignment }
185
+
186
+ onColumnHeaderAction(event: ColumnHeaderActionEvent<Employee>): void {
187
+ if (event.column.field !== 'salary') return;
188
+ if (event.key === 'align-left') this.salaryAlignment.set('left');
189
+ if (event.key === 'align-center') this.salaryAlignment.set('center');
190
+ if (event.key === 'align-right') this.salaryAlignment.set('right');
191
+ }
192
+ ```
193
+
194
+ Assigning `event.column.textAlign = 'center'` mutates plain configuration and does not trigger an
195
+ Angular update. Calling `.set()` on `event.column.textAlign` is also not type-safe because the
196
+ property may contain a static string or a read-only signal.
197
+
83
198
  ## Menu bar
84
199
 
85
200
  Set `menuBarItems` to render command buttons above the headers. An item with `items` becomes a
@@ -112,6 +227,9 @@ readonly provider = new AgridProvider<Person>({
112
227
  `visible`, `active`, and `disabled` may be booleans or callbacks. Callback context includes
113
228
  `rows`, `selectedRows`, `selectedCell`, `provider`, and `datasource`.
114
229
 
230
+ For imperative selection reads, call `grid.getCurrentRow()` or `grid.getCurrentCell()`. The
231
+ `(cellSelect)` output emits the same selected-cell shape and `null` when cell selection clears.
232
+
115
233
  ## Input masks
116
234
 
117
235
  Use `inputMask` to select a string mask for each row and cell. The callback
@@ -171,6 +289,10 @@ does not include. Numbers offer `=`, `≠`, `>`, `≥`, `<`, `≤`, and `between
171
289
  before / after / between. Conditions combine with the header text filter, value picker, and other
172
290
  columns using AND semantics, and are included in `AgridControl.toJSON()` state.
173
291
 
292
+ The header arrow opens the complete column menu. The condition button beside the inline filter
293
+ opens only the condition operator and operand controls; sorting, layout, grouping, custom commands,
294
+ clear-all actions, and distinct-value selection remain in the complete menu.
295
+
174
296
  ```ts
175
297
  const columns: ColDef<Order>[] = [
176
298
  { field: 'reference', header: 'Reference', filterable: true },
@@ -219,15 +341,40 @@ readonly provider = new AgridProvider<Person>({ columns, datasource, enableQuick
219
341
  Drive it programmatically with `control.setQuickFilter(text)`; it's part of `toJSON()` state and is
220
342
  cleared by `control.clearAllFilters()`.
221
343
 
344
+ Use `control.getFilterModel()` and `control.setFilterModel(model)` to persist or restore just the
345
+ filter, quick-filter, and sort state without the rest of the grid settings.
346
+
347
+ Rows inserted while filters or sorts are active stay visible in insertion order even if their values
348
+ do not currently match. Wire `control.reapplyFilters()` to a button or save action when those
349
+ inserted rows should be filtered and sorted like the rest of the datasource again. Use
350
+ `control.filterReapplyNeeded()` to show visual feedback when that action is available.
351
+
222
352
  ## Server-side filtering
223
353
 
224
- With `serverSideFiltering: true` the grid never filters locally it emits events so the host can
225
- refetch:
354
+ With `serverSideFiltering: true` the grid never filters locally. The recommended integration point
355
+ is the complete query snapshot:
356
+
357
+ ```html
358
+ <agrid [provider]="provider" (serverQueryChange)="loadRows($event)" />
359
+ ```
360
+
361
+ ```ts
362
+ effect(() => {
363
+ const query = provider.serverQuery();
364
+ if (!query) return;
365
+ store.load(query);
366
+ });
367
+ ```
368
+
369
+ `AgridServerQuery` contains column filters, value selections, menu conditions, ordered sorts,
370
+ quick-filter text, and page range (`startRow..endRow`, inclusive). The older granular outputs remain
371
+ available for compatibility:
226
372
 
227
- - `(filterChange)` — header text filters emit `{ field, value }`; menu conditions emit
373
+ - `(filterChange)` — header text filters emit `{ field, value }`; value filters emit
374
+ `{ field, value: '', selectedValues }`; menu conditions emit
228
375
  `{ field, value: '', operator, operand, operand2 }` (operator `null` clears the condition).
229
376
  - `(sortChange)` — `{ field, direction }`.
230
- - `(quickFilterChange)` — the quick-filter text (debounced by `filterDebounceMs`).
377
+ - `(quickFilterChange)` — the quick-filter text.
231
378
 
232
379
  ```html
233
380
  <agrid [provider]="provider"
@@ -237,7 +384,7 @@ refetch:
237
384
  ```
238
385
 
239
386
  Text/range/quick events are debounced by `filterDebounceMs` (default 300 ms; `0` disables). The
240
- The distinct-value picker is hidden in this mode unless the column supplies an explicit `values`
387
+ distinct-value picker is hidden in this mode unless the column supplies an explicit `values`
241
388
  list representing the complete server-side value set.
242
389
 
243
390
  ## Grouping and aggregates
@@ -316,6 +463,14 @@ tree.
316
463
  Tree mode can be combined with `masterDetail: true` and a `detailRenderer`. Detail expanders are
317
464
  shown only for leaf rows; parent rows retain their tree expand/collapse control.
318
465
 
466
+ Set `detailColumnField` to a column field when the panel should expose one larger multiline value.
467
+ The formatted value is shown normally; click it or focus it and press Enter to open a textarea.
468
+ Blur or Ctrl/Cmd+Enter commits, while Escape cancels. Editability, validation, history, and edit
469
+ events follow the linked column's normal cell behavior.
470
+ Set `detailActions` to add text-template buttons above the textarea. Each action has an `id`,
471
+ `label`, and optional `text` string or row-aware resolver; clicking inserts at the current
472
+ selection, or appends if the button opens the textarea.
473
+
319
474
  ## Standalone tree control
320
475
 
321
476
  Use `<agrid-tree>` for the same parent-ID or path hierarchy without grid columns. It adds tree
@@ -453,7 +608,8 @@ Use `rowChanged` to send one request after the user edits one or more fields in
453
608
  ```ts
454
609
  saveRow(event: RowUpdateEvent<Person>): void {
455
610
  this.http.patch(`/api/people/${event.row.id}`, event.row).subscribe(() => {
456
- this.grid()?.clearChangedCells(event.originalIndex);
611
+ this.provider.control.indicate(event.originalIndex, '#2da44e', 1000);
612
+ this.provider.control.clearChangedCells(event.originalIndex);
457
613
  });
458
614
  }
459
615
  ```
@@ -461,6 +617,8 @@ saveRow(event: RowUpdateEvent<Person>): void {
461
617
  The event fires with the latest complete row when inline navigation leaves that row, or when the
462
618
  sidebar editor Save button is used. `cellEdit` and `recordEdit` remain available for every committed
463
619
  field change. With `showChangedCellIndicator: true`, changed cells keep a corner marker until the
464
- PATCH succeeds. Override `--agrid-color-cell-changed` to customize its color.
620
+ PATCH succeeds. Override `--agrid-color-cell-changed` to customize its color. Call
621
+ `control.indicate(index, color, durationMs)` to flash a complete row for transient server-side
622
+ feedback.
465
623
 
466
624
  Full documentation and demos: https://thkl.github.io/agrid/