@guiexpert/table 1.1.93 → 1.1.96

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.
Files changed (123) hide show
  1. package/README.md +25 -0
  2. package/css/main.css +334 -0
  3. package/css/table-calc.css +7 -0
  4. package/css/table-classes.css +34 -0
  5. package/css/table-color-vars.css +78 -0
  6. package/css/table-fill-batch.css +58 -0
  7. package/css/table-pivot.css +42 -0
  8. package/css/table-row-drag.css +81 -0
  9. package/css/table-row-group.css +25 -0
  10. package/css/table-tree.css +9 -0
  11. package/index.cjs +57 -47
  12. package/index.d.ts +67 -0
  13. package/index.js +11290 -4985
  14. package/lib/table/action/action-id.type.d.ts +1 -1
  15. package/lib/table/aggregation/aggregate-internals.d.ts +30 -0
  16. package/lib/table/aggregation/aggregate-spec.if.d.ts +51 -0
  17. package/lib/table/aggregation/aggregate.d.ts +58 -0
  18. package/lib/table/aggregation/aggregator-name.type.d.ts +39 -0
  19. package/lib/table/aggregation/aggregator.if.d.ts +51 -0
  20. package/lib/table/aggregation/aggregators.d.ts +37 -0
  21. package/lib/table/aggregation/create-value-getter.d.ts +18 -0
  22. package/lib/table/aggregation/selection-aggregates.if.d.ts +35 -0
  23. package/lib/table/aggregation/value-accessor.type.d.ts +13 -0
  24. package/lib/table/aria/aria-texts.d.ts +11 -0
  25. package/lib/table/aria/aria-texts.if.d.ts +87 -0
  26. package/lib/table/batch-edit/batch-edit-options.d.ts +21 -0
  27. package/lib/table/batch-edit/batch-edit-options.if.d.ts +20 -0
  28. package/lib/table/batch-edit/batch-edit-service.d.ts +104 -0
  29. package/lib/table/batch-edit/cell-change.if.d.ts +39 -0
  30. package/lib/table/batch-edit/cell-value-change.if.d.ts +18 -0
  31. package/lib/table/batch-edit/pending-cell-changes.d.ts +104 -0
  32. package/lib/table/calc/column-values.d.ts +55 -0
  33. package/lib/table/calc/show-values-as-internals.d.ts +4 -0
  34. package/lib/table/calc/show-values-as.d.ts +87 -0
  35. package/lib/table/calc/show-values-as.type.d.ts +55 -0
  36. package/lib/table/calc/value-getter-context.if.d.ts +35 -0
  37. package/lib/table/calc/value-getter.type.d.ts +36 -0
  38. package/lib/table/data/common/event/ge-batch-edit-event.d.ts +46 -0
  39. package/lib/table/data/common/event/ge-model-change-event.d.ts +27 -1
  40. package/lib/table/data/common/event/ge-row-drag-event.d.ts +89 -0
  41. package/lib/table/data/common/row-number-column-def.d.ts +107 -0
  42. package/lib/table/data/common/row-number-columns.d.ts +17 -0
  43. package/lib/table/data/common/row-number-options.d.ts +19 -0
  44. package/lib/table/data/common/row-number-options.if.d.ts +44 -0
  45. package/lib/table/data/options/table-options.d.ts +6 -0
  46. package/lib/table/data/options/table-options.if.d.ts +44 -0
  47. package/lib/table/data/tablemodel/areamodel/abstract-area-model.d.ts +66 -0
  48. package/lib/table/data/tablemodel/areamodel/aggregate-footer-options.if.d.ts +73 -0
  49. package/lib/table/data/tablemodel/areamodel/area-model-aggregate-footer.d.ts +150 -0
  50. package/lib/table/data/tablemodel/areamodel/area-model-array-of-arrays.d.ts +24 -0
  51. package/lib/table/data/tablemodel/areamodel/area-model-cell-groups.d.ts +18 -1
  52. package/lib/table/data/tablemodel/areamodel/area-model-object-array.d.ts +70 -0
  53. package/lib/table/data/tablemodel/areamodel/area-model-pivot-footer.d.ts +47 -0
  54. package/lib/table/data/tablemodel/areamodel/area-model-pivot.d.ts +281 -0
  55. package/lib/table/data/tablemodel/areamodel/area-model-row-groups.d.ts +336 -0
  56. package/lib/table/data/tablemodel/areamodel/area-model-tree.d.ts +97 -0
  57. package/lib/table/data/tablemodel/areamodel/area-model.if.d.ts +94 -0
  58. package/lib/table/data/tablemodel/areamodel/derived-values-change.if.d.ts +30 -0
  59. package/lib/table/data/tablemodel/column/column-def.d.ts +31 -1
  60. package/lib/table/data/tablemodel/column/column-def.if.d.ts +86 -0
  61. package/lib/table/event-adapter.d.ts +7 -0
  62. package/lib/table/event-listener.if.d.ts +32 -0
  63. package/lib/table/factory/pivot-factory.d.ts +63 -0
  64. package/lib/table/factory/pivot-table-model-para.if.d.ts +44 -0
  65. package/lib/table/factory/row-group-factory.d.ts +70 -0
  66. package/lib/table/factory/row-group-table-model-para.if.d.ts +66 -0
  67. package/lib/table/factory/table-factory.d.ts +56 -1
  68. package/lib/table/fill/compute-fill-changes.d.ts +67 -0
  69. package/lib/table/fill/fill-direction.type.d.ts +7 -0
  70. package/lib/table/fill/fill-handle-options.d.ts +29 -0
  71. package/lib/table/fill/fill-handle-options.if.d.ts +35 -0
  72. package/lib/table/fill/fill-handle-service.d.ts +55 -0
  73. package/lib/table/fill/fill-series.d.ts +60 -0
  74. package/lib/table/fill/fill-value-context.if.d.ts +36 -0
  75. package/lib/table/formula/compile-formula.d.ts +86 -0
  76. package/lib/table/formula/evaluator.d.ts +19 -0
  77. package/lib/table/formula/formula-ast.if.d.ts +42 -0
  78. package/lib/table/formula/formula-error.d.ts +59 -0
  79. package/lib/table/formula/formula-options.if.d.ts +41 -0
  80. package/lib/table/formula/formula-values.d.ts +13 -0
  81. package/lib/table/formula/functions.d.ts +18 -0
  82. package/lib/table/formula/parser.d.ts +10 -0
  83. package/lib/table/formula/tokenizer.d.ts +22 -0
  84. package/lib/table/instanceof-workaround.d.ts +143 -6
  85. package/lib/table/pivot/area-model-pivot-header.d.ts +9 -0
  86. package/lib/table/pivot/pivot-column-def.d.ts +28 -0
  87. package/lib/table/pivot/pivot-column-info.if.d.ts +25 -0
  88. package/lib/table/pivot/pivot-config.if.d.ts +58 -0
  89. package/lib/table/pivot/pivot-internals.d.ts +13 -0
  90. package/lib/table/pivot/pivot-layout.d.ts +40 -0
  91. package/lib/table/pivot/pivot-result.d.ts +157 -0
  92. package/lib/table/pivot/pivot-table-model.d.ts +86 -0
  93. package/lib/table/pivot/pivot-value.type.d.ts +80 -0
  94. package/lib/table/render-scope.d.ts +142 -4
  95. package/lib/table/renderer/aggregate-cell-renderer.d.ts +27 -0
  96. package/lib/table/renderer/edit/input-cell-renderer.d.ts +1 -1
  97. package/lib/table/renderer/edit/select-cell-renderer.d.ts +1 -1
  98. package/lib/table/renderer/row-number-cell-renderer.d.ts +27 -0
  99. package/lib/table/row-drag/move-rows.d.ts +157 -0
  100. package/lib/table/row-drag/row-drag-options.d.ts +28 -0
  101. package/lib/table/row-drag/row-drag-options.if.d.ts +36 -0
  102. package/lib/table/row-drag/row-drag-service.d.ts +130 -0
  103. package/lib/table/row-drag/row-drop-target.if.d.ts +29 -0
  104. package/lib/table/rowgroup/group-rows-internals.d.ts +14 -0
  105. package/lib/table/rowgroup/group-rows-options.if.d.ts +13 -0
  106. package/lib/table/rowgroup/group-rows.d.ts +48 -0
  107. package/lib/table/rowgroup/row-group-column-def.d.ts +26 -0
  108. package/lib/table/rowgroup/row-group-column-options.if.d.ts +27 -0
  109. package/lib/table/rowgroup/row-group-detail-row.d.ts +8 -0
  110. package/lib/table/rowgroup/row-group-field.type.d.ts +56 -0
  111. package/lib/table/rowgroup/row-group-internals.d.ts +31 -0
  112. package/lib/table/rowgroup/row-group-layout.d.ts +13 -0
  113. package/lib/table/rowgroup/row-group-model-options.if.d.ts +39 -0
  114. package/lib/table/rowgroup/row-group.d.ts +51 -0
  115. package/lib/table/rowgroup/row-group.if.d.ts +48 -0
  116. package/lib/table/selection/selection-aggregator.d.ts +37 -0
  117. package/lib/table/selection/selection-service.d.ts +7 -0
  118. package/lib/table/service/convenience-dom.service.d.ts +5 -0
  119. package/lib/table/service/excel-service.d.ts +6 -0
  120. package/lib/table/service/excel-service.if.d.ts +35 -0
  121. package/lib/table/table-api.d.ts +389 -3
  122. package/lib/table/table-scope.d.ts +140 -0
  123. package/package.json +3 -2
