@thkl/agrid 0.1.10 → 0.1.12

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
@@ -72,14 +72,69 @@ be dragged.
72
72
  `confirmRowDelete` protects grid delete actions with a localized in-row Yes/No confirmation.
73
73
  Direct calls to `AgridDataSource.removeRow()` remain immediate.
74
74
 
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
75
+ `enableRowMarking` makes each control-cell row header clickable and adds a checkbox. Clicking the
76
+ header outside its nested controls toggles the same mark state and emits `(rowMark)` with the row,
77
+ its original datasource index, and the resulting `marked` state. Marked rows are included in
78
+ keyboard, cell-context, and row-context copy operations. Read `grid.markedRowIndices()` or call
77
79
  `grid.clearMarkedRows()` when the host needs to inspect or reset the copy basket.
78
80
 
79
81
  Marking is independent from row selection. Cell and range copy use the same copied columns for
80
82
  every marked row, while Copy row uses every visible column. Duplicate rows are omitted, and marked
81
83
  rows remain included when filters hide them.
82
84
 
85
+ Selecting numeric cells displays a live status bar with count, sum, average, minimum, and maximum.
86
+ Read the same values from `grid.selectionSummary()`. The signal is `null` when the active selection
87
+ contains no finite numeric values.
88
+
89
+ Set `enableColumnMarking: true` to toggle complete-column highlighting by clicking a header outside
90
+ its menu and resize controls. The grid exposes `markedColumnFields()` and emits `(columnMark)`.
91
+
92
+ Append custom commands to one column's menu and handle them through one typed output:
93
+
94
+ ```ts
95
+ { field: 'status', header: 'Status', headerMenuItems: [
96
+ { key: 'archive', label: 'Archive', icon: '⌂' },
97
+ ] }
98
+ ```
99
+
100
+ ```html
101
+ <agrid [provider]="provider" (columnHeaderAction)="onColumnHeaderAction($event)" />
102
+ ```
103
+
104
+ The event contains `{ column, key }`.
105
+
106
+ Use `(firstDataRendered)` when host logic must wait until the grid has completed its first render
107
+ containing datasource rows:
108
+
109
+ ```html
110
+ <agrid [provider]="provider" (firstDataRendered)="onGridReady($event)" />
111
+ ```
112
+
113
+ The event fires once per grid component. An initially empty datasource delays it until rows arrive.
114
+ Server-side loading placeholders do not count as rendered data. The payload contains `rows`,
115
+ `rowCount`, `provider`, and `datasource`.
116
+
117
+ For commands that change `textAlign` at runtime, keep the writable signal in the host instead of
118
+ mutating `event.column`:
119
+
120
+ ```ts
121
+ readonly salaryAlignment = signal<'left' | 'center' | 'right'>('right');
122
+
123
+ // Column definition
124
+ { field: 'salary', header: 'Salary', textAlign: this.salaryAlignment }
125
+
126
+ onColumnHeaderAction(event: ColumnHeaderActionEvent<Employee>): void {
127
+ if (event.column.field !== 'salary') return;
128
+ if (event.key === 'align-left') this.salaryAlignment.set('left');
129
+ if (event.key === 'align-center') this.salaryAlignment.set('center');
130
+ if (event.key === 'align-right') this.salaryAlignment.set('right');
131
+ }
132
+ ```
133
+
134
+ Assigning `event.column.textAlign = 'center'` mutates plain configuration and does not trigger an
135
+ Angular update. Calling `.set()` on `event.column.textAlign` is also not type-safe because the
136
+ property may contain a static string or a read-only signal.
137
+
83
138
  ## Menu bar
84
139
 
85
140
  Set `menuBarItems` to render command buttons above the headers. An item with `items` becomes a
@@ -171,6 +226,10 @@ does not include. Numbers offer `=`, `≠`, `>`, `≥`, `<`, `≤`, and `between
171
226
  before / after / between. Conditions combine with the header text filter, value picker, and other
172
227
  columns using AND semantics, and are included in `AgridControl.toJSON()` state.
173
228
 
229
+ The header arrow opens the complete column menu. The condition button beside the inline filter
230
+ opens only the condition operator and operand controls; sorting, layout, grouping, custom commands,
231
+ clear-all actions, and distinct-value selection remain in the complete menu.
232
+
174
233
  ```ts
