@guiexpert/table 1.1.93 → 1.1.94
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 +13 -0
- package/css/main.css +142 -0
- package/css/table-classes.css +34 -0
- package/css/table-color-vars.css +32 -0
- package/css/table-pivot.css +42 -0
- package/css/table-row-group.css +25 -0
- package/css/table-tree.css +9 -0
- package/index.cjs +44 -36
- package/index.d.ts +39 -0
- package/index.js +6247 -3509
- package/lib/table/aggregation/aggregate-internals.d.ts +30 -0
- package/lib/table/aggregation/aggregate-spec.if.d.ts +51 -0
- package/lib/table/aggregation/aggregate.d.ts +58 -0
- package/lib/table/aggregation/aggregator-name.type.d.ts +39 -0
- package/lib/table/aggregation/aggregator.if.d.ts +51 -0
- package/lib/table/aggregation/aggregators.d.ts +37 -0
- package/lib/table/aggregation/create-value-getter.d.ts +18 -0
- package/lib/table/aggregation/selection-aggregates.if.d.ts +35 -0
- package/lib/table/aggregation/value-accessor.type.d.ts +13 -0
- package/lib/table/data/common/row-number-column-def.d.ts +107 -0
- package/lib/table/data/common/row-number-columns.d.ts +17 -0
- package/lib/table/data/common/row-number-options.d.ts +19 -0
- package/lib/table/data/common/row-number-options.if.d.ts +44 -0
- package/lib/table/data/tablemodel/areamodel/aggregate-footer-options.if.d.ts +73 -0
- package/lib/table/data/tablemodel/areamodel/area-model-aggregate-footer.d.ts +145 -0
- package/lib/table/data/tablemodel/areamodel/area-model-cell-groups.d.ts +18 -1
- package/lib/table/data/tablemodel/areamodel/area-model-pivot-footer.d.ts +47 -0
- package/lib/table/data/tablemodel/areamodel/area-model-pivot.d.ts +250 -0
- package/lib/table/data/tablemodel/areamodel/area-model-row-groups.d.ts +314 -0
- package/lib/table/data/tablemodel/areamodel/area-model-tree.d.ts +14 -0
- package/lib/table/data/tablemodel/areamodel/area-model.if.d.ts +39 -0
- package/lib/table/data/tablemodel/areamodel/derived-values-change.if.d.ts +30 -0
- package/lib/table/factory/pivot-factory.d.ts +63 -0
- package/lib/table/factory/pivot-table-model-para.if.d.ts +44 -0
- package/lib/table/factory/row-group-factory.d.ts +70 -0
- package/lib/table/factory/row-group-table-model-para.if.d.ts +66 -0
- package/lib/table/factory/table-factory.d.ts +45 -1
- package/lib/table/instanceof-workaround.d.ts +143 -6
- package/lib/table/pivot/area-model-pivot-header.d.ts +9 -0
- package/lib/table/pivot/pivot-column-def.d.ts +28 -0
- package/lib/table/pivot/pivot-column-info.if.d.ts +25 -0
- package/lib/table/pivot/pivot-config.if.d.ts +58 -0
- package/lib/table/pivot/pivot-internals.d.ts +13 -0
- package/lib/table/pivot/pivot-layout.d.ts +40 -0
- package/lib/table/pivot/pivot-result.d.ts +157 -0
- package/lib/table/pivot/pivot-table-model.d.ts +86 -0
- package/lib/table/pivot/pivot-value.type.d.ts +67 -0
- package/lib/table/render-scope.d.ts +61 -4
- package/lib/table/renderer/aggregate-cell-renderer.d.ts +27 -0
- package/lib/table/renderer/row-number-cell-renderer.d.ts +27 -0
- package/lib/table/rowgroup/group-rows-internals.d.ts +14 -0
- package/lib/table/rowgroup/group-rows-options.if.d.ts +13 -0
- package/lib/table/rowgroup/group-rows.d.ts +48 -0
- package/lib/table/rowgroup/row-group-column-def.d.ts +26 -0
- package/lib/table/rowgroup/row-group-column-options.if.d.ts +27 -0
- package/lib/table/rowgroup/row-group-detail-row.d.ts +8 -0
- package/lib/table/rowgroup/row-group-field.type.d.ts +56 -0
- package/lib/table/rowgroup/row-group-internals.d.ts +31 -0
- package/lib/table/rowgroup/row-group-layout.d.ts +13 -0
- package/lib/table/rowgroup/row-group-model-options.if.d.ts +39 -0
- package/lib/table/rowgroup/row-group.d.ts +51 -0
- package/lib/table/rowgroup/row-group.if.d.ts +48 -0
- package/lib/table/selection/selection-aggregator.d.ts +37 -0
- package/lib/table/selection/selection-service.d.ts +7 -0
- package/lib/table/service/convenience-dom.service.d.ts +5 -0
- package/lib/table/table-api.d.ts +112 -1
- package/lib/table/table-scope.d.ts +19 -0
- package/package.json +3 -2
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import { AreaModelTree } from './area-model-tree.js';
|
|
2
|
+
import { ColumnDefIf } from '../column/column-def.if.js';
|
|
3
|
+
import { CellRendererIf } from '../../../renderer/cell-render.if.js';
|
|
4
|
+
import { FilterFunction } from '../../common/filter-function.js';
|
|
5
|
+
import { SortItem } from '../../common/sort-item.js';
|
|
6
|
+
import { DerivedValuesChangeIf } from './derived-values-change.if.js';
|
|
7
|
+
import { RowGroupField } from '../../../rowgroup/row-group-field.type.js';
|
|
8
|
+
import { RowGroupModelOptionsIf } from '../../../rowgroup/row-group-model-options.if.js';
|
|
9
|
+
import { RowGroup } from '../../../rowgroup/row-group.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The body of a row grouping: flat rows grouped by one or more levels, each group a row with an arrow, a label
|
|
13
|
+
* (`"Nord (3)"`) and aggregates per column (sum, average, …); below it the sub-groups or the original rows. Usually
|
|
14
|
+
* created by {@link RowGroupFactory.createTableModel}, which also adds the group column, hides the grouped columns and
|
|
15
|
+
* adds a total row.
|
|
16
|
+
*
|
|
17
|
+
* It is a tree ({@link AreaModelTree}, `isAreaModelTree` is true): group rows are {@link TreeRowIf tree rows} with a
|
|
18
|
+
* {@link RowGroupIf} as `data`, detail rows hold your objects. Expanding, `tableApi.expandAll()`, keyboard, screen
|
|
19
|
+
* reader attributes and remembering expanded groups (`autoRestoreOptions`) work as in a tree table.
|
|
20
|
+
*
|
|
21
|
+
* - **Filter**: `externalFilterFunction` gets your object (as in a flat table) – never a group. Groups without a match
|
|
22
|
+
* disappear; aggregates and counts follow the filter.
|
|
23
|
+
* - **Sorting**: by an aggregated column → groups by aggregate, rows by value; by the group column → groups by key;
|
|
24
|
+
* `tableApi.sort(fn)` sorts the rows inside their groups with `fn(a, b)` on your objects.
|
|
25
|
+
* - **Editing**: group rows are not editable. A changed value recomputes only the groups of that row; a changed
|
|
26
|
+
* grouped value moves the row to its new group.
|
|
27
|
+
* - **Regrouping**: {@link TableApi.setRowGroupBy} (or {@link setGroupBy} + `tableApi.repaintHard()`) – expanded
|
|
28
|
+
* groups, filter, sorting and checked rows stay.
|
|
29
|
+
* - Aggregates are computed when a group row is read after a change – once, not per repaint or scroll frame.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* const tableModel = RowGroupFactory.createTableModel<Sale>({
|
|
33
|
+
* rows: sales, columnDefs, groupBy: ["region", "produkt"], aggregations: { umsatz: "sum" }
|
|
34
|
+
* });
|
|
35
|
+
* const body = tableModel.getBodyModel() as AreaModelRowGroups<Sale>;
|
|
36
|
+
* body.getGroups().map(g => g.results.umsatz); // [270, 180]
|
|
37
|
+
*
|
|
38
|
+
* @see Training: [Row grouping](https://gui.expert/training/row-grouping/)
|
|
39
|
+
*
|
|
40
|
+
* @typeParam T - the row type
|
|
41
|
+
* @since 1.1.94
|
|
42
|
+
* @category Row grouping
|
|
43
|
+
*/
|
|
44
|
+
export declare class AreaModelRowGroups<T = any> extends AreaModelTree<any> {
|
|
45
|
+
/** Marker for {@link isAreaModelRowGroups} (works across copies of the core). `type` stays `"AreaModelTree"`. */
|
|
46
|
+
readonly kind = "AreaModelRowGroups";
|
|
47
|
+
/** The options (groupBy as set by {@link setGroupBy}). */
|
|
48
|
+
readonly options: RowGroupModelOptionsIf<T>;
|
|
49
|
+
/** Properties of the columns the factory hid for the current grouping. @internal */
|
|
50
|
+
hiddenColumnProperties: string[];
|
|
51
|
+
private sourceRows;
|
|
52
|
+
private fields;
|
|
53
|
+
private groups;
|
|
54
|
+
private readonly specs;
|
|
55
|
+
private readonly specByKey;
|
|
56
|
+
private readonly textRenderer;
|
|
57
|
+
/** the tree row of every group by id */
|
|
58
|
+
private groupRowById;
|
|
59
|
+
/** the tree row of every source row by source index */
|
|
60
|
+
private detailRows;
|
|
61
|
+
/** the filter of the user (gets the objects) and its result per source index */
|
|
62
|
+
private userFilter?;
|
|
63
|
+
private accepted?;
|
|
64
|
+
private allDirty;
|
|
65
|
+
private readonly dirtyLeaves;
|
|
66
|
+
private totals;
|
|
67
|
+
private lastSort?;
|
|
68
|
+
private batchDepth;
|
|
69
|
+
private regroupPending;
|
|
70
|
+
private builds;
|
|
71
|
+
/**
|
|
72
|
+
* @param {T[]} rows - the rows (not changed by the model)
|
|
73
|
+
* @param {ColumnDefIf[]} columnDefs - the columns of the table, usually with a {@link RowGroupColumnDef}
|
|
74
|
+
* @param {RowGroupModelOptionsIf<T>} options - levels, aggregations, expanded state …
|
|
75
|
+
* @param {number} [defaultRowHeight=34] - row height in px
|
|
76
|
+
*/
|
|
77
|
+
constructor(rows: T[], columnDefs: ColumnDefIf[], options: RowGroupModelOptionsIf<T>, defaultRowHeight?: number);
|
|
78
|
+
/**
|
|
79
|
+
* The rows the groups are built from.
|
|
80
|
+
*
|
|
81
|
+
* @returns {T[]} the source rows (do not modify the array – use {@link setRows})
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* const all = body.getSourceRows().length;
|
|
85
|
+
*/
|
|
86
|
+
getSourceRows(): T[];
|
|
87
|
+
/**
|
|
88
|
+
* Replaces the rows and groups them again (expanded groups, filter and sorting stay). Repaint with
|
|
89
|
+
* `tableApi.repaintHard()` – the row count changes.
|
|
90
|
+
*
|
|
91
|
+
* @param {T[]} rows - the new rows
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* body.setRows(await loadSales());
|
|
95
|
+
* tableApi.repaintHard();
|
|
96
|
+
*/
|
|
97
|
+
setRows(rows: T[]): void;
|
|
98
|
+
/**
|
|
99
|
+
* The current levels.
|
|
100
|
+
*
|
|
101
|
+
* @returns {RowGroupField<T>[]} a copy of the levels
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* body.getGroupBy(); // ["region", "produkt"]
|
|
105
|
+
*/
|
|
106
|
+
getGroupBy(): RowGroupField<T>[];
|
|
107
|
+
/**
|
|
108
|
+
* Groups the rows by other levels. Expanded groups whose path stays keep their state; filter, sorting and checked
|
|
109
|
+
* rows stay. An empty list shows the rows without groups. Prefer {@link TableApi.setRowGroupBy}: it also shows and
|
|
110
|
+
* hides the grouped columns and repaints.
|
|
111
|
+
*
|
|
112
|
+
* @param {RowGroupField<T>[]} groupBy - the new levels, top first
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* body.setGroupBy(["produkt"]);
|
|
116
|
+
* tableApi.repaintHard();
|
|
117
|
+
*/
|
|
118
|
+
setGroupBy(groupBy: RowGroupField<T>[]): void;
|
|
119
|
+
/**
|
|
120
|
+
* Builds the groups again from the current rows – after grouped values were changed outside the table (inside the
|
|
121
|
+
* table, editing a grouped value moves the row by itself). Repaint with `tableApi.repaintHard()`.
|
|
122
|
+
*
|
|
123
|
+
* @example
|
|
124
|
+
* sales[0].region = "Süd"; // a live update of a grouped value
|
|
125
|
+
* body.regroup();
|
|
126
|
+
* tableApi.repaintHard();
|
|
127
|
+
*/
|
|
128
|
+
regroup(): void;
|
|
129
|
+
/**
|
|
130
|
+
* The groups of the top level (with sub-groups, counts and aggregates).
|
|
131
|
+
*
|
|
132
|
+
* @returns {RowGroup<T>[]} the groups in display order (do not modify)
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* body.getGroups().map(g => `${g.label}: ${g.results.umsatz}`); // ["Nord: 270", "Süd: 180"]
|
|
136
|
+
*/
|
|
137
|
+
getGroups(): RowGroup<T>[];
|
|
138
|
+
/**
|
|
139
|
+
* The group with the given keys.
|
|
140
|
+
*
|
|
141
|
+
* @param {unknown[]} path - the keys from the top level down, e.g. `["Nord", "A"]`
|
|
142
|
+
* @returns {RowGroup<T> | undefined} the group, undefined if there is none
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* body.findGroup(["Nord", "A"])?.results.umsatz; // 220
|
|
146
|
+
*/
|
|
147
|
+
findGroup(path: unknown[]): RowGroup<T> | undefined;
|
|
148
|
+
/**
|
|
149
|
+
* The group of a displayed row.
|
|
150
|
+
*
|
|
151
|
+
* @param {number} rowIndex - index of the displayed body row
|
|
152
|
+
* @returns {RowGroup<T> | undefined} the group of a group row, undefined for a detail row
|
|
153
|
+
*
|
|
154
|
+
* @example
|
|
155
|
+
* const group = body.getRowGroup(0); // the first group (if the first row is a group row)
|
|
156
|
+
*/
|
|
157
|
+
getRowGroup(rowIndex: number): RowGroup<T> | undefined;
|
|
158
|
+
/**
|
|
159
|
+
* Your object of a displayed detail row.
|
|
160
|
+
*
|
|
161
|
+
* @param {number} rowIndex - index of the displayed body row
|
|
162
|
+
* @returns {T | undefined} the object, undefined for a group row
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* const sale = body.getDetailRow(1);
|
|
166
|
+
*/
|
|
167
|
+
getDetailRow(rowIndex: number): T | undefined;
|
|
168
|
+
/**
|
|
169
|
+
* True if the displayed row is a group row.
|
|
170
|
+
*
|
|
171
|
+
* @param {number} rowIndex - index of the displayed body row
|
|
172
|
+
* @returns {boolean} true for a group row
|
|
173
|
+
*
|
|
174
|
+
* @example
|
|
175
|
+
* const bold = body.isGroupRowIndex(rowIndex);
|
|
176
|
+
*/
|
|
177
|
+
isGroupRowIndex(rowIndex: number): boolean;
|
|
178
|
+
/**
|
|
179
|
+
* The aggregates over all rows the filter keeps (the same as the total row).
|
|
180
|
+
*
|
|
181
|
+
* @returns {Record<string, unknown>} key → result
|
|
182
|
+
*
|
|
183
|
+
* @example
|
|
184
|
+
* body.getTotals().umsatz; // 450
|
|
185
|
+
*/
|
|
186
|
+
getTotals(): Record<string, unknown>;
|
|
187
|
+
/**
|
|
188
|
+
* The checked objects (detail rows) – with a checkbox column or `showCheckboxWihoutExtraColumn`.
|
|
189
|
+
*
|
|
190
|
+
* @returns {T[]} the checked objects in source order
|
|
191
|
+
*
|
|
192
|
+
* @example
|
|
193
|
+
* const selected = body.getCheckedRows();
|
|
194
|
+
*/
|
|
195
|
+
getCheckedRows(): T[];
|
|
196
|
+
/**
|
|
197
|
+
* Expands the groups above a level and collapses the others: `0` collapses all, `1` opens the top level, a large
|
|
198
|
+
* number opens all. Repaint with `tableApi.repaintHard()`.
|
|
199
|
+
*
|
|
200
|
+
* @param {number} level - the number of open levels
|
|
201
|
+
*
|
|
202
|
+
* @example
|
|
203
|
+
* body.expandToLevel(1);
|
|
204
|
+
* tableApi.repaintHard();
|
|
205
|
+
*/
|
|
206
|
+
expandToLevel(level: number): void;
|
|
207
|
+
/**
|
|
208
|
+
* Counts how often the groups were built (start, {@link setRows}, {@link setGroupBy}, {@link regroup}, an edited
|
|
209
|
+
* grouped value). The table compares it before and after an edit to know that the rows moved (→ hard repaint).
|
|
210
|
+
*
|
|
211
|
+
* @returns {number} the number of builds
|
|
212
|
+
*
|
|
213
|
+
* @example
|
|
214
|
+
* const before = body.getBuildCount();
|
|
215
|
+
* body.setValue(1, 1, "Ost");
|
|
216
|
+
* const moved = body.getBuildCount() !== before; // true: "region" is grouped
|
|
217
|
+
*/
|
|
218
|
+
getBuildCount(): number;
|
|
219
|
+
/**
|
|
220
|
+
* Runs several `setValue` calls as one change: a row whose grouped value changes moves only at the end, so the row
|
|
221
|
+
* indices of the other calls stay valid (`tableApi.updateCells()` uses it).
|
|
222
|
+
*
|
|
223
|
+
* @param {() => void} fn - the changes
|
|
224
|
+
* @returns {boolean} true if the groups were built again at the end
|
|
225
|
+
*
|
|
226
|
+
* @example
|
|
227
|
+
* const moved = body.batch(() => {
|
|
228
|
+
* body.setValue(1, 1, "Süd"); // region – moves the row
|
|
229
|
+
* body.setValue(2, 4, 999); // still the row that was at index 2
|
|
230
|
+
* });
|
|
231
|
+
*/
|
|
232
|
+
batch(fn: () => void): boolean;
|
|
233
|
+
getValueAt(rowIndex: number, columnIndex: number): any;
|
|
234
|
+
getTextValueAt(rowIndex: number, columnIndex: number): string;
|
|
235
|
+
/** Group rows: the {@link AggregateCellRenderer} for aggregates, no renderer otherwise; detail rows: the column's. */
|
|
236
|
+
getCellRenderer(rowIndex: number, columnIndex: number): CellRendererIf | undefined;
|
|
237
|
+
getCustomClassesAt(rowIndex: number, columnIndex: number): string[];
|
|
238
|
+
/**
|
|
239
|
+
* True for the label cell of a group row (the table gives it `role="rowheader"`).
|
|
240
|
+
*
|
|
241
|
+
* @param {number} rowIndex - index of the displayed body row
|
|
242
|
+
* @param {number} columnIndex - column index
|
|
243
|
+
* @returns {boolean} true for the group label
|
|
244
|
+
*/
|
|
245
|
+
isGroupLabelCell(rowIndex: number, columnIndex: number): boolean;
|
|
246
|
+
/** Group rows and the group column are not editable. */
|
|
247
|
+
isEditable(rowIndex: number, columnIndex: number): boolean;
|
|
248
|
+
/**
|
|
249
|
+
* Sets a value of a detail row (group rows: `false`). Only the groups of that row are computed again; if a grouped
|
|
250
|
+
* value changed, the row moves to its new group (the row count may change).
|
|
251
|
+
*/
|
|
252
|
+
setValue(rowIndex: number, columnIndex: number, value: any): boolean;
|
|
253
|
+
/**
|
|
254
|
+
* Filters the rows: `predictFn` gets **your objects** (as in a flat table), index and array of the source rows –
|
|
255
|
+
* never a group. Groups without a matching row disappear (also below collapsed groups); counts and aggregates follow
|
|
256
|
+
* the filter. Usually called by `tableApi.externalFilterChanged()` with `tableOptions.externalFilterFunction`.
|
|
257
|
+
*
|
|
258
|
+
* @param {FilterFunction<S>} predictFn - returns a truthy value for the rows to show
|
|
259
|
+
*
|
|
260
|
+
* @example
|
|
261
|
+
* tableOptions.externalFilterFunction = (sale: Sale) => sale.monat === "Jan";
|
|
262
|
+
* tableApi.externalFilterChanged(); // Nord (2) 150 · Süd (1) 80
|
|
263
|
+
*/
|
|
264
|
+
externalFilterChanged<S>(predictFn: FilterFunction<S>): void;
|
|
265
|
+
/**
|
|
266
|
+
* Sorts groups and rows (see the class description); the order is applied again after regrouping.
|
|
267
|
+
*
|
|
268
|
+
* @param {SortItem[]} sortItems - primary key first
|
|
269
|
+
* @returns {boolean} false for an empty list
|
|
270
|
+
*
|
|
271
|
+
* @example
|
|
272
|
+
* body.doSort([new SortItem(tableApi.getColumnIndex("umsatz"), "desc")]); // groups by sum, rows by value
|
|
273
|
+
* tableApi.repaint();
|
|
274
|
+
*/
|
|
275
|
+
doSort(sortItems: SortItem[]): boolean;
|
|
276
|
+
/**
|
|
277
|
+
* Sorts the rows inside their groups with your function – it gets your objects, as in a flat table. The groups keep
|
|
278
|
+
* their order. Used by `tableApi.sort(fn)`.
|
|
279
|
+
*
|
|
280
|
+
* @param {(a: any, b: any) => number} compareFn - compares two of your objects
|
|
281
|
+
*
|
|
282
|
+
* @example
|
|
283
|
+
* tableApi.sort((a: Sale, b: Sale) => b.umsatz - a.umsatz);
|
|
284
|
+
*/
|
|
285
|
+
sort(compareFn: (a: any, b: any) => number): void;
|
|
286
|
+
/**
|
|
287
|
+
* Drops the computed aggregates; with `change` (an edit or `updateCells`) only those of the groups of the changed rows.
|
|
288
|
+
* The table calls it; the next read computes them again.
|
|
289
|
+
*
|
|
290
|
+
* @param {DerivedValuesChangeIf} [change] - the changed rows
|
|
291
|
+
*
|
|
292
|
+
* @example
|
|
293
|
+
* sales[0].umsatz = 999; // changed outside the table
|
|
294
|
+
* tableApi.repaint(); // calls invalidateDerivedValues() – all groups are computed again
|
|
295
|
+
*/
|
|
296
|
+
invalidateDerivedValues(change?: DerivedValuesChangeIf): void;
|
|
297
|
+
/** After sorting the tree rows: the groups (`getGroups()`, `children`) in display order. */
|
|
298
|
+
private syncGroupOrder;
|
|
299
|
+
/** The label of a group row in the group column. */
|
|
300
|
+
protected getGroupLabel(group: RowGroup<T>): string;
|
|
301
|
+
/** Computes what is missing: everything after a change of data/filter/grouping, otherwise the dirty paths. */
|
|
302
|
+
protected ensureAggregates(): void;
|
|
303
|
+
private markRowDirty;
|
|
304
|
+
private keysOf;
|
|
305
|
+
/** A comparator of two sibling rows for one sort column. */
|
|
306
|
+
private createRowComparator;
|
|
307
|
+
/**
|
|
308
|
+
* (Re)builds groups and tree rows from the source rows, keeps the expanded state of known groups, the checked
|
|
309
|
+
* detail rows, the filter and the sorting.
|
|
310
|
+
*/
|
|
311
|
+
private build;
|
|
312
|
+
private createGroupRow;
|
|
313
|
+
private isExpandedByDefault;
|
|
314
|
+
}
|
|
@@ -106,6 +106,20 @@ export declare class AreaModelTree<S> extends AbstractAreaModel<TreeRow<S>> {
|
|
|
106
106
|
* @since 1.1.91
|
|
107
107
|
*/
|
|
108
108
|
getDisplayedChildren(row: TreeRowIf<S>): TreeRowIf<S>[];
|
|
109
|
+
/**
|
|
110
|
+
* The leaves (rows without children) of the whole tree, depth-first in the current order – independent of the
|
|
111
|
+
* expanded state. Used for totals: collapsing a node does not change them, and parent rows are not counted twice.
|
|
112
|
+
*
|
|
113
|
+
* @param {boolean} [filtered=true] - only the leaves the active filter keeps (`externalFilterChanged`); `false`: all
|
|
114
|
+
* @returns {TreeRowIf<S>[]} a new array
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* const treeModel = tableModel.getBodyModel() as AreaModelTree<OrderNode>;
|
|
118
|
+
* const total = aggregate(treeModel.getLeafRows(), row => row.data.total, "sum");
|
|
119
|
+
*
|
|
120
|
+
* @since 1.1.94
|
|
121
|
+
*/
|
|
122
|
+
getLeafRows(filtered?: boolean): TreeRowIf<S>[];
|
|
109
123
|
/**
|
|
110
124
|
* The displayed rows: all parents expanded and accepted by the active filter, in display order.
|
|
111
125
|
*
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { DerivedValuesChangeIf } from './derived-values-change.if.js';
|
|
1
2
|
import { CellRendererIf } from '../../../renderer/cell-render.if.js';
|
|
2
3
|
import { CheckedType } from '../../common/checked-type.js';
|
|
3
4
|
import { CheckboxModelIf } from '../../../checkbox/checkbox-model.if.js';
|
|
@@ -280,6 +281,44 @@ export interface AreaModelIf {
|
|
|
280
281
|
* @since 1.1.92
|
|
281
282
|
*/
|
|
282
283
|
getUnfilteredRowCount?(): number;
|
|
284
|
+
/**
|
|
285
|
+
* Optional: drops cached, derived values (e.g. the totals of an {@link AreaModelAggregateFooter}). The table calls
|
|
286
|
+
* it for all three areas when data may have changed: filter, sorting, editing, `updateCells()` and the public
|
|
287
|
+
* `tableApi.repaint()` / `repaintHard()` – not on scrolling or internal repaints.
|
|
288
|
+
*
|
|
289
|
+
* After an edit and `updateCells()` the table passes `change` (since 1.1.94): the area and the displayed rows that
|
|
290
|
+
* changed – a model may recompute only what depends on them ({@link AreaModelRowGroups} recomputes the groups of
|
|
291
|
+
* these rows). Without `change` anything may have changed.
|
|
292
|
+
*
|
|
293
|
+
* @example
|
|
294
|
+
* class CachedTotalsFooter extends AreaModel {
|
|
295
|
+
* private total?: number;
|
|
296
|
+
* constructor(private readonly body: AreaModelObjectArray<Order>) {
|
|
297
|
+
* super();
|
|
298
|
+
* this.areaIdent = "footer";
|
|
299
|
+
* }
|
|
300
|
+
* invalidateDerivedValues(): void { this.total = undefined; }
|
|
301
|
+
* override getRowCount(): number { return 1; }
|
|
302
|
+
* override getValueAt(_row: number, _col: number): any {
|
|
303
|
+
* return this.total ??= this.body.getFilteredRows().reduce((sum, o) => sum + o.total, 0);
|
|
304
|
+
* }
|
|
305
|
+
* }
|
|
306
|
+
*
|
|
307
|
+
* @since 1.1.94
|
|
308
|
+
*/
|
|
309
|
+
invalidateDerivedValues?(change?: DerivedValuesChangeIf): void;
|
|
310
|
+
/**
|
|
311
|
+
* Optional: true for a cell that names its row (a label such as "Nord" or "Total") – the table gives it
|
|
312
|
+
* `role="rowheader"` instead of `gridcell`, so screen readers announce it with the row.
|
|
313
|
+
*
|
|
314
|
+
* @example
|
|
315
|
+
* class LabeledBody extends AreaModel {
|
|
316
|
+
* isRowHeaderCell(_rowIndex: number, columnIndex: number): boolean { return columnIndex === 0; }
|
|
317
|
+
* }
|
|
318
|
+
*
|
|
319
|
+
* @since 1.1.94
|
|
320
|
+
*/
|
|
321
|
+
isRowHeaderCell?(rowIndex: number, columnIndex: number): boolean;
|
|
283
322
|
/**
|
|
284
323
|
* Returns the value of the cell at the specified row and column indices.
|
|
285
324
|
* This is one of the most important methods in the area model, as it provides
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { AreaIdent } from '../area-ident.type.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* What changed when the table calls {@link AreaModelIf.invalidateDerivedValues} after an edit or
|
|
5
|
+
* {@link TableApi.updateCells}: the area and the displayed rows. A model may use it to recompute only what depends on
|
|
6
|
+
* these rows (a row grouping recomputes the path of the changed row only). Without it everything may have changed.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* class MyModel extends AreaModel {
|
|
10
|
+
* private readonly dirtyRows = new Set<number>();
|
|
11
|
+
* private allDirty = true;
|
|
12
|
+
*
|
|
13
|
+
* invalidateDerivedValues(change?: DerivedValuesChangeIf): void {
|
|
14
|
+
* if (change?.area === "body") {
|
|
15
|
+
* change.rowIndices.forEach(r => this.dirtyRows.add(r)); // only these rows
|
|
16
|
+
* } else {
|
|
17
|
+
* this.allDirty = true; // anything may have changed
|
|
18
|
+
* }
|
|
19
|
+
* }
|
|
20
|
+
* }
|
|
21
|
+
*
|
|
22
|
+
* @since 1.1.94
|
|
23
|
+
* @category Area models
|
|
24
|
+
*/
|
|
25
|
+
export interface DerivedValuesChangeIf {
|
|
26
|
+
/** The area whose values changed. */
|
|
27
|
+
area: AreaIdent;
|
|
28
|
+
/** The changed rows (indices of the displayed rows at the time of the change). */
|
|
29
|
+
rowIndices: number[];
|
|
30
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { PivotTableModelParaIf } from './pivot-table-model-para.if.js';
|
|
2
|
+
import { PivotTableModel } from '../pivot/pivot-table-model.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Creates pivot tables: rows grouped by row fields (a hierarchy with subtotals), the values of column fields as
|
|
6
|
+
* columns (several fields = a multi-row header), aggregated values in the cells, total column and total row.
|
|
7
|
+
* MIT-licensed, no dependencies – at ag-Grid this is an Enterprise feature.
|
|
8
|
+
*
|
|
9
|
+
* The configuration has the form of a row grouping ({@link RowGroupFactory}): fields are property paths, functions
|
|
10
|
+
* (e.g. the quarter of a date) or {@link RowGroupFieldIf} with label, format and order; values use the aggregators of
|
|
11
|
+
* {@link Aggregators} (`sum`, `avg`, `count` …) or your own {@link AggregatorIf}. Subtotals and totals are merged from
|
|
12
|
+
* aggregation states – an average is always the average of the rows.
|
|
13
|
+
*
|
|
14
|
+
* Filter with `externalFilterFunction` (it gets your source objects, before the aggregation), sort by clicking a value
|
|
15
|
+
* column, export with `downloadExcel()`, and pivot again at runtime with {@link TableApi.setPivotConfig}.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* // case 1: regions × months, sum of umsatz, total column and total row
|
|
19
|
+
* const tableModel = PivotFactory.createTableModel<Sale>({
|
|
20
|
+
* rows: sales,
|
|
21
|
+
* rowFields: ["region"],
|
|
22
|
+
* columnFields: [{ value: "monat", order: "insertion" }],
|
|
23
|
+
* values: ["umsatz"]
|
|
24
|
+
* });
|
|
25
|
+
* // Region | Jan | Feb | Total
|
|
26
|
+
* // Nord | 150 | 120 | 270
|
|
27
|
+
* // Süd | 80 | 100 | 180
|
|
28
|
+
* // Total | 230 | 220 | 450
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* // countries › cities × years, revenue, all groups closed; pivot by another field with a select
|
|
32
|
+
* const tableModel = PivotFactory.createTableModel<Order>({
|
|
33
|
+
* rows: orders,
|
|
34
|
+
* rowFields: ["country", "city"],
|
|
35
|
+
* columnFields: [{ name: "year", label: "Year", value: o => new Date(o.date).getFullYear() }],
|
|
36
|
+
* values: [{ value: "total", label: "Revenue" }],
|
|
37
|
+
* expanded: 0
|
|
38
|
+
* });
|
|
39
|
+
* select.addEventListener("change", () => tableApi.setPivotConfig({ columnFields: [select.value] }));
|
|
40
|
+
*
|
|
41
|
+
* @see Training: [Pivot tables](https://gui.expert/training/pivot-tables/)
|
|
42
|
+
*
|
|
43
|
+
* @since 1.1.94
|
|
44
|
+
* @category Pivot
|
|
45
|
+
*/
|
|
46
|
+
export declare class PivotFactory {
|
|
47
|
+
/**
|
|
48
|
+
* Creates the table model of a pivot table (see the class description).
|
|
49
|
+
*
|
|
50
|
+
* @param {PivotTableModelParaIf<T>} p - rows, row fields, column fields, values, options
|
|
51
|
+
* @returns {PivotTableModel<T>} the table model – a {@link TableModelIf}
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* // case 3: the average – Jan total is 230 / 3 = 76.67, not the mean of the region averages (77.5)
|
|
55
|
+
* const tableModel = PivotFactory.createTableModel<Sale>({
|
|
56
|
+
* rows: sales, rowFields: ["region"], columnFields: ["monat"],
|
|
57
|
+
* values: [{ value: "umsatz", aggregator: "avg" }]
|
|
58
|
+
* });
|
|
59
|
+
*/
|
|
60
|
+
static createTableModel<T = any>(p: PivotTableModelParaIf<T>): PivotTableModel<T>;
|
|
61
|
+
/** The pivot part of the parameters. */
|
|
62
|
+
private static toConfig;
|
|
63
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { TableOptionsIf } from '../data/options/table-options.if.js';
|
|
2
|
+
import { DefaultRowHeightsIf } from '../data/options/default-row-heights.if.js';
|
|
3
|
+
import { GetT } from '../data/common/get-t.js';
|
|
4
|
+
import { SelectionModelIf } from '../selection/selection-model.if.js';
|
|
5
|
+
import { RowNumberOptionsIf } from '../data/common/row-number-options.if.js';
|
|
6
|
+
import { SizeIf } from '../data/common/size.if.js';
|
|
7
|
+
import { RowGroupColumnOptionsIf } from '../rowgroup/row-group-column-options.if.js';
|
|
8
|
+
import { PivotConfigIf } from '../pivot/pivot-config.if.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Parameters of {@link PivotFactory.createTableModel}: the rows, the pivot ({@link PivotConfigIf}) and the look of the
|
|
12
|
+
* table.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* const para: PivotTableModelParaIf<Sale> = {
|
|
16
|
+
* rows: sales,
|
|
17
|
+
* rowFields: ["region", "produkt"],
|
|
18
|
+
* columnFields: [{ value: "monat", order: "insertion" }],
|
|
19
|
+
* values: ["umsatz"],
|
|
20
|
+
* emptyText: "–",
|
|
21
|
+
* valueColumnWidth: px80
|
|
22
|
+
* };
|
|
23
|
+
* const tableModel = PivotFactory.createTableModel(para);
|
|
24
|
+
*
|
|
25
|
+
* @typeParam T - the row type
|
|
26
|
+
* @since 1.1.94
|
|
27
|
+
* @category Pivot
|
|
28
|
+
*/
|
|
29
|
+
export interface PivotTableModelParaIf<T = any> extends PivotConfigIf<T> {
|
|
30
|
+
/** The source rows (not changed). */
|
|
31
|
+
rows: T[];
|
|
32
|
+
/** The group column (arrows and row labels): header, width, `showCount` (default false), own label, fixed (default true). */
|
|
33
|
+
groupColumn?: Partial<RowGroupColumnOptionsIf<T>>;
|
|
34
|
+
/** The width of a value column. Default 100 px. */
|
|
35
|
+
valueColumnWidth?: SizeIf;
|
|
36
|
+
/** Row numbers in front of the group column (counted per displayed row). */
|
|
37
|
+
rowNumbersVisible?: boolean | Partial<RowNumberOptionsIf>;
|
|
38
|
+
/** Options of the table (row heights, selection model …). */
|
|
39
|
+
tableOptions?: TableOptionsIf;
|
|
40
|
+
/** Row heights of header, body and footer. Default: `tableOptions.defaultRowHeights`. */
|
|
41
|
+
defaultRowHeights?: DefaultRowHeightsIf;
|
|
42
|
+
/** Selection model factory. Default: `tableOptions.getSelectionModel`. */
|
|
43
|
+
getSelectionModel?: GetT<SelectionModelIf>;
|
|
44
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { TableModelIf } from '../data/tablemodel/table-model.if.js';
|
|
2
|
+
import { RowGroupTableModelParaIf } from './row-group-table-model-para.if.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Creates a table model that groups flat rows: one row per group with an arrow, a label (`"Nord (3)"`) and
|
|
6
|
+
* aggregates per column, below it the sub-groups or the original rows – plus a total row. MIT-licensed, no
|
|
7
|
+
* dependencies, built on the tree table ({@link AreaModelRowGroups}).
|
|
8
|
+
*
|
|
9
|
+
* What the factory does:
|
|
10
|
+
* - puts a {@link RowGroupColumnDef} (arrows, labels) behind row numbers and checkbox column and fixes it on the left;
|
|
11
|
+
* - hides the columns of grouped properties (`hideGroupedColumns`), their values are in the group column;
|
|
12
|
+
* - builds the body ({@link AreaModelRowGroups}) and a total row from the `aggregations` ({@link AreaModelAggregateFooter}).
|
|
13
|
+
*
|
|
14
|
+
* Regroup at runtime with {@link TableApi.setRowGroupBy}; filter, sort, edit and export as in any table.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* // R1: sales by region, sum of umsatz, total row
|
|
18
|
+
* const tableModel = RowGroupFactory.createTableModel<Sale>({
|
|
19
|
+
* rows: sales,
|
|
20
|
+
* columnDefs,
|
|
21
|
+
* groupBy: ["region"],
|
|
22
|
+
* aggregations: { umsatz: "sum" }
|
|
23
|
+
* });
|
|
24
|
+
* // ▾ Nord (3) … 270 · A Jan 100 · B Jan 50 · A Feb 120 · ▾ Süd (3) … 180 · … · footer "6 rows" … 450
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* // two levels, all groups closed, regroup with a select
|
|
28
|
+
* const tableModel = RowGroupFactory.createTableModel<Order>({
|
|
29
|
+
* rows: orders, columnDefs, groupBy: ["country", "city"],
|
|
30
|
+
* aggregations: { quantity: "sum", total: "sum" }, expanded: 0
|
|
31
|
+
* });
|
|
32
|
+
* select.addEventListener("change", () => tableApi.setRowGroupBy(select.value.split(",")));
|
|
33
|
+
*
|
|
34
|
+
* @see Training: [Row grouping](https://gui.expert/training/row-grouping/)
|
|
35
|
+
*
|
|
36
|
+
* @since 1.1.94
|
|
37
|
+
* @category Row grouping
|
|
38
|
+
*/
|
|
39
|
+
export declare class RowGroupFactory {
|
|
40
|
+
/**
|
|
41
|
+
* Creates the table model of a row grouping (see the class description).
|
|
42
|
+
*
|
|
43
|
+
* @param {RowGroupTableModelParaIf<T>} p - rows, levels, columns, aggregations, options
|
|
44
|
+
* @returns {TableModelIf} the table model – its body is an {@link AreaModelRowGroups}
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* const tableModel = RowGroupFactory.createTableModel<Sale>({
|
|
48
|
+
* rows: sales, columnDefs, groupBy: ["region", "produkt"], aggregations: { umsatz: "avg" }
|
|
49
|
+
* });
|
|
50
|
+
* const body = tableModel.getBodyModel() as AreaModelRowGroups<Sale>;
|
|
51
|
+
* body.getGroups()[0].results.umsatz; // 90 – Nord, the average of its three rows
|
|
52
|
+
*/
|
|
53
|
+
static createTableModel<T = any>(p: RowGroupTableModelParaIf<T>): TableModelIf;
|
|
54
|
+
/**
|
|
55
|
+
* Hides the columns of the grouped properties (and shows the ones that are no longer grouped) and sets the header of
|
|
56
|
+
* the group column – after `body.setGroupBy(…)`. {@link TableApi.setRowGroupBy} calls it for you.
|
|
57
|
+
*
|
|
58
|
+
* @param {TableModelIf} tableModel - a table model of {@link createTableModel}
|
|
59
|
+
* @returns {boolean} false if the body is no row grouping
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* const body = tableModel.getBodyModel() as AreaModelRowGroups<Sale>;
|
|
63
|
+
* body.setGroupBy(["produkt"]);
|
|
64
|
+
* RowGroupFactory.updateLayout(tableModel);
|
|
65
|
+
* tableApi.recalcWrappers();
|
|
66
|
+
* tableApi.repaintHard();
|
|
67
|
+
*/
|
|
68
|
+
static updateLayout(tableModel: TableModelIf): boolean;
|
|
69
|
+
private static footerConfig;
|
|
70
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { ColumnDefIf } from '../data/tablemodel/column/column-def.if.js';
|
|
2
|
+
import { TableOptionsIf } from '../data/options/table-options.if.js';
|
|
3
|
+
import { DefaultRowHeightsIf } from '../data/options/default-row-heights.if.js';
|
|
4
|
+
import { GetT } from '../data/common/get-t.js';
|
|
5
|
+
import { SelectionModelIf } from '../selection/selection-model.if.js';
|
|
6
|
+
import { RowNumberOptionsIf } from '../data/common/row-number-options.if.js';
|
|
7
|
+
import { AggregateSpecMap } from '../aggregation/aggregate-spec.if.js';
|
|
8
|
+
import { AggregateFooterConfig } from '../data/tablemodel/areamodel/aggregate-footer-options.if.js';
|
|
9
|
+
import { RowGroupField } from '../rowgroup/row-group-field.type.js';
|
|
10
|
+
import { RowGroupColumnOptionsIf } from '../rowgroup/row-group-column-options.if.js';
|
|
11
|
+
import { RowGroupExpanded } from '../rowgroup/row-group-model-options.if.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Parameters of {@link RowGroupFactory.createTableModel}.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* const para: RowGroupTableModelParaIf<Sale> = {
|
|
18
|
+
* rows: sales,
|
|
19
|
+
* columnDefs,
|
|
20
|
+
* groupBy: ["region", "produkt"],
|
|
21
|
+
* aggregations: { umsatz: "sum" },
|
|
22
|
+
* expanded: 1 // regions open, products closed
|
|
23
|
+
* };
|
|
24
|
+
* const tableModel = RowGroupFactory.createTableModel(para);
|
|
25
|
+
*
|
|
26
|
+
* @typeParam T - the row type
|
|
27
|
+
* @since 1.1.94
|
|
28
|
+
* @category Row grouping
|
|
29
|
+
*/
|
|
30
|
+
export interface RowGroupTableModelParaIf<T = any> {
|
|
31
|
+
/** The flat rows (not changed). */
|
|
32
|
+
rows: T[];
|
|
33
|
+
/** The levels, top first: property paths (`"region"`), functions (`o => o.date.getFullYear()`) or {@link RowGroupFieldIf}. */
|
|
34
|
+
groupBy: RowGroupField<T>[];
|
|
35
|
+
/** The columns. Default: one per key of the first row. The group column is added in front of them. */
|
|
36
|
+
columnDefs?: ColumnDefIf[];
|
|
37
|
+
/** Aggregates in the group rows; the key is the property of the column (`{ umsatz: "sum" }`, see {@link AggregateSpecMap}). */
|
|
38
|
+
aggregations?: AggregateSpecMap<T>;
|
|
39
|
+
/**
|
|
40
|
+
* The total row: `true` = the `aggregations`, `false` = none, or an own configuration ({@link AggregateFooterConfig}).
|
|
41
|
+
* Default: `true` if there are aggregations.
|
|
42
|
+
*/
|
|
43
|
+
footerAggregates?: boolean | AggregateFooterConfig<T>;
|
|
44
|
+
/** The group column: header, width, count, own label, fixed. */
|
|
45
|
+
groupColumn?: Partial<RowGroupColumnOptionsIf<T>>;
|
|
46
|
+
/** Hide the columns of the grouped properties. Default true. */
|
|
47
|
+
hideGroupedColumns?: boolean;
|
|
48
|
+
/** Expanded groups: `true` (default), `false`, open levels as a number (`0` = all closed) or a function per group. */
|
|
49
|
+
expanded?: RowGroupExpanded<T>;
|
|
50
|
+
/** The label of the group of empty keys (`null`, `undefined`, `""`). Default `"(empty)"`. */
|
|
51
|
+
emptyLabel?: string;
|
|
52
|
+
/** Options of the table (row heights, selection model, filter …). */
|
|
53
|
+
tableOptions?: TableOptionsIf;
|
|
54
|
+
/** Columns fixed on the left besides the group column (which is fixed by itself). Default 0. */
|
|
55
|
+
fixedLeftColumnCount?: number;
|
|
56
|
+
/** Columns fixed on the right. Default 0. */
|
|
57
|
+
fixedRightColumnCount?: number;
|
|
58
|
+
/** Row numbers in front of all columns (counted per displayed row, group rows included). */
|
|
59
|
+
rowNumbersVisible?: boolean | Partial<RowNumberOptionsIf>;
|
|
60
|
+
/** Set with a {@link CheckboxColumnDef} in `columnDefs` (default: detected). */
|
|
61
|
+
rowCheckboxVisible?: boolean;
|
|
62
|
+
/** Row heights of header, body and footer. Default: `tableOptions.defaultRowHeights`. */
|
|
63
|
+
defaultRowHeights?: DefaultRowHeightsIf;
|
|
64
|
+
/** Selection model factory. Default: `tableOptions.getSelectionModel`. */
|
|
65
|
+
getSelectionModel?: GetT<SelectionModelIf>;
|
|
66
|
+
}
|