@@ -0,0 +1,47 @@
1
+ import { AreaModel } from './area-model.js';
2
+ import { HasDefaultRowHeightIf } from './has-default-row-height-if.js';
3
+ import { CellRendererIf } from '../../../renderer/cell-render.if.js';
4
+ import { AreaModelPivot } from './area-model-pivot.js';
5
+
6
+ /**
7
+ * The total row of a pivot table: the label `"Total"` in the group column and the column totals (cells, subtotals,
8
+ * grand total) of the {@link AreaModelPivot}. The totals come from the source rows (merged states), never from the
9
+ * displayed rows – an average stays the average of all rows. Created by {@link PivotFactory.createTableModel}
10
+ * (`totalRow: false` → no row).
11
+ *
12
+ * @example
13
+ * const footer = tableModel.getAreaModel("footer") as AreaModelPivotFooter;
14
+ * footer.getValueAt(0, pivotModel.getPivotBody().getColumnIndex([])); // 450 – the grand total
15
+ *
16
+ * @since 1.1.94
17
+ * @category Pivot
18
+ */
19
+ export declare class AreaModelPivotFooter<T = any> extends AreaModel implements HasDefaultRowHeightIf {
20
+ private readonly body;
21
+ defaultRowHeight: number;
22
+ /** Marker (works across copies of the core). */
23
+ readonly type = "AreaModelPivotFooter";
24
+ private readonly textRenderer;
25
+ /**
26
+ * @param {AreaModelPivot<T>} body - the pivot body (its columns and totals)
27
+ * @param {number} [defaultRowHeight=34] - row height in px
28
+ */
29
+ constructor(body: AreaModelPivot<T>, defaultRowHeight?: number);
30
+ /** 1 – or 0 with `totalRow: false` or without row fields (the body shows the total then). */
31
+ getRowCount(): number;
32
+ getRowHeight(_rowIndex: number): number;
33
+ getConstantRowHeight(): number | undefined;
34
+ getValueAt(rowIndex: number, columnIndex: number): any;
35
+ getTextValueAt(rowIndex: number, columnIndex: number): string;
36
+ getCellRenderer(_rowIndex: number, columnIndex: number): CellRendererIf | undefined;
37
+ getCustomClassesAt(rowIndex: number, columnIndex: number): string[];
38
+ /**
39
+ * True for the label cell ("Total") – the table gives it `role="rowheader"`.
40
+ *
41
+ * @param {number} rowIndex - footer row
42
+ * @param {number} columnIndex - column index
43
+ * @returns {boolean} true for the group column of the total row
44
+ */
45
+ isRowHeaderCell(rowIndex: number, columnIndex: number): boolean;
46
+ private isLabelColumn;
47
+ }
@@ -0,0 +1,281 @@
1
+ import { AreaModelTree } from './area-model-tree.js';
2
+ import { RowDropTargetIf } from '../../../row-drag/row-drop-target.if.js';
3
+ import { ColumnDefIf } from '../column/column-def.if.js';
4
+ import { CellRendererIf } from '../../../renderer/cell-render.if.js';
5
+ import { FilterFunction } from '../../common/filter-function.js';
6
+ import { SortItem } from '../../common/sort-item.js';
7
+ import { RowGroupIf } from '../../../rowgroup/row-group.if.js';
8
+ import { PivotConfigIf } from '../../../pivot/pivot-config.if.js';
9
+ import { PivotColumnInfoIf } from '../../../pivot/pivot-column-info.if.js';
10
+ import { PivotResult } from '../../../pivot/pivot-result.js';
11
+ import { RowGroupColumnOptionsIf } from '../../../rowgroup/row-group-column-options.if.js';
12
+
13
+ /**
14
+ * The body of a pivot table: one row per row group (a tree for several row fields, with subtotals in the group rows),
15
+ * one column per column key × value, plus subtotal and total columns. Created by {@link PivotFactory.createTableModel}
16
+ * – the factory also builds the grouped header and the total row.
17
+ *
18
+ * It is a tree ({@link AreaModelTree}, `isAreaModelTree` is true): rows are tree rows with a {@link RowGroupIf} as
19
+ * `data`; expanding, `tableApi.expandAll()`, keyboard, screen reader attributes and remembering expanded rows work as
20
+ * in a tree table. The cells come from a {@link PivotResult} ({@link getResult}).
21
+ *
22
+ * - **Filter**: `externalFilterFunction` gets your **source objects** (as in a flat table and a row grouping) – it
23
+ * filters before the aggregation; all cells and totals follow. Rows without a matching source row disappear; the
24
+ * columns stay (they come from all rows).
25
+ * - **Sorting**: by a value column → the rows of every level by that value (empty cells last); by the group column →
26
+ * by key. `tableApi.sort(fn)` compares two {@link RowGroupIf}.
27
+ * - Cells are not editable. Changed source data: {@link TableApi.setPivotConfig} with new rows or `tableApi.repaint()` (same
28
+ * keys), new keys or fields: {@link TableApi.setPivotConfig}.
29
+ *
30
+ * @example
31
+ * const tableModel = PivotFactory.createTableModel<Sale>({
32
+ * rows: sales, rowFields: ["region"], columnFields: ["monat"], values: ["umsatz"]
33
+ * });
34
+ * const pivotBody = tableModel.getPivotBody();
35
+ * pivotBody.getValueAt(0, pivotBody.getColumnIndex(["Jan"])); // 150 – Nord, Jan
36
+ *
37
+ * @see Training: [Pivot tables](https://gui.expert/training/pivot-tables/)
38
+ *
39
+ * @typeParam T - the row type
40
+ * @since 1.1.94
41
+ * @category Pivot
42
+ */
43
+ export declare class AreaModelPivot<T = any> extends AreaModelTree<any> {
44
+ /** Marker for {@link isAreaModelPivot} (works across copies of the core). `type` stays `"AreaModelTree"`. */
45
+ readonly kind = "AreaModelPivot";
46
+ private config;
47
+ private sourceRows;
48
+ private result;
49
+ private rowFields;
50
+ private groupColumnOptions;
51
+ private readonly textRenderer;
52
+ private infoByProperty;
53
+ private userFilter?;
54
+ private accepted?;
55
+ private dirty;
56
+ private builds;
57
+ /**
58
+ * @param {T[]} rows - the source rows (not changed)
59
+ * @param {PivotConfigIf<T>} config - row fields, column fields, values
60
+ * @param {Partial<RowGroupColumnOptionsIf<T>>} [groupColumn] - label options of the group column (`showCount`, `label`)
61
+ * @param {number} [defaultRowHeight=34] - row height in px
62
+ */
63
+ constructor(rows: T[], config: PivotConfigIf<T>, groupColumn?: Partial<RowGroupColumnOptionsIf<T>>, defaultRowHeight?: number);
64
+ /**
65
+ * The current configuration.
66
+ *
67
+ * @returns {PivotConfigIf<T>} a copy
68
+ *
69
+ * @example
70
+ * pivotBody.getConfig().columnFields; // ["monat"]
71
+ */
72
+ getConfig(): PivotConfigIf<T>;
73
+ /**
74
+ * The pivot: row and column hierarchies and all values (also subtotals and totals) – for drill-down, own
75
+ * evaluations or a chart next to the table.
76
+ *
77
+ * @returns {PivotResult<T>} the result (do not modify)
78
+ *
79
+ * @example
80
+ * const result = pivotBody.getResult();
81
+ * result.columnLeaves.map(c => c.label); // ["Jan", "Feb"]
82
+ * result.getValue(); // 450
83
+ */
84
+ getResult(): PivotResult<T>;
85
+ /**
86
+ * The rows the pivot is built from.
87
+ *
88
+ * @returns {T[]} the source rows (do not modify the array – use `tableApi.setPivotConfig({}, rows)`)
89
+ *
90
+ * @example
91
+ * pivotBody.getSourceRows().length; // 6
92
+ */
93
+ getSourceRows(): T[];
94
+ /**
95
+ * Counts how often the pivot was built (start, new configuration, new rows).
96
+ *
97
+ * @returns {number} the number of builds
98
+ *
99
+ * @example
100
+ * const before = pivotBody.getBuildCount();
101
+ */
102
+ getBuildCount(): number;
103
+ /**
104
+ * Builds the pivot again (keys, rows) – {@link PivotTableModel.setPivotConfig} calls it, followed
105
+ * by the new columns ({@link setColumnDefs}). Expanded rows whose path stays keep their state; the filter stays, the
106
+ * sorting is reset.
107
+ *
108
+ * @param {PivotConfigIf<T>} config - the configuration
109
+ * @param {T[]} [rows] - new source rows (default: the current ones)
110
+ * @internal
111
+ */
112
+ configure(config: PivotConfigIf<T>, rows?: T[]): void;
113
+ /**
114
+ * Sets the columns (group column, value columns) after a new layout. @internal
115
+ *
116
+ * @param {ColumnDefIf[]} columnDefs - all columns of the table
117
+ */
118
+ setColumnDefs(columnDefs: ColumnDefIf[]): void;
119
+ /**
120
+ * The property of a column (follows moved columns).
121
+ *
122
+ * @param {number} columnIndex - column index
123
+ * @returns {string | undefined} the property, e.g. {@link RowGroupColumnDef.PROPERTY} for the group column
124
+ *
125
+ * @example
126
+ * pivotBody.getPropertyAt(0); // "RowGroupColumn"
127
+ */
128
+ getPropertyAt(columnIndex: number): string | undefined;
129
+ /**
130
+ * The row group of a displayed row.
131
+ *
132
+ * @param {number} rowIndex - index of the displayed body row
133
+ * @returns {RowGroupIf<T> | undefined} the group, undefined for the total row of a pivot without row fields
134
+ *
135
+ * @example
136
+ * pivotBody.getRowGroup(0)?.path; // ["Nord"]
137
+ */
138
+ getRowGroup(rowIndex: number): RowGroupIf<T> | undefined;
139
+ /**
140
+ * What a column shows: a column key, a subtotal or a total, and which value.
141
+ *
142
+ * @param {number} columnIndex - column index
143
+ * @returns {PivotColumnInfoIf | undefined} undefined for the group column (and row numbers)
144
+ *
145
+ * @example
146
+ * pivotBody.getColumnInfo(1)?.column?.label; // "Jan"
147
+ */
148
+ getColumnInfo(columnIndex: number): PivotColumnInfoIf | undefined;
149
+ /**
150
+ * The property of a value column – for `tableApi.getColumnIndex()`, `sortByColumn()`, `setColumnWidth()` …
151
+ *
152
+ * @param {unknown[]} columnPath - the column keys from the top column field down, e.g. `[2026, 1]`; `[]` = the total column
153
+ * @param {number | string} [value=0] - the index or name of the value
154
+ * @returns {string | undefined} the property, undefined if there is no such column
155
+ *
156
+ * @example
157
+ * tableApi.sortByColumn(pivotBody.getColumnProperty([])!, "desc"); // rows by their total, largest first
158
+ */
159
+ getColumnProperty(columnPath: unknown[], value?: number | string): string | undefined;
160
+ /**
161
+ * The index of a value column (see {@link getColumnProperty}).
162
+ *
163
+ * @param {unknown[]} columnPath - the column keys, `[]` = the total column
164
+ * @param {number | string} [value=0] - the index or name of the value
165
+ * @returns {number} the column index or -1
166
+ *
167
+ * @example
168
+ * pivotBody.getColumnIndex(["Jan"]); // 1
169
+ */
170
+ getColumnIndex(columnPath: unknown[], value?: number | string): number;
171
+ /**
172
+ * The source rows behind a cell (drill-down): the rows of its row group and column key that the filter keeps.
173
+ *
174
+ * @param {number} rowIndex - index of the displayed body row
175
+ * @param {number} columnIndex - column index (the group column: all rows of the row group)
176
+ * @returns {T[]} a new array in source order
177
+ *
178
+ * @example
179
+ * pivotBody.getCellSourceRows(0, 1).map(s => s.umsatz); // [100, 50] – Nord, Jan
180
+ */
181
+ getCellSourceRows(rowIndex: number, columnIndex: number): T[];
182
+ getValueAt(rowIndex: number, columnIndex: number): any;
183
+ /**
184
+ * The value of a pivot cell as shown: the aggregate, or with „Show values as“ ({@link PivotValueIf.showValuesAs})
185
+ * its share of the row total, column total, grand total or parent row.
186
+ *
187
+ * @param {RowGroupIf<T> | undefined} group - the row group; `undefined` = all rows (the total row)
188
+ * @param {PivotColumnInfoIf} info - the column ({@link getColumnInfo})
189
+ * @returns {unknown} the shown value, `undefined` for an empty cell
190
+ *
191
+ * @example
192
+ * const body = (tableModel as PivotTableModel).getPivotBody();
193
+ * body.getShownValue(body.getRowGroup(0), body.getColumnInfo(2)!); // 0.4 – 40 % of the row
194
+ *
195
+ * @since 1.1.95
196
+ */
197
+ getShownValue(group: RowGroupIf<T> | undefined, info: PivotColumnInfoIf): unknown;
198
+ /**
199
+ * Row drag: `false` – the table does not move rows of a pivot table itself (pivot rows are aggregates). With
200
+ * `rowDragOptions.managed: false` the app gets the events and decides.
201
+ *
202
+ * @returns {boolean} always false
203
+ * @since 1.1.95
204
+ */
205
+ canMoveRows(_rowIndices: number[], _target: RowDropTargetIf): boolean;
206
+ /**
207
+ * Row drag: does nothing (see {@link canMoveRows}).
208
+ *
209
+ * @returns {boolean} always false
210
+ * @since 1.1.95
211
+ */
212
+ moveRows(_rowIndices: number[], _target: RowDropTargetIf): boolean;
213
+ getTextValueAt(rowIndex: number, columnIndex: number): string;
214
+ /** Value columns: the {@link AggregateCellRenderer} (text node); the group column: none (label as text). */
215
+ getCellRenderer(_rowIndex: number, columnIndex: number): CellRendererIf | undefined;
216
+ getCustomClassesAt(rowIndex: number, columnIndex: number): string[];
217
+ /**
218
+ * True for the label cell of a row (the table gives it `role="rowheader"`).
219
+ *
220
+ * @param {number} rowIndex - index of the displayed body row
221
+ * @param {number} columnIndex - column index
222
+ * @returns {boolean} true for the group column
223
+ */
224
+ isRowHeaderCell(rowIndex: number, columnIndex: number): boolean;
225
+ /** Pivot cells are computed – not editable. */
226
+ isEditable(_rowIndex: number, _columnIndex: number): boolean;
227
+ /** Pivot cells are computed – `setValue` does nothing (`false`). */
228
+ setValue(_rowIndex: number, _columnIndex: number, _value: any): boolean;
229
+ /**
230
+ * Filters the **source rows** before the aggregation: `predictFn` gets your objects (as in a flat table), index and
231
+ * array of the source rows. All cells and totals follow; rows without a matching source row disappear. Usually
232
+ * called by `tableApi.externalFilterChanged()` with `tableOptions.externalFilterFunction`.
233
+ *
234
+ * @param {FilterFunction<S>} predictFn - returns a truthy value for the rows to count
235
+ *
236
+ * @example
237
+ * tableOptions.externalFilterFunction = (sale: Sale) => sale.produkt === "A";
238
+ * tableApi.externalFilterChanged(); // Nord: Jan 100, Feb 120 · Süd: Jan 80, Feb 30
239
+ */
240
+ externalFilterChanged<S>(predictFn: FilterFunction<S>): void;
241
+ /**
242
+ * Sorts the rows of every level: by a value column by its values (empty cells last, in both directions), by the
243
+ * group column by key. A new pivot ({@link TableApi.setPivotConfig}) starts unsorted (the columns are different).
244
+ *
245
+ * @param {SortItem[]} sortItems - primary key first
246
+ * @returns {boolean} false for an empty list
247
+ *
248
+ * @example
249
+ * pivotBody.doSort([new SortItem(pivotBody.getColumnIndex([]), "desc")]); // by the total, largest first
250
+ * tableApi.repaint();
251
+ */
252
+ doSort(sortItems: SortItem[]): boolean;
253
+ /**
254
+ * Sorts the rows of every level with your function – it gets two {@link RowGroupIf} (siblings).
255
+ *
256
+ * @param {(a: any, b: any) => number} compareFn - compares two row groups
257
+ *
258
+ * @example
259
+ * tableApi.sort((a: RowGroupIf, b: RowGroupIf) => b.count - a.count); // most source rows first
260
+ */
261
+ sort(compareFn: (a: any, b: any) => number): void;
262
+ /**
263
+ * Computes the cells again on the next read – the table calls it after filter, sorting and `tableApi.repaint()`.
264
+ *
265
+ * @example
266
+ * sales[0].umsatz = 999; // changed outside the table
267
+ * tableApi.repaint(); // calls invalidateDerivedValues() – all cells are computed again
268
+ */
269
+ invalidateDerivedValues(): void;
270
+ private isSubtotalSuppressed;
271
+ /** The label of a row in the group column. */
272
+ protected getGroupLabel(group: RowGroupIf<T>): string;
273
+ private ensureComputed;
274
+ /** The filter result per source row (1 = counted). */
275
+ private acceptedBy;
276
+ private createRowComparator;
277
+ /** (Re)builds the pivot and the tree rows; keeps the expanded state of known row groups and the filter (a new pivot is unsorted). */
278
+ private build;
279
+ private createRow;
280
+ private isExpandedByDefault;
281
+ }
@@ -0,0 +1,336 @@
1
+ import { AreaModelTree } from './area-model-tree.js';
2
+ import { RowDropTargetIf } from '../../../row-drag/row-drop-target.if.js';
3
+ import { TreeRowIf } from '../../common/tree-row-if.js';
4
+ import { ColumnDefIf } from '../column/column-def.if.js';
5
+ import { CellRendererIf } from '../../../renderer/cell-render.if.js';
6
+ import { FilterFunction } from '../../common/filter-function.js';
7
+ import { SortItem } from '../../common/sort-item.js';
8
+ import { DerivedValuesChangeIf } from './derived-values-change.if.js';
9
+ import { RowGroupField } from '../../../rowgroup/row-group-field.type.js';
10
+ import { RowGroupModelOptionsIf } from '../../../rowgroup/row-group-model-options.if.js';
11
+ import { RowGroup } from '../../../rowgroup/row-group.js';
12
+
13
+ /**
14
+ * The body of a row grouping: flat rows grouped by one or more levels, each group a row with an arrow, a label
15
+ * (`"Nord (3)"`) and aggregates per column (sum, average, …); below it the sub-groups or the original rows. Usually
16
+ * created by {@link RowGroupFactory.createTableModel}, which also adds the group column, hides the grouped columns and
17
+ * adds a total row.
18
+ *
19
+ * It is a tree ({@link AreaModelTree}, `isAreaModelTree` is true): group rows are {@link TreeRowIf tree rows} with a
20
+ * {@link RowGroupIf} as `data`, detail rows hold your objects. Expanding, `tableApi.expandAll()`, keyboard, screen
21
+ * reader attributes and remembering expanded groups (`autoRestoreOptions`) work as in a tree table.
22
+ *
23
+ * - **Filter**: `externalFilterFunction` gets your object (as in a flat table) – never a group. Groups without a match
24
+ * disappear; aggregates and counts follow the filter.
25
+ * - **Sorting**: by an aggregated column → groups by aggregate, rows by value; by the group column → groups by key;
26
+ * `tableApi.sort(fn)` sorts the rows inside their groups with `fn(a, b)` on your objects.
27
+ * - **Editing**: group rows are not editable. A changed value recomputes only the groups of that row; a changed
28
+ * grouped value moves the row to its new group.
29
+ * - **Regrouping**: {@link TableApi.setRowGroupBy} (or {@link setGroupBy} + `tableApi.repaintHard()`) – expanded
30
+ * groups, filter, sorting and checked rows stay.
31
+ * - Aggregates are computed when a group row is read after a change – once, not per repaint or scroll frame.
32
+ *
33
+ * @example
34
+ * const tableModel = RowGroupFactory.createTableModel<Sale>({
35
+ * rows: sales, columnDefs, groupBy: ["region", "produkt"], aggregations: { umsatz: "sum" }
36
+ * });
37
+ * const body = tableModel.getBodyModel() as AreaModelRowGroups<Sale>;
38
+ * body.getGroups().map(g => g.results.umsatz); // [270, 180]
39
+ *
40
+ * @see Training: [Row grouping](https://gui.expert/training/row-grouping/)
41
+ *
42
+ * @typeParam T - the row type
43
+ * @since 1.1.94
44
+ * @category Row grouping
45
+ */
46
+ export declare class AreaModelRowGroups<T = any> extends AreaModelTree<any> {
47
+ /** Marker for {@link isAreaModelRowGroups} (works across copies of the core). `type` stays `"AreaModelTree"`. */
48
+ readonly kind = "AreaModelRowGroups";
49
+ /** The options (groupBy as set by {@link setGroupBy}). */
50
+ readonly options: RowGroupModelOptionsIf<T>;
51
+ /** Properties of the columns the factory hid for the current grouping. @internal */
52
+ hiddenColumnProperties: string[];
53
+ private sourceRows;
54
+ private fields;
55
+ private groups;
56
+ private readonly specs;
57
+ private readonly specByKey;
58
+ private readonly textRenderer;
59
+ /** the tree row of every group by id */
60
+ private groupRowById;
61
+ /** the tree row of every source row by source index */
62
+ private detailRows;
63
+ /** the filter of the user (gets the objects) and its result per source index */
64
+ private userFilter?;
65
+ private accepted?;
66
+ private allDirty;
67
+ private readonly dirtyLeaves;
68
+ private totals;
69
+ private lastSort?;
70
+ private batchDepth;
71
+ private regroupPending;
72
+ private builds;
73
+ /**
74
+ * @param {T[]} rows - the rows (not changed by the model)
75
+ * @param {ColumnDefIf[]} columnDefs - the columns of the table, usually with a {@link RowGroupColumnDef}
76
+ * @param {RowGroupModelOptionsIf<T>} options - levels, aggregations, expanded state …
77
+ * @param {number} [defaultRowHeight=34] - row height in px
78
+ */
79
+ constructor(rows: T[], columnDefs: ColumnDefIf[], options: RowGroupModelOptionsIf<T>, defaultRowHeight?: number);
80
+ /**
81
+ * The rows the groups are built from.
82
+ *
83
+ * @returns {T[]} the source rows (do not modify the array – use {@link setRows})
84
+ *
85
+ * @example
86
+ * const all = body.getSourceRows().length;
87
+ */
88
+ getSourceRows(): T[];
89
+ /**
90
+ * Replaces the rows and groups them again (expanded groups, filter and sorting stay). Repaint with
91
+ * `tableApi.repaintHard()` – the row count changes.
92
+ *
93
+ * @param {T[]} rows - the new rows
94
+ *
95
+ * @example
96
+ * body.setRows(await loadSales());
97
+ * tableApi.repaintHard();
98
+ */
99
+ setRows(rows: T[]): void;
100
+ /**
101
+ * The current levels.
102
+ *
103
+ * @returns {RowGroupField<T>[]} a copy of the levels
104
+ *
105
+ * @example
106
+ * body.getGroupBy(); // ["region", "produkt"]
107
+ */
108
+ getGroupBy(): RowGroupField<T>[];
109
+ /**
110
+ * Groups the rows by other levels. Expanded groups whose path stays keep their state; filter, sorting and checked
111
+ * rows stay. An empty list shows the rows without groups. Prefer {@link TableApi.setRowGroupBy}: it also shows and
112
+ * hides the grouped columns and repaints.
113
+ *
114
+ * @param {RowGroupField<T>[]} groupBy - the new levels, top first
115
+ *
116
+ * @example
117
+ * body.setGroupBy(["produkt"]);
118
+ * tableApi.repaintHard();
119
+ */
120
+ setGroupBy(groupBy: RowGroupField<T>[]): void;
121
+ /**
122
+ * Builds the groups again from the current rows – after grouped values were changed outside the table (inside the
123
+ * table, editing a grouped value moves the row by itself). Repaint with `tableApi.repaintHard()`.
124
+ *
125
+ * @example
126
+ * sales[0].region = "Süd"; // a live update of a grouped value
127
+ * body.regroup();
128
+ * tableApi.repaintHard();
129
+ */
130
+ regroup(): void;
131
+ /**
132
+ * The groups of the top level (with sub-groups, counts and aggregates).
133
+ *
134
+ * @returns {RowGroup<T>[]} the groups in display order (do not modify)
135
+ *
136
+ * @example
137
+ * body.getGroups().map(g => `${g.label}: ${g.results.umsatz}`); // ["Nord: 270", "Süd: 180"]
138
+ */
139
+ getGroups(): RowGroup<T>[];
140
+ /**
141
+ * The group with the given keys.
142
+ *
143
+ * @param {unknown[]} path - the keys from the top level down, e.g. `["Nord", "A"]`
144
+ * @returns {RowGroup<T> | undefined} the group, undefined if there is none
145
+ *
146
+ * @example
147
+ * body.findGroup(["Nord", "A"])?.results.umsatz; // 220
148
+ */
149
+ findGroup(path: unknown[]): RowGroup<T> | undefined;
150
+ /**
151
+ * The group of a displayed row.
152
+ *
153
+ * @param {number} rowIndex - index of the displayed body row
154
+ * @returns {RowGroup<T> | undefined} the group of a group row, undefined for a detail row
155
+ *
156
+ * @example
157
+ * const group = body.getRowGroup(0); // the first group (if the first row is a group row)
158
+ */
159
+ getRowGroup(rowIndex: number): RowGroup<T> | undefined;
160
+ /**
161
+ * Your object of a displayed detail row.
162
+ *
163
+ * @param {number} rowIndex - index of the displayed body row
164
+ * @returns {T | undefined} the object, undefined for a group row
165
+ *
166
+ * @example
167
+ * const sale = body.getDetailRow(1);
168
+ */
169
+ getDetailRow(rowIndex: number): T | undefined;
170
+ /**
171
+ * True if the displayed row is a group row.
172
+ *
173
+ * @param {number} rowIndex - index of the displayed body row
174
+ * @returns {boolean} true for a group row
175
+ *
176
+ * @example
177
+ * const bold = body.isGroupRowIndex(rowIndex);
178
+ */
179
+ isGroupRowIndex(rowIndex: number): boolean;
180
+ /**
181
+ * The aggregates over all rows the filter keeps (the same as the total row).
182
+ *
183
+ * @returns {Record<string, unknown>} key → result
184
+ *
185
+ * @example
186
+ * body.getTotals().umsatz; // 450
187
+ */
188
+ getTotals(): Record<string, unknown>;
189
+ /**
190
+ * The checked objects (detail rows) – with a checkbox column or `showCheckboxWihoutExtraColumn`.
191
+ *
192
+ * @returns {T[]} the checked objects in source order
193
+ *
194
+ * @example
195
+ * const selected = body.getCheckedRows();
196
+ */
197
+ getCheckedRows(): T[];
198
+ /**
199
+ * Expands the groups above a level and collapses the others: `0` collapses all, `1` opens the top level, a large
200
+ * number opens all. Repaint with `tableApi.repaintHard()`.
201
+ *
202
+ * @param {number} level - the number of open levels
203
+ *
204
+ * @example
205
+ * body.expandToLevel(1);
206
+ * tableApi.repaintHard();
207
+ */
208
+ expandToLevel(level: number): void;
209
+ /**
210
+ * Counts how often the groups were built (start, {@link setRows}, {@link setGroupBy}, {@link regroup}, an edited
211
+ * grouped value). The table compares it before and after an edit to know that the rows moved (→ hard repaint).
212
+ *
213
+ * @returns {number} the number of builds
214
+ *
215
+ * @example
216
+ * const before = body.getBuildCount();
217
+ * body.setValue(1, 1, "Ost");
218
+ * const moved = body.getBuildCount() !== before; // true: "region" is grouped
219
+ */
220
+ getBuildCount(): number;
221
+ /**
222
+ * Runs several `setValue` calls as one change: a row whose grouped value changes moves only at the end, so the row
223
+ * indices of the other calls stay valid (`tableApi.updateCells()` uses it).
224
+ *
225
+ * @param {() => void} fn - the changes
226
+ * @returns {boolean} true if the groups were built again at the end
227
+ *
228
+ * @example
229
+ * const moved = body.batch(() => {
230
+ * body.setValue(1, 1, "Süd"); // region – moves the row
231
+ * body.setValue(2, 4, 999); // still the row that was at index 2
232
+ * });
233
+ */
234
+ batch(fn: () => void): boolean;
235
+ /** Group rows: the label resp. the aggregate of the column; detail rows: the value of the row (calculated or path). */
236
+ protected getRawValueOfRow(row: TreeRowIf<any>, property: string): any;
237
+ /**
238
+ * Row drag: `false` – the table does not move rows of a grouped table itself (group rows are built from the data, a change of group means changing data). With
239
+ * `rowDragOptions.managed: false` the app gets the events and decides.
240
+ *
241
+ * @returns {boolean} always false
242
+ * @since 1.1.95
243
+ */
244
+ canMoveRows(_rowIndices: number[], _target: RowDropTargetIf): boolean;
245
+ /**
246
+ * Row drag: does nothing (see {@link canMoveRows}).
247
+ *
248
+ * @returns {boolean} always false
249
+ * @since 1.1.95
250
+ */
251
+ moveRows(_rowIndices: number[], _target: RowDropTargetIf): boolean;
252
+ getTextValueAt(rowIndex: number, columnIndex: number): string;
253
+ /** Group rows: the {@link AggregateCellRenderer} for aggregates, no renderer otherwise; detail rows: the column's. */
254
+ getCellRenderer(rowIndex: number, columnIndex: number): CellRendererIf | undefined;
255
+ getCustomClassesAt(rowIndex: number, columnIndex: number): string[];
256
+ /**
257
+ * True for the label cell of a group row (the table gives it `role="rowheader"`).
258
+ *
259
+ * @param {number} rowIndex - index of the displayed body row
260
+ * @param {number} columnIndex - column index
261
+ * @returns {boolean} true for the group label
262
+ */
263
+ isGroupLabelCell(rowIndex: number, columnIndex: number): boolean;
264
+ /** Group rows and the group column are not editable. */
265
+ isEditable(rowIndex: number, columnIndex: number): boolean;
266
+ /**
267
+ * Sets a value of a detail row (group rows: `false`). Only the groups of that row are computed again; if a grouped
268
+ * value changed, the row moves to its new group (the row count may change).
269
+ */
270
+ setValue(rowIndex: number, columnIndex: number, value: any): boolean;
271
+ /**
272
+ * Filters the rows: `predictFn` gets **your objects** (as in a flat table), index and array of the source rows –
273
+ * never a group. Groups without a matching row disappear (also below collapsed groups); counts and aggregates follow
274
+ * the filter. Usually called by `tableApi.externalFilterChanged()` with `tableOptions.externalFilterFunction`.
275
+ *
276
+ * @param {FilterFunction<S>} predictFn - returns a truthy value for the rows to show
277
+ *
278
+ * @example
279
+ * tableOptions.externalFilterFunction = (sale: Sale) => sale.monat === "Jan";
280
+ * tableApi.externalFilterChanged(); // Nord (2) 150 · Süd (1) 80
281
+ */
282
+ externalFilterChanged<S>(predictFn: FilterFunction<S>): void;
283
+ /**
284
+ * Sorts groups and rows (see the class description); the order is applied again after regrouping.
285
+ *
286
+ * @param {SortItem[]} sortItems - primary key first
287
+ * @returns {boolean} false for an empty list
288
+ *
289
+ * @example
290
+ * body.doSort([new SortItem(tableApi.getColumnIndex("umsatz"), "desc")]); // groups by sum, rows by value
291
+ * tableApi.repaint();
292
+ */
293
+ doSort(sortItems: SortItem[]): boolean;
294
+ /**
295
+ * Sorts the rows inside their groups with your function – it gets your objects, as in a flat table. The groups keep
296
+ * their order. Used by `tableApi.sort(fn)`.
297
+ *
298
+ * @param {(a: any, b: any) => number} compareFn - compares two of your objects
299
+ *
300
+ * @example
301
+ * tableApi.sort((a: Sale, b: Sale) => b.umsatz - a.umsatz);
302
+ */
303
+ sort(compareFn: (a: any, b: any) => number): void;
304
+ /**
305
+ * Drops the computed aggregates; with `change` (an edit or `updateCells`) only those of the groups of the changed rows.
306
+ * The table calls it; the next read computes them again.
307
+ *
308
+ * @param {DerivedValuesChangeIf} [change] - the changed rows
309
+ *
310
+ * @example
311
+ * sales[0].umsatz = 999; // changed outside the table
312
+ * tableApi.repaint(); // calls invalidateDerivedValues() – all groups are computed again
313
+ */
314
+ invalidateDerivedValues(change?: DerivedValuesChangeIf): void;
315
+ /** After sorting the tree rows: the groups (`getGroups()`, `children`) in display order. */
316
+ /** An aggregate over a calculated column (key or value = its property) uses the computed value. */
317
+ private withCalculatedValue;
318
+ /** A grouping level on a calculated column groups by the computed value. */
319
+ private withCalculatedKeys;
320
+ private syncGroupOrder;
321
+ /** The label of a group row in the group column. */
322
+ protected getGroupLabel(group: RowGroup<T>): string;
323
+ /** Computes what is missing: everything after a change of data/filter/grouping, otherwise the dirty paths. */
324
+ protected ensureAggregates(): void;
325
+ private markRowDirty;
326
+ private keysOf;
327
+ /** A comparator of two sibling rows for one sort column. */
328
+ private createRowComparator;
329
+ /**
330
+ * (Re)builds groups and tree rows from the source rows, keeps the expanded state of known groups, the checked
331
+ * detail rows, the filter and the sorting.
332
+ */
333
+ private build;
334
+ private createGroupRow;
335
+ private isExpandedByDefault;
336
+ }