175
234
  const columns: ColDef<Order>[] = [
176
235
  { field: 'reference', header: 'Reference', filterable: true },
@@ -237,7 +296,8 @@ refetch:
237
296
  ```
238
297
 
239
298
  Text/range/quick events are debounced by `filterDebounceMs` (default 300 ms; `0` disables). The
240
- Excel-style value picker stays client-only and is hidden in this mode.
299
+ The distinct-value picker is hidden in this mode unless the column supplies an explicit `values`
300
+ list representing the complete server-side value set.
241
301
 
242
302
  ## Grouping and aggregates
243
303
 
@@ -270,6 +330,7 @@ const treeConfig: AgridTreeConfig<OrgRow> = {
270
330
  getId: row => row.id,
271
331
  getParentId: row => row.parentId, // null / unknown id ⇒ root row
272
332
  treeField: 'name', // column that shows the twisty
333
+ aggregateTreeNodes: true, // parent cells roll up descendant leaves
273
334
  };
274
335
 
275
336
  readonly provider = new AgridProvider<OrgRow>({
@@ -279,6 +340,11 @@ readonly provider = new AgridProvider<OrgRow>({
279
340
  });
280
341
  ```
281
342
 
343
+ With `aggregateTreeNodes: true`, every expandable row displays descendant-leaf rollups in columns
344
+ that define `aggregate` (or have one set through `AgridControl`). For example,
345
+ `{ field: 'amount', aggregate: 'sum' }` shows the sum of a node's leaves in that node's amount cell.
346
+ Collapsed leaves still contribute. Filtering recalculates the rollups over the matching tree.
347
+
282
348
  For path-like values such as `01.01.0001`, return the ordered segments:
283
349
 
284
350
  ```ts
@@ -309,10 +375,19 @@ tree.
309
375
  Tree mode can be combined with `masterDetail: true` and a `detailRenderer`. Detail expanders are
310
376
  shown only for leaf rows; parent rows retain their tree expand/collapse control.
311
377
 
378
+ Set `detailColumnField` to a column field when the panel should expose one larger multiline value.
379
+ The formatted value is shown normally; click it or focus it and press Enter to open a textarea.
380
+ Blur or Ctrl/Cmd+Enter commits, while Escape cancels. Editability, validation, history, and edit
381
+ events follow the linked column's normal cell behavior.
382
+ Set `detailActions` to add text-template buttons above the textarea. Each action has an `id`,
383
+ `label`, and optional `text` string or row-aware resolver; clicking inserts at the current
384
+ selection, or appends if the button opens the textarea.
385
+
312
386
  ## Standalone tree control
313
387
 
314
388
  Use `<agrid-tree>` for the same parent-ID or path hierarchy without grid columns. It adds tree
315
- keyboard navigation and optional single or multi-selection.
389
+ keyboard navigation and optional single or multi-selection. Since this component has no columns,
390
+ it ignores `aggregateTreeNodes`; descendant rollups apply only to tree mode in `<agrid>`.
316
391
 
317
392
  ```ts
318
393
  readonly treeProvider = new AgridTreeProvider<OrgRow>({
@@ -342,6 +417,77 @@ The tree uses the shared `--agrid-color-text`, `--agrid-color-text-muted`,
342
417
  `--agrid-color-accent`, `--agrid-color-accent-subtle`, `--agrid-color-accent-fg`,
343
418
  `--agrid-color-border`, `--agrid-color-bg`, and `--agrid-color-bg-muted` CSS variables.
344
419
 
420
+ ## Pivot tables
421
+
422
+ Use `pivotConfig` to derive a read-only client-side cross-tabulation from a flat datasource. The
423
+ first pivot release supports one row dimension, one column dimension, and one value field.
424
+
425
+ ```ts
426
+ const provider = new AgridProvider<Sale>({
427
+ columns: [
428
+ { field: 'region', header: 'Region' },
429
+ { field: 'quarter', header: 'Quarter' },
430
+ { field: 'revenue', header: 'Revenue', type: 'number', formatter: currency },
431
+ ],
432
+ datasource: new AgridDataSource(sales),
433
+ pivotConfig: {
434
+ rowField: 'region',
435
+ columnField: 'quarter',
436
+ valueField: 'revenue',
437
+ aggregate: 'sum',
438
+ },
439
+ });
440
+ ```
441
+
442
+ Each distinct `rowField` value becomes one row and each distinct `columnField` value becomes a
443
+ generated column. Intersections use `sum` by default and also support `avg`, `min`, `max`, `count`,
444
+ or a custom `(values) => result` function. Dimension labels and pivot values reuse source column
445
+ formatters. Missing intersections render empty.
446
+
447
+ Pivot rows react to datasource replacement and updates. They are derived values, so editing,
448
+ adding, reordering, master/detail, tree mode, server-side row models, and aggregate footers are
449
+ disabled or rejected in pivot mode. Client-side filtering, sorting, selection, and pagination
450
+ continue through the normal grid pipeline. Multi-level dimensions and totals are intentionally
451
+ outside this first slice.
452
+
453
+ When `showSidebar` is enabled, pivot grids add a **Pivot** sidebar tab. It updates `rowField`,
454
+ `columnField`, `valueField`, and built-in aggregate functions immediately without replacing the
455
+ provider. Assigning `provider.pivotConfig` programmatically uses the same reactive path.
456
+
457
+ ### Saving and restoring settings
458
+
459
+ `saveSettings()` returns a detached, versioned object containing the pivot configuration and the
460
+ existing control state (column visibility, width, order, pinning, filters, sorts, pagination, and
461
+ aggregates). It is safe to JSON-encode and send to a backend. `loadSettings()` applies it to an
462
+ already-rendered grid immediately.
463
+
464
+ ```ts
465
+ const settings = provider.saveSettings();
466
+ await api.saveGridSettings(settings);
467
+
468
+ const restored = await api.loadGridSettings();
469
+ provider.loadSettings(restored);
470
+ ```
471
+
472
+ The component exposes the same methods through `viewChild(AgridComponent)`. Sidebar pivot and
473
+ column-visibility changes emit the complete snapshot through `(settingsChange)`:
474
+
475
+ ```html
476
+ <agrid #grid [provider]="provider" (settingsChange)="saveSettings($event)" />
477
+ ```
478
+
479
+ ```ts
480
+ saveSettings(settings: AgridSettings): void {
481
+ void api.saveGridSettings(settings);
482
+ }
483
+
484
+ // Loading after the grid exists:
485
+ grid().loadSettings(savedSettings);
486
+ ```
487
+
488
+ Custom aggregate functions are executable code and cannot be represented in JSON; attempting to
489
+ save one throws an explicit error. Register custom functions in application code instead.
490
+
345
491
  ## Page selector
346
492
 
347
493
  Use `<agrid-page-selector>` to navigate pages by previous/next button, dropdown, or typed ID.
@@ -374,7 +520,8 @@ Use `rowChanged` to send one request after the user edits one or more fields in
374
520
  ```ts
375
521
  saveRow(event: RowUpdateEvent<Person>): void {
376
522
  this.http.patch(`/api/people/${event.row.id}`, event.row).subscribe(() => {
377
- this.grid()?.clearChangedCells(event.originalIndex);
523
+ this.provider.control.indicate(event.originalIndex, '#2da44e', 1000);
524
+ this.provider.control.clearChangedCells(event.originalIndex);
378
525
  });
379
526
  }
380
527
  ```
@@ -382,6 +529,8 @@ saveRow(event: RowUpdateEvent<Person>): void {
382
529
  The event fires with the latest complete row when inline navigation leaves that row, or when the
383
530
  sidebar editor Save button is used. `cellEdit` and `recordEdit` remain available for every committed
384
531
  field change. With `showChangedCellIndicator: true`, changed cells keep a corner marker until the
385
- PATCH succeeds. Override `--agrid-color-cell-changed` to customize its color.
532
+ PATCH succeeds. Override `--agrid-color-cell-changed` to customize its color. Call
533
+ `control.indicate(index, color, durationMs)` to flash a complete row for transient server-side
534
+ feedback.
386
535
 
387
536
  Full documentation and demos: https://thkl.github.io/agrid/