@thkl/agrid 0.1.7 → 0.1.10

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
@@ -30,7 +30,12 @@ interface Person {
30
30
 
31
31
  const columns: ColDef<Person>[] = [
32
32
  { field: 'id', header: 'ID', editable: false },
33
- { field: 'name', header: 'Name', filterable: true },
33
+ {
34
+ field: 'name',
35
+ header: 'Name',
36
+ filterable: true,
37
+ infoIcon: ({ row }) => Boolean(row.name),
38
+ },
34
39
  ];
35
40
 
36
41
  @Component({
@@ -55,6 +60,10 @@ export class PeopleComponent {
55
60
  }
56
61
  ```
57
62
 
63
+ Set `infoIcon: true` to show a right-aligned `?` action for every cell in a
64
+ column, or use a predicate to enable it per row. Listen to `(cellInfo)` to
65
+ receive the row, field, value, original row index, and column definition.
66
+
58
67
  Set `group: 'employee'` on adjacent columns to render the `Employee` label above them. Dragging
59
68
  that grouped header moves its current contiguous column segment as one block. Reordering, hiding,
60
69
  or pinning may split one group ID into multiple headers. Segments containing locked columns cannot
@@ -71,6 +80,59 @@ Marking is independent from row selection. Cell and range copy use the same copi
71
80
  every marked row, while Copy row uses every visible column. Duplicate rows are omitted, and marked
72
81
  rows remain included when filters hide them.
73
82
 
83
+ ## Menu bar
84
+
85
+ Set `menuBarItems` to render command buttons above the headers. An item with `items` becomes a
86
+ split button: its label emits the parent id and its chevron opens additional commands. Every
87
+ command emits through the single `(menuBarAction)` output.
88
+
89
+ ```ts
90
+ readonly provider = new AgridProvider<Person>({
91
+ columns,
92
+ datasource,
93
+ menuBarItems: [
94
+ { id: 'refresh', label: 'Refresh', icon: '↻' },
95
+ {
96
+ id: 'selection',
97
+ label: 'Selection',
98
+ disabled: ({ selectedRows }) => selectedRows.length === 0,
99
+ items: [
100
+ { id: 'activate', label: 'Activate' },
101
+ { id: 'deactivate', label: 'Deactivate', active: ({ rows }) => rows.some(row => !row.active) },
102
+ ],
103
+ },
104
+ ],
105
+ });
106
+ ```
107
+
108
+ ```html
109
+ <agrid [provider]="provider" (menuBarAction)="runCommand($event)" />
110
+ ```
111
+
112
+ `visible`, `active`, and `disabled` may be booleans or callbacks. Callback context includes
113
+ `rows`, `selectedRows`, `selectedCell`, `provider`, and `datasource`.
114
+
115
+ ## Input masks
116
+
117
+ Use `inputMask` to select a string mask for each row and cell. The callback
118
+ receives `{ row, value, column }`; return `null` for cells that should remain
119
+ unrestricted.
120
+
121
+ ```ts
122
+ {
123
+ field: 'reference',
124
+ header: 'Reference',
125
+ inputMask: ({ row }) =>
126
+ row.numeric
127
+ ? /\d{0,3}(?:-\d{0,5}(?:-\d{0,5})?)?/
128
+ : /[a-z0-9]{0,3}(?: [a-z0-9]{0,3}(?: [a-z0-9]{0,5})?)?/i,
129
+ }
130
+ ```
131
+
132
+ The regex is matched against the complete proposed value and must allow
133
+ partial input. Separators are typed explicitly rather than inserted
134
+ automatically. Invalid edits revert to the last accepted value.
135
+
74
136
  ## Boolean columns
75
137
 
76
138
  Set `type: 'boolean'` to render a cell as an inline checkbox. Clicking it toggles the value and
@@ -85,22 +147,41 @@ const columns: ColDef<Person>[] = [
85
147
  ];
86
148
  ```
87
149
 
88
- ## Typed filters
150
+ ## Runtime readonly cells
89
151
 
90
- Mark a `number` or `date` column `filterable: true` and its column menu gains a **condition**
91
- filter in addition to the value picker. Numbers offer `=`, `≠`, `>`, `≥`, `<`, `≤`, and `between`;
92
- dates offer on / before / after / between. The condition combines with the value picker and other
93
- columns using AND semantics, and is included in `AgridControl.toJSON()` state.
152
+ Use `cellReadonly` when editability depends on the current row. Returning `true` blocks inline
153
+ editing, boolean toggles, paste, fill, and sidebar edits for that cell.
94
154
 
95
155
  ```ts
96
156
  const columns: ColDef<Order>[] = [
157
+ { field: 'status', header: 'Status' },
158
+ {
159
+ field: 'approvedBy',
160
+ header: 'Approved by',
161
+ cellReadonly: ({ row }) => row.status !== 'Draft',
162
+ },
163
+ ];
164
+ ```
165
+
166
+ ## Condition filters
167
+
168
+ Mark a column `filterable: true` and its column menu gains a **condition** filter in addition to
169
+ the value picker. Text columns offer equals, not equal, like, starts with, ends with, includes, and
170
+ does not include. Numbers offer `=`, `≠`, `>`, `≥`, `<`, `≤`, and `between`; dates offer on /
171
+ before / after / between. Conditions combine with the header text filter, value picker, and other
172
+ columns using AND semantics, and are included in `AgridControl.toJSON()` state.
173
+
174
+ ```ts
175
+ const columns: ColDef<Order>[] = [
176
+ { field: 'reference', header: 'Reference', filterable: true },
97
177
  { field: 'total', header: 'Total', type: 'number', filterable: true },
98
178
  { field: 'placedAt', header: 'Placed', type: 'date', filterable: true },
99
179
  ];
100
180
  ```
101
181
 
102
182
  Set it programmatically with `control.setRangeFilter(field, operator, operand, operand2?)`, where
103
- `operator` is one of `'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'between'` (pass `null` to clear).
183
+ `operator` can also be `'like' | 'startsWith' | 'endsWith' | 'includes' | 'notIncludes'` for text
184
+ columns (pass `null` to clear). `like` uses `%` for any sequence and `_` for one character.
104
185
 
105
186
  ## Edit validation
106
187
 
@@ -143,7 +224,7 @@ cleared by `control.clearAllFilters()`.
143
224
  With `serverSideFiltering: true` the grid never filters locally — it emits events so the host can
144
225
  refetch:
145
226
 
146
- - `(filterChange)` — text filters emit `{ field, value }`; typed range conditions emit
227
+ - `(filterChange)` — header text filters emit `{ field, value }`; menu conditions emit
147
228
  `{ field, value: '', operator, operand, operand2 }` (operator `null` clears the condition).
148
229
  - `(sortChange)` — `{ field, direction }`.
149
230
  - `(quickFilterChange)` — the quick-filter text (debounced by `filterDebounceMs`).
@@ -179,9 +260,8 @@ one aggregated column are both active.
179
260
 
180
261
  ## Tree data
181
262
 
182
- Pass `treeConfig` to render rows as a hierarchical tree. The hierarchy lives on the flat row
183
- array via stable `id` / `parentId` accessors, so there are no nested `children` arrays and
184
- selection and editing keep working on the same indices.
263
+ Pass `treeConfig` to render rows as a hierarchical tree. Use stable `id` / `parentId` accessors
264
+ for an existing hierarchy, or `getPath` to generate display-only branches from each row.
185
265
 
186
266
  ```ts
187
267
  import { AgridTreeConfig } from '@thkl/agrid';
@@ -199,12 +279,90 @@ readonly provider = new AgridProvider<OrgRow>({
199
279
  });
200
280
  ```
201
281
 
282
+ For path-like values such as `01.01.0001`, return the ordered segments:
283
+
284
+ ```ts
285
+ const treeConfig: AgridTreeConfig<Row> = {
286
+ getPath: row => row.oz.split('.'),
287
+ nodeUuid: row => row.uuid,
288
+ formatPathSegment: ({ row, segment, level, leaf }) =>
289
+ leaf
290
+ ? `${segment} ${row.description}`
291
+ : `${segment} ${level === 0 ? row.areaLabel : row.groupLabel}`,
292
+ treeField: 'oz',
293
+ };
294
+ ```
295
+
296
+ This renders `01 / 01 / 0001, 0002`. Generated `01` branch rows are display-only; the leaf remains
297
+ the original datasource row. Its tree cell displays `0001`, while editing still uses the complete
298
+ `01.01.0001` value. `formatPathSegment` changes labels only; raw segments still control grouping,
299
+ expansion identity, and sort order. The callback receives the original `row`; shared branch nodes
300
+ use the first row encountered for that raw path prefix. `nodeUuid` uses the same source row and is
301
+ included in generated branch-node click events.
302
+
202
303
  The `treeField` column shows an indented expand/collapse twisty. Filtering and sorting behave as
203
304
  in a flat grid; with `keepAncestorsOnFilter` (default `true`) a match deep in the tree keeps its
204
305
  parents visible and force-opens the path to it. Tree mode takes precedence over grouping and
205
306
  disables pagination. Call `grid.expandAllNodes()` / `grid.collapseAllNodes()` to toggle the whole
206
307
  tree.
207
308
 
309
+ Tree mode can be combined with `masterDetail: true` and a `detailRenderer`. Detail expanders are
310
+ shown only for leaf rows; parent rows retain their tree expand/collapse control.
311
+
312
+ ## Standalone tree control
313
+
314
+ 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.
316
+
317
+ ```ts
318
+ readonly treeProvider = new AgridTreeProvider<OrgRow>({
319
+ datasource: new AgridDataSource(rows),
320
+ treeConfig: {
321
+ getId: row => row.id,
322
+ getParentId: row => row.parentId,
323
+ treeField: 'name',
324
+ defaultExpanded: true,
325
+ },
326
+ getDescription: row => row.role,
327
+ selection: 'single',
328
+ ariaLabel: 'Organization',
329
+ });
330
+ ```
331
+
332
+ ```html
333
+ <agrid-tree [provider]="treeProvider"
334
+ (nodeClick)="openNode($event)"
335
+ (nodeDoubleClicked)="openNode($event)"
336
+ (selectionChange)="selectionChanged($event)" />
337
+ ```
338
+
339
+ Row and generated path-branch clicks emit `AgridTreeNodeEvent<T>`. Branch events include their
340
+ configured or generated `uuid`. `expandAllNodes()` and `collapseAllNodes()` are public methods.
341
+ The tree uses the shared `--agrid-color-text`, `--agrid-color-text-muted`,
342
+ `--agrid-color-accent`, `--agrid-color-accent-subtle`, `--agrid-color-accent-fg`,
343
+ `--agrid-color-border`, `--agrid-color-bg`, and `--agrid-color-bg-muted` CSS variables.
344
+
345
+ ## Page selector
346
+
347
+ Use `<agrid-page-selector>` to navigate pages by previous/next button, dropdown, or typed ID.
348
+ All three interactions emit an `AgridPageItem<TId>` through the single `(selectPage)` output.
349
+
350
+ ```ts
351
+ readonly pages: AgridPageItem<number>[] = [
352
+ { id: 1, label: 'Cover' },
353
+ { id: 2, label: 'Measurements' },
354
+ { id: 3, label: 'Summary' },
355
+ ];
356
+ ```
357
+
358
+ ```html
359
+ <agrid-page-selector [items]="pages" [selectedId]="currentPageId()"
360
+ (selectPage)="currentPageId.set($event.id)" />
361
+ ```
362
+
363
+ IDs can be strings or numbers. Typed IDs are selected with Enter. Available inputs are
364
+ `disabled`, `previousLabel`, `nextLabel`, `inputLabel`, `menuLabel`, and `emptyText`.
365
+
208
366
  ## Saving edited rows
209
367
 
210
368
  Use `rowChanged` to send one request after the user edits one or more fields in a row: