@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
@@ -6,6 +6,10 @@ import { FilterFunction } from '../../common/filter-function.js';
6
6
  import { SorterService } from '../../../service/sorter.service.js';
7
7
  import { SortItem } from '../../common/sort-item.js';
8
8
  import { TreeRowIf } from '../../common/tree-row-if.js';
9
+ import { CellRendererIf } from '../../../renderer/cell-render.if.js';
10
+ import { DerivedValuesChangeIf } from './derived-values-change.if.js';
11
+ import { ShowValuesAsIf } from '../../../calc/show-values-as.type.js';
12
+ import { RowDropTargetIf } from '../../../row-drag/row-drop-target.if.js';
9
13
 
10
14
  /**
11
15
  * Represents a tree-based table model.
@@ -57,6 +61,8 @@ export declare class AreaModelTree<S> extends AbstractAreaModel<TreeRow<S>> {
57
61
  private lastPredictFn?;
58
62
  /** `row.keep` was set for the current filter (it depends on the filter and the data, not on expand/collapse). */
59
63
  private keepMarked;
64
+ /** „Show values as“: shown value per row and property (cleared on filter, sorting and invalidation). */
65
+ private showValuesCache;
60
66
  constructor(areaIdent: AreaIdent, rows: TreeRowIf<S>[], defaultRowHeight: number, columnDefs?: ColumnDefIf[]);
61
67
  changeColumnOrder(sourceColumnIndex: number, targetColumnIndex: number): void;
62
68
  /**
@@ -106,6 +112,20 @@ export declare class AreaModelTree<S> extends AbstractAreaModel<TreeRow<S>> {
106
112
  * @since 1.1.91
107
113
  */
108
114
  getDisplayedChildren(row: TreeRowIf<S>): TreeRowIf<S>[];
115
+ /**
116
+ * The leaves (rows without children) of the whole tree, depth-first in the current order – independent of the
117
+ * expanded state. Used for totals: collapsing a node does not change them, and parent rows are not counted twice.
118
+ *
119
+ * @param {boolean} [filtered=true] - only the leaves the active filter keeps (`externalFilterChanged`); `false`: all
120
+ * @returns {TreeRowIf<S>[]} a new array
121
+ *
122
+ * @example
123
+ * const treeModel = tableModel.getBodyModel() as AreaModelTree<OrderNode>;
124
+ * const total = aggregate(treeModel.getLeafRows(), row => row.data.total, "sum");
125
+ *
126
+ * @since 1.1.94
127
+ */
128
+ getLeafRows(filtered?: boolean): TreeRowIf<S>[];
109
129
  /**
110
130
  * The displayed rows: all parents expanded and accepted by the active filter, in display order.
111
131
  *
@@ -122,7 +142,84 @@ export declare class AreaModelTree<S> extends AbstractAreaModel<TreeRow<S>> {
122
142
  * @since 1.1.92
123
143
  */
124
144
  getUnfilteredRowCount(): number;
145
+ /**
146
+ * Row drag: true if the rows can be moved to the target – no filter hides a displayed row, and the target is none of
147
+ * the dragged rows and no descendant of them (no cycle). `"before"` / `"after"` = sibling of the target row,
148
+ * `"inside"` = last child.
149
+ *
150
+ * @param {number[]} rowIndices - display indices of the dragged rows
151
+ * @param {RowDropTargetIf} target - the drop target
152
+ * @returns {boolean} true if {@link moveRows} would move them
153
+ *
154
+ * @example
155
+ * const treeModel = tableModel.getBodyModel() as AreaModelTree<OrderNode>;
156
+ * treeModel.canMoveRows([0], { rowIndex: 1, position: "inside" }); // false if row 1 is a child of row 0
157
+ *
158
+ * @since 1.1.95
159
+ */
160
+ canMoveRows(rowIndices: number[], target: RowDropTargetIf): boolean;
161
+ /**
162
+ * Row drag: moves tree rows (with their children) in front of / behind the target row (siblings) or into it
163
+ * (`"inside"`: last children, the target is expanded) – in place in {@link rows} and the `children` arrays;
164
+ * `parent` and `deep` follow. See {@link canMoveRows}.
165
+ *
166
+ * @param {number[]} rowIndices - display indices of the dragged rows
167
+ * @param {RowDropTargetIf} target - the drop target (display index before the move)
168
+ * @returns {boolean} true if moved
169
+ *
170
+ * @example
171
+ * const treeModel = tableModel.getBodyModel() as AreaModelTree<OrderNode>;
172
+ * treeModel.moveRows([3], { rowIndex: 0, position: "inside" });
173
+ * tableApi.repaintHard(); // or simply tableApi.moveRows([3], { rowIndex: 0, position: "inside" })
174
+ *
175
+ * @since 1.1.95
176
+ */
177
+ moveRows(rowIndices: number[], target: RowDropTargetIf): boolean;
178
+ /** The topmost tree rows of the display indices (children travel with their parent). */
179
+ private getDragRows;
125
180
  getValueAt(rowIndex: number, columnIndex: number): any;
181
+ /**
182
+ * The value of a tree row for a property – before „Show values as“: the computed value of a calculated column,
183
+ * otherwise the property (path) of `row.data`. Subclasses (row groups) return their aggregates for group rows.
184
+ *
185
+ * @param {TreeRowIf<S>} row - the tree row
186
+ * @param {string} prop - the property of the column
187
+ * @returns {any} the raw value
188
+ * @since 1.1.95
189
+ */
190
+ protected getRawValueOfRow(row: TreeRowIf<S>, prop: string): any;
191
+ /**
192
+ * The text of a cell: for a „Show values as“ column the formatted value (`"66.7%"`), for a formula error its code;
193
+ * otherwise like before (`'' + value`).
194
+ *
195
+ * @param {number} rowIndex - the row
196
+ * @param {number} columnIndex - the column
197
+ * @returns {string} the text
198
+ */
199
+ getTextValueAt(rowIndex: number, columnIndex: number): string;
200
+ /** A „Show values as“ column without an own body renderer shows the formatted text. */
201
+ getCellRenderer(rowIndex: number, columnIndex: number): CellRendererIf | undefined;
202
+ /** The message of a formula error as tooltip. */
203
+ getTooltipAt(rowIndex: number, columnIndex: number): any;
204
+ /**
205
+ * Drops derived values: cached aggregates of formulas and „Show values as“ (called by the table on filter,
206
+ * sorting, editing, `updateCells` and `api.repaint()`).
207
+ *
208
+ * @param {DerivedValuesChangeIf} [_change] - the changed rows (not used here)
209
+ * @since 1.1.95
210
+ */
211
+ invalidateDerivedValues(_change?: DerivedValuesChangeIf): void;
212
+ /**
213
+ * „Show values as“ of one tree row: percentages of the column total relate to the (filtered) leaf rows,
214
+ * `percentOfParentTotal`, `runningTotal`, `differenceFromPrevious` and `rank` to the displayed siblings.
215
+ */
216
+ protected applyShowValuesAs(row: TreeRowIf<S>, property: string, value: unknown, options: ShowValuesAsIf): unknown;
217
+ protected readPropertyPath(t: any, property: string): unknown;
218
+ protected getAggregationSourceForValues(): {
219
+ signal: unknown;
220
+ length: number;
221
+ rows: () => ArrayLike<unknown>;
222
+ };
126
223
  getRowHeight(_rowIndex: number): number;
127
224
  /**
128
225
  * All rows share `defaultRowHeight` – unless a subclass overrides `getRowHeight()`, then row heights may
@@ -1,3 +1,5 @@
1
+ import { RowDropTargetIf } from '../../../row-drag/row-drop-target.if.js';
2
+ import { DerivedValuesChangeIf } from './derived-values-change.if.js';
1
3
  import { CellRendererIf } from '../../../renderer/cell-render.if.js';
2
4
  import { CheckedType } from '../../common/checked-type.js';
3
5
  import { CheckboxModelIf } from '../../../checkbox/checkbox-model.if.js';
@@ -280,6 +282,98 @@ export interface AreaModelIf {
280
282
  * @since 1.1.92
281
283
  */
282
284
  getUnfilteredRowCount?(): number;
285
+ /**
286
+ * Optional (row drag, managed mode): true if the rows can be moved to the target – e.g. false for a node dropped
287
+ * into its own child, or while a filter hides rows. Without {@link moveRows} the table cannot move rows itself.
288
+ *
289
+ * @param {number[]} rowIndices - display indices of the dragged rows (ascending)
290
+ * @param {RowDropTargetIf} target - the drop target (display index before the move)
291
+ * @returns {boolean} true if {@link moveRows} would move them
292
+ *
293
+ * @example
294
+ * class TasksBodyModel extends AreaModel {
295
+ * tasks: string[] = ["Write", "Test", "Ship"];
296
+ * override getRowCount(): number { return this.tasks.length; }
297
+ * override getValueAt(rowIndex: number): any { return this.tasks[rowIndex]; }
298
+ * canMoveRows(rowIndices: number[], target: RowDropTargetIf): boolean { return target.position !== "inside"; }
299
+ * moveRows(rowIndices: number[], target: RowDropTargetIf): boolean {
300
+ * return moveArrayItems(this.tasks, rowIndices, getInsertBeforeIndex(target)) > -1;
301
+ * }
302
+ * }
303
+ *
304
+ * @since 1.1.95
305
+ */
306
+ canMoveRows?(rowIndices: number[], target: RowDropTargetIf): boolean;
307
+ /**
308
+ * Optional (row drag, managed mode): moves the rows to the target in the model – the table calls it on a drop and
309
+ * for `tableApi.moveRows()`, then repaints. Implemented by `AreaModelObjectArray`, `AreaModelArrayOfArrays` and
310
+ * `AreaModelTree` (in place in their row arrays). See {@link canMoveRows} for an example.
311
+ *
312
+ * @param {number[]} rowIndices - display indices of the dragged rows (ascending)
313
+ * @param {RowDropTargetIf} target - the drop target (display index before the move)
314
+ * @returns {boolean} true if the rows were moved
315
+ *
316
+ * @since 1.1.95
317
+ */
318
+ moveRows?(rowIndices: number[], target: RowDropTargetIf): boolean;
319
+ /**
320
+ * Optional: drops cached, derived values (e.g. the totals of an {@link AreaModelAggregateFooter}). The table calls
321
+ * it for all three areas when data may have changed: filter, sorting, editing, `updateCells()` and the public
322
+ * `tableApi.repaint()` / `repaintHard()` – not on scrolling or internal repaints.
323
+ *
324
+ * After an edit and `updateCells()` the table passes `change` (since 1.1.94): the area and the displayed rows that
325
+ * changed – a model may recompute only what depends on them ({@link AreaModelRowGroups} recomputes the groups of
326
+ * these rows). Without `change` anything may have changed.
327
+ *
328
+ * @example
329
+ * class CachedTotalsFooter extends AreaModel {
330
+ * private total?: number;
331
+ * constructor(private readonly body: AreaModelObjectArray<Order>) {
332
+ * super();
333
+ * this.areaIdent = "footer";
334
+ * }
335
+ * invalidateDerivedValues(): void { this.total = undefined; }
336
+ * override getRowCount(): number { return 1; }
337
+ * override getValueAt(_row: number, _col: number): any {
338
+ * return this.total ??= this.body.getFilteredRows().reduce((sum, o) => sum + o.total, 0);
339
+ * }
340
+ * }
341
+ *
342
+ * @since 1.1.94
343
+ */
344
+ invalidateDerivedValues?(change?: DerivedValuesChangeIf): void;
345
+ /**
346
+ * Optional: true for a cell that names its row (a label such as "Nord" or "Total") – the table gives it
347
+ * `role="rowheader"` instead of `gridcell`, so screen readers announce it with the row.
348
+ *
349
+ * @example
350
+ * class LabeledBody extends AreaModel {
351
+ * isRowHeaderCell(_rowIndex: number, columnIndex: number): boolean { return columnIndex === 0; }
352
+ * }
353
+ *
354
+ * @since 1.1.94
355
+ */
356
+ isRowHeaderCell?(rowIndex: number, columnIndex: number): boolean;
357
+ /**
358
+ * Optional: true if the column with this property is calculated (`ColumnDefIf.valueGetter` / `formula`).
359
+ * Implemented by the object-array, tree, row-group and pivot models.
360
+ *
361
+ * @example
362
+ * tableModel.getBodyModel().isCalculatedProperty?.("total"); // true
363
+ *
364
+ * @since 1.1.95
365
+ */
366
+ isCalculatedProperty?(property: string): boolean;
367
+ /**
368
+ * Optional: the value of a column for a row object – computed for a calculated column, otherwise the property
369
+ * path ({@link TableApi.getRowValue}). Implemented by the object-array, tree, row-group and pivot models.
370
+ *
371
+ * @example
372
+ * tableModel.getBodyModel().getRowValue?.({ price: 25, quantity: 10 }, "total"); // 250
373
+ *
374
+ * @since 1.1.95
375
+ */
376
+ getRowValue?(row: any, property: string): unknown;
283
377
  /**
284
378
  * Returns the value of the cell at the specified row and column indices.
285
379
  * 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
+ }
@@ -1,5 +1,5 @@
1
1
  import { SizeIf } from '../../common/size.if.js';
2
- import { ColumnDefIf, ValueRowSortComparator } from './column-def.if.js';
2
+ import { CellValidator, ColumnDefIf, RowDragColumnOption, ValueRowSortComparator } from './column-def.if.js';
3
3
  import { CellRendererIf } from '../../../renderer/cell-render.if.js';
4
4
  import { AreaObjectMapType } from '../../common/area-map.type.js';
5
5
  import { AreaObjectMap } from '../../common/area-map.js';
@@ -7,6 +7,9 @@ import { BooleanFunction } from '../../common/boolean-function.js';
7
7
  import { SortState } from '../../common/sort-state.type.js';
8
8
  import { GetEditRenderer } from '../../../renderer/edit/edit-cell-renderer.type.js';
9
9
  import { editInputPipe } from '../../../renderer/edit/edit-input-pipe.if.js';
10
+ import { ValueGetter, ValueSetter } from '../../../calc/value-getter.type.js';
11
+ import { ShowValuesAsConfig } from '../../../calc/show-values-as.type.js';
12
+ import { FormulaOptionsIf } from '../../../formula/formula-options.if.js';
10
13
 
11
14
  /**
12
15
  * The column definition – see {@link ColumnDefIf} for all fields.
@@ -52,6 +55,17 @@ export declare class ColumnDef implements ColumnDefIf {
52
55
  editInputPipe?: editInputPipe | undefined;
53
56
  isVisible: BooleanFunction;
54
57
  visible: boolean;
58
+ /** A calculated column, see {@link ColumnDefIf.valueGetter}. */
59
+ valueGetter?: ValueGetter;
60
+ /** Writes an edited calculated value back, see {@link ColumnDefIf.valueSetter}. */
61
+ valueSetter?: ValueSetter;
62
+ /** „Show values as“, see {@link ColumnDefIf.showValuesAs}. */
63
+ showValuesAs?: ShowValuesAsConfig;
64
+ /** The formula of a formula column, see {@link ColumnDefIf.formula}. */
65
+ formula?: string;
66
+ rowDrag?: RowDragColumnOption;
67
+ /** Checks edited values, see {@link ColumnDefIf.validate} (since 1.1.96). */
68
+ validate?: CellValidator;
55
69
  constructor(property: string, headerLabel: string, width?: SizeIf, classes?: AreaObjectMapType<string[]>, rendererMap?: AreaObjectMapType<CellRendererIf>, minWidth?: SizeIf, maxWidth?: SizeIf, sortable?: BooleanFunction | undefined, sortComparator?: ValueRowSortComparator | undefined, sortState?: SortState | undefined, sortStatesOrder?: SortState[] | undefined, sortIconVisible?: BooleanFunction | undefined, editable?: BooleanFunction | undefined, getEditRenderer?: GetEditRenderer | undefined, editInputPipe?: editInputPipe | undefined, isVisible?: BooleanFunction);
56
70
  static bodyRenderer(bodyRenderer: CellRendererIf): AreaObjectMap<CellRendererIf>;
57
71
  /**
@@ -109,5 +123,21 @@ export declare class ColumnDef implements ColumnDefIf {
109
123
  getEditRenderer?: GetEditRenderer;
110
124
  /** Converts the editor text before it is stored, e.g. `editInputPipeForNumber`. */
111
125
  editInputPipe?: editInputPipe;
126
+ /** A calculated column: `(row, context) => value` (since 1.1.95). */
127
+ valueGetter?: ValueGetter;
128
+ /** Makes a calculated column editable (since 1.1.95). */
129
+ valueSetter?: ValueSetter;
130
+ /** „Show values as“: `"percentOfColumnTotal"`, `"rank"` … (since 1.1.95). */
131
+ showValuesAs?: ShowValuesAsConfig;
132
+ /**
133
+ * A formula column, e.g. `"[price] * [quantity] * 1.19"` – compiled once into the `valueGetter` (since 1.1.95,
134
+ * see {@link compileFormula}). Takes precedence over `valueGetter`.
135
+ */
136
+ formula?: string;
137
+ /** Own functions of the formula (since 1.1.95). */
138
+ formulaOptions?: FormulaOptionsIf;
139
+ rowDrag?: RowDragColumnOption;
140
+ /** Checks edited values: a message for an invalid value (since 1.1.96). */
141
+ validate?: CellValidator;
112
142
  }): ColumnDefIf;
113
143
  }
@@ -5,6 +5,8 @@ import { BooleanFunction } from '../../common/boolean-function.js';
5
5
  import { SortState } from '../../common/sort-state.type.js';
6
6
  import { GetEditRenderer } from '../../../renderer/edit/edit-cell-renderer.type.js';
7
7
  import { editInputPipe } from '../../../renderer/edit/edit-input-pipe.if.js';
8
+ import { ValueGetter, ValueSetter } from '../../../calc/value-getter.type.js';
9
+ import { ShowValuesAsConfig } from '../../../calc/show-values-as.type.js';
8
10
 
9
11
  /**
10
12
  * Represents a function that compares two values for sorting purposes.
@@ -116,4 +118,88 @@ export interface ColumnDefIf {
116
118
  getEditRenderer?: GetEditRenderer;
117
119
  /** Converts the text of the editor before it is written into the model, e.g. {@link editInputPipeForNumber}. */
118
120
  editInputPipe?: editInputPipe;
121
+ /**
122
+ * A calculated column: computes the value from the row instead of reading `property` (the data stays unchanged).
123
+ * Sorting, filter ({@link TableApi.getRowValue}), copy, Excel, totals (`footerAggregates`) and row groups use the
124
+ * computed value. `property` stays the id of the column (state, `footerAggregates`, other formulas).
125
+ * Without a {@link ColumnDefIf.valueSetter} the column cannot be edited.
126
+ *
127
+ * Supported by the models of `TableFactory.createTableModel({ rows })`, trees, row groups (not by array-of-arrays
128
+ * models; an own `AreaModel` calls it itself).
129
+ *
130
+ * @example
131
+ * ColumnDef.create({ property: "total", headerLabel: "Total", sortable: TrueFn,
132
+ * valueGetter: (o: Item) => o.price * o.quantity })
133
+ *
134
+ * @see Training: [Columns with ColumnDef](https://gui.expert/training/column-definitions/)
135
+ * @since 1.1.95
136
+ */
137
+ valueGetter?: ValueGetter;
138
+ /**
139
+ * Makes a calculated column editable: writes the edited value back into the row (return `false` to reject).
140
+ *
141
+ * @example
142
+ * valueSetter: (o: Item, v: unknown) => { o.price = Number(v) / o.quantity; }
143
+ *
144
+ * @since 1.1.95
145
+ */
146
+ valueSetter?: ValueSetter;
147
+ /**
148
+ * Shows the numbers of the column differently without changing the data: share of the column total, running
149
+ * total, rank … (see {@link ShowValuesAs}). Change it at runtime with {@link TableApi.setShowValuesAs}.
150
+ *
151
+ * @example
152
+ * ColumnDef.create({ property: "total", headerLabel: "Share", showValuesAs: "percentOfColumnTotal" })
153
+ *
154
+ * @since 1.1.95
155
+ */
156
+ showValuesAs?: ShowValuesAsConfig;
157
+ /**
158
+ * The formula of a formula column (`ColumnDef.create({ formula })` compiles it into the `valueGetter`), e.g.
159
+ * `"[price] * [quantity] * 1.19"`. Informational – change it with {@link TableApi.setColumnFormula}.
160
+ *
161
+ * @since 1.1.95
162
+ */
163
+ formula?: string;
164
+ /**
165
+ * Row drag: the body cells of this column get a drag handle (`true`, or a function that decides per row – e.g. no
166
+ * handle for locked rows). Dragging the handle moves the row (see {@link TableOptionsIf.rowDragOptions}); the keyboard
167
+ * alternative is Ctrl+Alt+↑/↓. `TableFactory.createTableModel({ rows, rowDrag: true })` sets it on the first data
168
+ * column.
169
+ *
170
+ * @example
171
+ * ColumnDef.create({ property: "title", headerLabel: "Task", rowDrag: (task: { locked: boolean }) => !task.locked })
172
+ *
173
+ * @since 1.1.95
174
+ */
175
+ rowDrag?: RowDragColumnOption;
176
+ /**
177
+ * Checks an edited, filled or pasted value (after `editInputPipe`): return a message for an invalid value,
178
+ * `undefined` for a valid one. Batch edit: invalid values are marked (`ge-table-cell-invalid`, the message as
179
+ * tooltip) and block {@link TableApi.commitBatchEdit}; otherwise an invalid value is not written.
180
+ *
181
+ * @example
182
+ * ColumnDef.create({
183
+ * property: "price", headerLabel: "Price", editable: TrueFn, editInputPipe: editInputPipeForNumber,
184
+ * validate: (value: unknown) => typeof value === "number" && value >= 0 ? undefined : "A price is a number ≥ 0",
185
+ * })
186
+ *
187
+ * @since 1.1.96
188
+ */
189
+ validate?: CellValidator;
119
190
  }
191
+ /**
192
+ * {@link ColumnDefIf.validate}: a message for an invalid value, `undefined` (or `null`, `""`) for a valid one.
193
+ *
194
+ * @category Fill handle & batch edit
195
+ * @since 1.1.96
196
+ */
197
+ export type CellValidator<T = any> = (value: unknown, row: T) => string | undefined | null | void;
198
+ /**
199
+ * {@link ColumnDefIf.rowDrag}: `true` = every row has a drag handle in this column, a function decides per row
200
+ * (`row` = the row object, a `TreeRow` in a tree; `rowIndex` = display index).
201
+ *
202
+ * @category Row drag
203
+ * @since 1.1.95
204
+ */
205
+ export type RowDragColumnOption = boolean | ((row: any, rowIndex: number) => boolean);
@@ -5,6 +5,8 @@ import { EventListenerIf } from './event-listener.if.js';
5
5
  import { SelectionModelIf } from './selection/selection-model.if.js';
6
6
  import { FocusModelIf } from './focus/focus-model.if.js';
7
7
  import { GeStateChangeEvent } from './data/common/event/ge-state-change-event.js';
8
+ import { GeBatchEditEvent } from './data/common/event/ge-batch-edit-event.js';
9
+ import { GeRowDragEvent } from './data/common/event/ge-row-drag-event.js';
8
10
 
9
11
  /**
10
12
  * Empty implementation of {@link EventListenerIf} – extend it and override only the events you need.
@@ -33,4 +35,9 @@ export declare class EventAdapter implements EventListenerIf {
33
35
  onFocusChanged(_model: FocusModelIf): void;
34
36
  /** @since 1.1.92 */
35
37
  onStateChanged(_evt: GeStateChangeEvent): void;
38
+ onRowDragStart(_evt: GeRowDragEvent): void;
39
+ onRowDragMove(_evt: GeRowDragEvent): void;
40
+ onRowDragEnd(_evt: GeRowDragEvent): void;
41
+ /** @since 1.1.96 */
42
+ onBatchEditChanged(_evt: GeBatchEditEvent): void;
36
43
  }
@@ -2,6 +2,8 @@ import { GeMouseEvent } from './data/common/event/ge-mouse-event.js';
2
2
  import { GeModelChangeEvent } from './data/common/event/ge-model-change-event.js';
3
3
  import { GeScrollEvent } from './data/common/event/ge-scroll-event.js';
4
4
  import { GeStateChangeEvent } from './data/common/event/ge-state-change-event.js';
5
+ import { GeRowDragEvent } from './data/common/event/ge-row-drag-event.js';
6
+ import { GeBatchEditEvent } from './data/common/event/ge-batch-edit-event.js';
5
7
  import { SelectionModelIf } from './selection/selection-model.if.js';
6
8
  import { FocusModelIf } from './focus/focus-model.if.js';
7
9
  import { EventFocusChangedListenerIf } from './focus/event-focus-changed-listener.if.js';
@@ -63,4 +65,34 @@ export interface EventListenerIf extends EventFocusChangedListenerIf, EventSelec
63
65
  * @since 1.1.92
64
66
  */
65
67
  onStateChanged?(evt: GeStateChangeEvent): void;
68
+ /**
69
+ * Optional: a row drag started (drag handle moved past the threshold, or Ctrl+Alt+↑/↓). `evt.preventDefault()`
70
+ * cancels it. See {@link GeRowDragEvent}.
71
+ *
72
+ * @since 1.1.95
73
+ */
74
+ onRowDragStart?(evt: GeRowDragEvent): void;
75
+ /**
76
+ * Optional: the drop target of a row drag changed (not on every pixel). `evt.preventDefault()` marks the target as
77
+ * invalid (no drop marker, no drop).
78
+ *
79
+ * @since 1.1.95
80
+ */
81
+ onRowDragMove?(evt: GeRowDragEvent): void;
82
+ /**
83
+ * Optional: a row drag ended – `evt.type` is `"end"` (dropped; `evt.moved` = the table moved the rows) or `"cancel"`.
84
+ * Managed: called after the move (`evt.toIndex` = the real position). Save the new order here; to veto a drop, call
85
+ * `evt.preventDefault()` in {@link onRowDragMove}.
86
+ *
87
+ * @since 1.1.95
88
+ */
89
+ onRowDragEnd?(evt: GeRowDragEvent): void;
90
+ /**
91
+ * The pending changes of a batch edit changed: values were marked, set back, written or discarded
92
+ * ({@link TableApi.startBatchEdit}, {@link TableOptionsIf.batchEdit}). Optional.
93
+ *
94
+ * @param {GeBatchEditEvent} evt - type, cells of this step, number of pending and invalid cells
95
+ * @since 1.1.96
96
+ */
97
+ onBatchEditChanged?(evt: GeBatchEditEvent): void;
66
98
  }
@@ -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
+ }