@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,17 @@
1
+ import { ColumnDefIf } from '../tablemodel/column/column-def.if.js';
2
+
3
+ /**
4
+ * 1 if the first column is a {@link RowNumberColumnDef}, otherwise 0 – the offset of the built-in columns that are
5
+ * bound to "the first column": the {@link CheckboxColumnDef}, the tree arrow and the checkbox of
6
+ * `showCheckboxWihoutExtraColumn` sit at this index (behind the row numbers).
7
+ *
8
+ * @param {ColumnDefIf[] | undefined} columnDefs - the column definitions of the table model
9
+ * @returns {0 | 1} the number of leading row number columns
10
+ *
11
+ * @example
12
+ * const firstDataColumn = getLeadingRowNumberColumnCount(tableModel.getColumnDefs());
13
+ *
14
+ * @since 1.1.93
15
+ * @category Row numbers
16
+ */
17
+ export declare function getLeadingRowNumberColumnCount(columnDefs: ColumnDefIf[] | undefined): 0 | 1;
@@ -0,0 +1,19 @@
1
+ import { RowNumberMode, RowNumberOptionsIf } from './row-number-options.if.js';
2
+
3
+ /**
4
+ * The default options of the row number column: numbers of the current view, no thousands separators, header `"#"`,
5
+ * not exported, at least 36 px wide.
6
+ *
7
+ * @example
8
+ * const columnDef = new RowNumberColumnDef({ ...new RowNumberOptions(), headerLabel: "" });
9
+ *
10
+ * @since 1.1.93
11
+ * @category Row numbers
12
+ */
13
+ export declare class RowNumberOptions implements RowNumberOptionsIf {
14
+ mode: RowNumberMode;
15
+ format: (rowNumber: number) => string;
16
+ headerLabel: string;
17
+ exported: boolean;
18
+ minWidthInPx: number;
19
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * How a {@link RowNumberColumnDef} counts:
3
+ * - `"display"` (default): the position in the current view – 1, 2, 3 … also after sorting and filtering
4
+ * (like the row headers of a spreadsheet).
5
+ * - `"original"`: the position in the unsorted, unfiltered rows (`getAllRows()` of the body model) – the number stays
6
+ * with its row. Body models without `getAllRows()` (tree, own area models) count like `"display"`.
7
+ *
8
+ * @since 1.1.93
9
+ * @category Row numbers
10
+ */
11
+ export type RowNumberMode = "display" | "original";
12
+ /**
13
+ * Options of the row number column ({@link RowNumberColumnDef}). Pass any subset – to
14
+ * `new RowNumberColumnDef({ … })` or as `rowNumbersVisible: { … }` to {@link TableFactory.createTableModel}.
15
+ * Defaults: {@link RowNumberOptions}.
16
+ *
17
+ * @example
18
+ * const tableModel = TableFactory.createTableModel({
19
+ * rows: orders,
20
+ * rowNumbersVisible: { mode: "original", format: n => n.toLocaleString("en-US"), exported: true }
21
+ * });
22
+ *
23
+ * @since 1.1.93
24
+ * @category Row numbers
25
+ */
26
+ export interface RowNumberOptionsIf {
27
+ /** `"display"` (default) or `"original"`, see {@link RowNumberMode}. */
28
+ mode: RowNumberMode;
29
+ /**
30
+ * The text of a number. Default `String(n)` – no thousands separators (`"2000000"`).
31
+ * The column width is measured with the text of the largest number, so the text should not get shorter for
32
+ * larger numbers.
33
+ *
34
+ * @example
35
+ * const options: Partial<RowNumberOptionsIf> = { format: n => n.toLocaleString("de-DE") }; // "2.000.000"
36
+ */
37
+ format: (rowNumber: number) => string;
38
+ /** Text of the header cell. Default `"#"` (an empty header cell has no accessible name). */
39
+ headerLabel: string;
40
+ /** `true`: {@link TableApi.copyToClipboard} and {@link TableApi.downloadExcel} contain the numbers. Default `false`. */
41
+ exported: boolean;
42
+ /** The column is never narrower than this (px). Default 36. */
43
+ minWidthInPx: number;
44
+ }
@@ -12,6 +12,9 @@ import { ShortcutActionIdMapping } from '../../action/shortcut-actionid-mapping.
12
12
  import { HeaderGroupOptions } from './header-group-options.js';
13
13
  import { AriaTextsIf } from '../../aria/aria-texts.if.js';
14
14
  import { OverlayOptionsIf } from './overlay-options.if.js';
15
+ import { RowDragOptionsIf } from '../../row-drag/row-drag-options.if.js';
16
+ import { FillHandleOptionsIf } from '../../fill/fill-handle-options.if.js';
17
+ import { BatchEditOptionsIf } from '../../batch-edit/batch-edit-options.if.js';
15
18
 
16
19
  /**
17
20
  * All default options of a table – see {@link TableOptionsIf} for the meaning of each field.
@@ -80,4 +83,7 @@ export declare class TableOptions implements TableOptionsIf {
80
83
  ariaTexts?: Partial<AriaTextsIf>;
81
84
  /** Texts and behaviour of the "Loading…" / "No rows" overlays; missing fields use {@link OverlayOptions}. @since 1.1.92 */
82
85
  overlayOptions?: Partial<OverlayOptionsIf>;
86
+ rowDragOptions?: Partial<RowDragOptionsIf>;
87
+ fillHandle?: boolean | Partial<FillHandleOptionsIf>;
88
+ batchEdit?: boolean | Partial<BatchEditOptionsIf>;
83
89
  }
@@ -1,3 +1,6 @@
1
+ import { RowDragOptionsIf } from '../../row-drag/row-drag-options.if.js';
2
+ import { FillHandleOptionsIf } from '../../fill/fill-handle-options.if.js';
3
+ import { BatchEditOptionsIf } from '../../batch-edit/batch-edit-options.if.js';
1
4
  import { DefaultRowHeightsIf } from './default-row-heights.if.js';
2
5
  import { TreeOptionsIf } from './tree-options.if.js';
3
6
  import { FilterFunction } from '../common/filter-function.js';
@@ -317,6 +320,47 @@ export interface TableOptionsIf {
317
320
  * @since 1.1.92
318
321
  */
319
322
  overlayOptions?: Partial<OverlayOptionsIf>;
323
+ /**
324
+ * Row drag: managed or events only, multi-row drag, tree drop, auto-scroll (see {@link RowDragOptionsIf}). The drag
325
+ * handles come from {@link ColumnDefIf.rowDrag}. Fields you leave out keep their default ({@link RowDragOptions}).
326
+ *
327
+ * @example
328
+ * const tableOptions: TableOptionsIf = {
329
+ * ...new TableOptions(),
330
+ * rowDragOptions: { managed: false }, // only events – e.g. the server decides
331
+ * };
332
+ *
333
+ * @since 1.1.95
334
+ */
335
+ rowDragOptions?: Partial<RowDragOptionsIf>;
336
+ /**
337
+ * The fill handle: a small square at the bottom right corner of the selected range. Dragging it fills the
338
+ * neighbouring cells (copy, or a series: 1, 2, 3 …, dates, `Item 1` → `Item 2`); ⌥/Alt switches between series and
339
+ * copy. Keyboard: Ctrl/⌘+D fills down, Ctrl/⌘+R fills right. Needs a selection model of the type `"range"`;
340
+ * only editable cells are filled. `true` = defaults ({@link FillHandleOptions}).
341
+ *
342
+ * @example
343
+ * const selectionModel = new SelectionModel("range", "multi"); // one model – not a new one per call
344
+ * const tableOptions: TableOptionsIf = {
345
+ * ...new TableOptions(),
346
+ * getSelectionModel: () => selectionModel,
347
+ * fillHandle: true,
348
+ * };
349
+ *
350
+ * @since 1.1.96
351
+ */
352
+ fillHandle?: boolean | Partial<FillHandleOptionsIf>;
353
+ /**
354
+ * Batch edit always on: edited and filled values are collected (marked `ge-table-cell-pending`) and written with
355
+ * {@link TableApi.commitBatchEdit} or discarded with {@link TableApi.cancelBatchEdit}. Without it, start a batch
356
+ * edit with {@link TableApi.startBatchEdit} (it ends with commit or cancel).
357
+ *
358
+ * @example
359
+ * const tableOptions: TableOptionsIf = { ...new TableOptions(), batchEdit: true };
360
+ *
361
+ * @since 1.1.96
362
+ */
363
+ batchEdit?: boolean | Partial<BatchEditOptionsIf>;
320
364
  /**
321
365
  * The state the table starts with: column layout, sorting, tree, checked and selected rows … – e.g. loaded from
322
366
  * your server ({@link TableApi.getState}, {@link EventListenerIf.onStateChanged}). A state stored by
@@ -7,6 +7,7 @@ import { CheckboxModelIf } from '../../../checkbox/checkbox-model.if.js';
7
7
  import { FilterFunction } from '../../common/filter-function.js';
8
8
  import { SortItem } from '../../common/sort-item.js';
9
9
  import { HasDefaultRowHeightIf } from './has-default-row-height-if.js';
10
+ import { ColumnValues } from '../../../calc/column-values.js';
10
11
 
11
12
  /** @category Area models */
12
13
  export declare abstract class AbstractAreaModel<T> implements AreaModelIf, HasDefaultRowHeightIf {
@@ -14,6 +15,8 @@ export declare abstract class AbstractAreaModel<T> implements AreaModelIf, HasDe
14
15
  protected columnDefs: ColumnDefIf[];
15
16
  defaultRowHeight: number;
16
17
  rowSelectionModel: CheckboxModelIf<any> | undefined;
18
+ private columnValuesInstance?;
19
+ private calcTextRenderer?;
17
20
  protected cellRenderers: (CellRendererIf | undefined)[];
18
21
  protected yPositions: number[];
19
22
  constructor(areaIdent: AreaIdent, columnDefs?: ColumnDefIf[], defaultRowHeight?: number);
@@ -48,4 +51,67 @@ export declare abstract class AbstractAreaModel<T> implements AreaModelIf, HasDe
48
51
  protected setPropertyValue(o: any, props: string[], value: any): boolean;
49
52
  protected arrayMove(arr: any[], fromIndex: number, toIndex: number): any[];
50
53
  private calcYPositions;
54
+ /**
55
+ * True if a column of this model with this property is calculated (`valueGetter` or `formula`).
56
+ *
57
+ * @param {string} property - the property of the column
58
+ * @returns {boolean} whether its values are computed
59
+ *
60
+ * @example
61
+ * tableModel.getBodyModel().isCalculatedProperty?.("total"); // true
62
+ *
63
+ * @since 1.1.95
64
+ */
65
+ isCalculatedProperty(property: string): boolean;
66
+ /**
67
+ * The value of a column for a row object – the computed value of a calculated column, otherwise the property
68
+ * (path). A `TreeRow` is unwrapped. The table API offers it as {@link TableApi.getRowValue}.
69
+ *
70
+ * @param {any} row - a row object (or `TreeRow`)
71
+ * @param {string} property - the property of the column
72
+ * @returns {unknown} the value
73
+ *
74
+ * @example
75
+ * const body = tableModel.getBodyModel() as AreaModelObjectArray<Item>;
76
+ * body.getRowValue({ price: 25, quantity: 10 }, "total"); // 250
77
+ *
78
+ * @since 1.1.95
79
+ */
80
+ getRowValue(row: any, property: string): unknown;
81
+ /**
82
+ * True if the model has calculated or „Show values as“ columns – their cells depend on other cells
83
+ * (the table redraws them after `updateCells`).
84
+ *
85
+ * @returns {boolean} whether derived columns exist
86
+ * @since 1.1.95
87
+ */
88
+ hasDerivedColumns(): boolean;
89
+ /**
90
+ * True if the cells of a column depend on other cells (calculated or „Show values as“).
91
+ *
92
+ * @param {string} property - the property of the column
93
+ * @returns {boolean} whether the column is derived
94
+ * @since 1.1.95
95
+ */
96
+ isDerivedProperty(property: string): boolean;
97
+ /** The resolver of calculated values of this model (created on first use). @internal */
98
+ protected getColumnValues(): ColumnValues;
99
+ /** Drops the cached aggregates and the getter lookup of calculated columns. */
100
+ protected invalidateColumnValues(): void;
101
+ /** The value of a property path – overridden by the models with their own path semantics. */
102
+ protected readPropertyPath(row: any, property: string): unknown;
103
+ /** The rows of column aggregates in formulas (`SUM([total])`): the filtered row objects. */
104
+ protected getAggregationSourceForValues(): {
105
+ signal: unknown;
106
+ length: number;
107
+ rows: () => ArrayLike<unknown>;
108
+ };
109
+ /** Writes an edited value of a calculated column through its `valueSetter`; without one: rejected. */
110
+ protected setCalculatedValue(row: any, columnDef: ColumnDefIf, value: any): boolean;
111
+ /** The text of a cell of a calculated / „Show values as“ column; `undefined` for a normal column. */
112
+ protected getCalculatedText(property: string | undefined, value: unknown): string | undefined;
113
+ /** The renderer of a „Show values as“ column without an own renderer: the text of `getTextValueAt`. */
114
+ protected getCalculatedRenderer(property: string | undefined): CellRendererIf | undefined;
115
+ /** The CSS class and tooltip of an error value of a formula. */
116
+ protected addCalculatedClasses(classes: string[], property: string | undefined, value: () => unknown): string[];
51
117
  }
@@ -0,0 +1,73 @@
1
+ import { AggregateSpecMap } from '../../../aggregation/aggregate-spec.if.js';
2
+ import { AreaModelIf } from './area-model.if.js';
3
+
4
+ /**
5
+ * One total row of an {@link AreaModelAggregateFooter}: the aggregates by column property and an optional label.
6
+ *
7
+ * @example
8
+ * const sumRow: AggregateFooterRowIf<Order> = { label: "Sum", aggregates: { quantity: "sum", total: "sum" } };
9
+ * const avgRow: AggregateFooterRowIf<Order> = {
10
+ * label: (rowCount, results) => `Ø of ${rowCount} orders`,
11
+ * aggregates: { total: { aggregator: "avg", format: v => `${(v as number).toFixed(2)} €` } }
12
+ * };
13
+ *
14
+ * @since 1.1.94
15
+ * @category Aggregation
16
+ */
17
+ export interface AggregateFooterRowIf<T = any> {
18
+ /** Key = property of the column that shows the result (a key without a column is only in `getResults()`). */
19
+ aggregates: AggregateSpecMap<T>;
20
+ /**
21
+ * Text in the label column (always set as text, never as HTML). Default: `"6 rows"` – or, in a footer with
22
+ * several rows whose aggregates all use the same built-in aggregator, its word (`"Sum"`, `"Average"`, `"Min"` …).
23
+ */
24
+ label?: string | ((rowCount: number, results: Record<string, unknown>) => string);
25
+ }
26
+ /**
27
+ * The options of an {@link AreaModelAggregateFooter} (and of `TableFactory.createTableModel({ footerAggregates })`).
28
+ *
29
+ * @example
30
+ * const options: AggregateFooterOptionsIf<Order> = {
31
+ * rows: [
32
+ * { label: "Sum", aggregates: { quantity: "sum", total: "sum" } },
33
+ * { label: "Average", aggregates: { quantity: "avg", total: "avg" } }
34
+ * ],
35
+ * labelProperty: "customer", // the labels go into the customer column
36
+ * rowHeight: 36
37
+ * };
38
+ *
39
+ * @since 1.1.94
40
+ * @category Aggregation
41
+ */
42
+ export interface AggregateFooterOptionsIf<T = any> {
43
+ /** One footer row per entry. */
44
+ rows: AggregateFooterRowIf<T>[];
45
+ /** The column of the labels (property). Default: the first data column without an aggregate in that row. */
46
+ labelProperty?: string;
47
+ /** Height of the footer rows in px. Default 34 (the factory uses `defaultRowHeights.footer`). */
48
+ rowHeight?: number;
49
+ /**
50
+ * Which rows are aggregated: `"filtered"` (default) – the rows the body shows (after `externalFilterFunction`;
51
+ * in a tree all leaves that pass the filter, expanded or not); `"all"` – all rows, the filter is ignored.
52
+ */
53
+ source?: "filtered" | "all";
54
+ /** An own source of rows (instead of the body model's rows), e.g. the data of a server response. */
55
+ getRows?: (bodyModel: AreaModelIf) => ArrayLike<T>;
56
+ }
57
+ /**
58
+ * The short and the full forms of a footer configuration:
59
+ * - `{ total: "sum" }` – one row ({@link AggregateSpecMap}),
60
+ * - `[{ label: "Sum", aggregates: … }, …]` – several rows ({@link AggregateFooterRowIf}),
61
+ * - `{ rows: […], labelProperty, rowHeight, source, getRows }` – everything ({@link AggregateFooterOptionsIf}).
62
+ *
63
+ * @example
64
+ * const oneRow: AggregateFooterConfig<Order> = { quantity: "sum", total: "sum" };
65
+ * const twoRows: AggregateFooterConfig<Order> = [
66
+ * { label: "Sum", aggregates: { total: "sum" } },
67
+ * { label: "Max", aggregates: { total: "max" } }
68
+ * ];
69
+ *
70
+ * @since 1.1.94
71
+ * @category Aggregation
72
+ */
73
+ export type AggregateFooterConfig<T = any> = AggregateSpecMap<T> | AggregateFooterRowIf<T>[] | AggregateFooterOptionsIf<T>;
@@ -0,0 +1,150 @@
1
+ import { AreaModel } from './area-model.js';
2
+ import { AreaModelIf } from './area-model.if.js';
3
+ import { HasDefaultRowHeightIf } from './has-default-row-height-if.js';
4
+ import { ColumnDefIf } from '../column/column-def.if.js';
5
+ import { CellRendererIf } from '../../../renderer/cell-render.if.js';
6
+ import { AggregateFooterConfig, AggregateFooterOptionsIf } from './aggregate-footer-options.if.js';
7
+ import { ResolvedAggregateSpec } from '../../../aggregation/aggregate-internals.js';
8
+
9
+ interface AggregationSource {
10
+ /** changes when the rows change (identity) – undefined: only invalidateDerivedValues() */
11
+ signal: unknown;
12
+ /** the number of rows, cheap (part of the cache key) – undefined: not known without computing the rows */
13
+ length?: number;
14
+ /** the rows (objects) – or, for models without row objects, the number of rows; called on a cache miss only */
15
+ getRows(): ArrayLike<unknown> | number;
16
+ }
17
+ /**
18
+ * A ready-made footer with totals (sum, average, count, …) that follow filter and sorting – without an own
19
+ * {@link AreaModel}. One or more total rows; every aggregate belongs to the column with the same **property**
20
+ * (column order, row numbers and the checkbox column don't matter). The easiest way is the factory option
21
+ * `footerAggregates` of {@link TableFactory.createTableModel}.
22
+ *
23
+ * - **Data**: the rows the body shows (`getFilteredRows()`), in a tree all leaves the filter keeps (expanded or not),
24
+ * in other models every row via `getValueAt` of the column. Options `source: "all"` and `getRows` change that.
25
+ * - **Cells**: the value is the raw result (a number stays a number – Excel export, own footer renderers); the text is
26
+ * formatted (`format` of the spec, otherwise {@link formatAggregateValue}). A footer renderer of the column
27
+ * (`rendererMap.footer`) gets the raw value; without it the {@link AggregateCellRenderer} writes the text.
28
+ * - **Label**: `"6 rows"` (or the row's `label`) in the first data column without an aggregate, or in `labelProperty`;
29
+ * screen readers announce it as row header.
30
+ * - **Cache**: computed once per change of the data, not per repaint or scroll frame. The table drops the cache when
31
+ * it filters, sorts, edits, gets `updateCells()` or `tableApi.repaint()` / `repaintHard()` – after changing row
32
+ * objects yourself call `tableApi.repaint()`, as for the cells.
33
+ *
34
+ * @example
35
+ * // one line: totals of quantity and total – the label "100,000 rows" goes into the first column without aggregate
36
+ * const tableModel = TableFactory.createTableModel({
37
+ * rows: orders,
38
+ * columnDefs,
39
+ * footerAggregates: { quantity: "sum", total: "sum" }
40
+ * });
41
+ *
42
+ * @example
43
+ * // two total rows with own labels, an own body model
44
+ * const footer = new AreaModelAggregateFooter<Order>(() => tableModel.getBodyModel(), columnDefs, [
45
+ * { label: "Sum", aggregates: { quantity: "sum", total: "sum" } },
46
+ * { label: (rowCount: number) => `Ø of ${rowCount}`, aggregates: { quantity: "avg", total: "avg" } }
47
+ * ]);
48
+ * const tableModel = TableFactory.createTableModel({ rows: orders, columnDefs, footerAreaModel: footer });
49
+ *
50
+ * @example
51
+ * // read the results, e.g. for a title
52
+ * const footer = tableModel.getAreaModel("footer") as AreaModelAggregateFooter<Order>;
53
+ * const { total } = footer.getResults(); // { total: 27481033.5, … }
54
+ *
55
+ * @see {@link aggregateColumns} (the same without a table), {@link TableApi.getSelectionAggregates} (selected cells)
56
+ * @see Training: [Footer totals](https://gui.expert/training/footer-totals/)
57
+ *
58
+ * @since 1.1.94
59
+ * @category Aggregation
60
+ */
61
+ export declare class AreaModelAggregateFooter<T = any> extends AreaModel implements HasDefaultRowHeightIf {
62
+ private readonly columnDefs;
63
+ /** Marker (works across copies of the core): {@link isAreaModelAggregateFooter}. */
64
+ readonly type = "AreaModelAggregateFooter";
65
+ /** Height of the footer rows in px (`rowHeight` of the options, default 34). */
66
+ defaultRowHeight: number;
67
+ private readonly getBody;
68
+ private readonly options;
69
+ private readonly footerRows;
70
+ private readonly textRenderer;
71
+ private cache?;
72
+ /**
73
+ * @param {AreaModelIf | (() => AreaModelIf)} body - the body model, or a function that returns it (if the footer is
74
+ * created before the table model, e.g. `() => tableModel.getBodyModel()`)
75
+ * @param {ColumnDefIf[]} columnDefs - the column definitions of the table (the same array – columns are found by property)
76
+ * @param {AggregateFooterConfig<T>} config - `{ total: "sum" }` (one row), `[{ label, aggregates }, …]` (several rows)
77
+ * or full {@link AggregateFooterOptionsIf}
78
+ */
79
+ constructor(body: AreaModelIf | (() => AreaModelIf), columnDefs: ColumnDefIf[], config: AggregateFooterConfig<T>);
80
+ /** The full options of a short configuration. */
81
+ static toOptions<T>(config: AggregateFooterConfig<T>): AggregateFooterOptionsIf<T>;
82
+ getRowCount(): number;
83
+ getRowHeight(_rowIndex: number): number;
84
+ getConstantRowHeight(): number | undefined;
85
+ /**
86
+ * Drops the cached results – the next cell read computes them again ({@link AreaModelIf.invalidateDerivedValues}).
87
+ * The table calls it when it filters, sorts, edits, gets `updateCells()` and on `tableApi.repaint()` / `repaintHard()`.
88
+ *
89
+ * @example
90
+ * order.total = 99; // changed outside the table
91
+ * tableApi.repaint(); // the cells and the footer show it
92
+ */
93
+ invalidateDerivedValues(): void;
94
+ /**
95
+ * The results of a footer row by key (also of keys without a column).
96
+ *
97
+ * @param {number} [rowIndex=0] - the footer row
98
+ * @returns {Record<string, unknown>} key → result (`{}` for a row that does not exist); do not modify it
99
+ *
100
+ * @example
101
+ * const footer = tableModel.getAreaModel("footer") as AreaModelAggregateFooter<Order>;
102
+ * document.title = `${formatAggregateValue(footer.getResults().total)} € in ${footer.getAggregationRowCount()} orders`;
103
+ */
104
+ getResults(rowIndex?: number): Record<string, unknown>;
105
+ /**
106
+ * The number of aggregated rows (e.g. the filtered body rows; in a tree the leaves).
107
+ *
108
+ * @returns {number} the number of rows the results are based on
109
+ */
110
+ getAggregationRowCount(): number;
111
+ /**
112
+ * Whether a cell shows the label of its footer row (the table gives it `role="rowheader"`).
113
+ *
114
+ * @param {number} rowIndex - footer row
115
+ * @param {number} columnIndex - column (model index)
116
+ * @returns {boolean} true for the label cell
117
+ */
118
+ isLabelCell(rowIndex: number, columnIndex: number): boolean;
119
+ getValueAt(rowIndex: number, columnIndex: number): any;
120
+ getTextValueAt(rowIndex: number, columnIndex: number): string;
121
+ /**
122
+ * The renderer of a cell: for a result the footer renderer of the column (it gets the raw value), otherwise – and for
123
+ * the label – an {@link AggregateCellRenderer} that writes {@link getTextValueAt} as text.
124
+ */
125
+ getCellRenderer(rowIndex: number, columnIndex: number): CellRendererIf | undefined;
126
+ getCustomClassesAt(rowIndex: number, columnIndex: number): string[];
127
+ /** The label column of a footer row, -1 for none. */
128
+ protected getLabelColumnIndex(rowIndex: number): number;
129
+ /** The label text of a footer row. */
130
+ protected getLabel(rowIndex: number): string;
131
+ /**
132
+ * The rows to aggregate: `getRows` of the options, the (filtered) rows of an object body, the leaves of a tree, or –
133
+ * for models without row objects – the row count.
134
+ * @internal
135
+ */
136
+ protected getSource(): AggregationSource;
137
+ /**
138
+ * The getter of a spec for a model without row objects: a property with a column → `getValueAt` of that column;
139
+ * a function or a path without column → applied to `getRowByIndex(r)`.
140
+ * @internal
141
+ */
142
+ protected createIndexGetter(body: AreaModelIf, spec: ResolvedAggregateSpec<T>): ((rowIndex: number) => unknown) | undefined;
143
+ /**
144
+ * A spec whose value is the property of a calculated column of the body (`footerAggregates: { total: "sum" }` with
145
+ * `total` = `price * quantity`) sums the computed values, not the (missing) property of the row (since 1.1.95).
146
+ */
147
+ private withCalculatedValues;
148
+ private compute;
149
+ }
150
+ export {};
@@ -2,6 +2,7 @@ import { AbstractAreaModel } from './abstract-area-model.js';
2
2
  import { AreaIdent } from '../area-ident.type.js';
3
3
  import { ColumnDefIf } from '../column/column-def.if.js';
4
4
  import { FilterFunction } from '../../common/filter-function.js';
5
+ import { RowDropTargetIf } from '../../../row-drag/row-drop-target.if.js';
5
6
 
6
7
  /**
7
8
  * Represents a model for an area containing a two-dimensional array of data.
@@ -25,6 +26,29 @@ export declare class AreaModelArrayOfArrays<T> extends AbstractAreaModel<T[][]>
25
26
  * @since 1.1.92
26
27
  */
27
28
  getUnfilteredRowCount(): number;
29
+ /**
30
+ * Row drag: true if the rows can be moved (no filter hides a row, target `"before"` / `"after"` a row).
31
+ *
32
+ * @param {number[]} rowIndices - display indices of the dragged rows
33
+ * @param {RowDropTargetIf} target - the drop target
34
+ * @returns {boolean} true if {@link moveRows} would move them
35
+ * @since 1.1.95
36
+ */
37
+ canMoveRows(rowIndices: number[], target: RowDropTargetIf): boolean;
38
+ /**
39
+ * Row drag: moves the rows – in place in the displayed rows and in {@link arr}.
40
+ *
41
+ * @param {number[]} rowIndices - display indices of the dragged rows
42
+ * @param {RowDropTargetIf} target - the drop target (display index before the move)
43
+ * @returns {boolean} true if moved
44
+ *
45
+ * @example
46
+ * const model = new AreaModelArrayOfArrays("body", [["a"], ["b"], ["c"]], 30);
47
+ * model.moveRows([0], { rowIndex: 2, position: "after" }); // b, c, a
48
+ *
49
+ * @since 1.1.95
50
+ */
51
+ moveRows(rowIndices: number[], target: RowDropTargetIf): boolean;
28
52
  getValueAt(rowIndex: number, columnIndex: number): any;
29
53
  setValue(rowIndex: number, columnIndex: number, value: any): boolean;
30
54
  getRowByIndex(rowIndex: number): any;
@@ -15,7 +15,7 @@ import { TableHeaderGroupStateIf } from '../../../state/table-state.if.js';
15
15
  /** @category Header groups */
16
16
  export declare class AreaModelCellGroups implements AreaModelIf {
17
17
  readonly areaIdent: AreaIdent;
18
- readonly groups: CellGroupIf[];
18
+ groups: CellGroupIf[];
19
19
  columnDefs: ColumnDefIf[];
20
20
  readonly defaultRowHeight: number;
21
21
  private headerGroupOptions;
@@ -80,6 +80,23 @@ export declare class AreaModelCellGroups implements AreaModelIf {
80
80
  * @since 1.1.92
81
81
  */
82
82
  setGroupClosed(id: string, closed: boolean): ColumnDefIf[] | undefined;
83
+ /**
84
+ * Replaces the header groups (e.g. a pivot table with other column keys): builds the header rows again and returns
85
+ * the column definitions in header order – existing definitions with the same property are kept (width, classes,
86
+ * renderers), their visibility follows the (collapsed) groups. Repaint the table afterwards (a pivot table does all of
87
+ * this in {@link TableApi.setPivotConfig}).
88
+ *
89
+ * @param {CellGroupIf[]} groups - the new groups; every column is a leaf with a `property`
90
+ * @param {ColumnDefIf[]} [columnDefs] - definitions to use for these properties (default: the current ones)
91
+ * @returns {ColumnDefIf[]} the column definitions, one per leaf, in header order
92
+ * @example
93
+ * const header = tableApi.getTableModel().getAreaModel("header") as AreaModelCellGroups;
94
+ * const columnDefs = header.setGroups([
95
+ * { data: "Customer", children: [{ data: "Name", property: "name" }, { data: "City", property: "city" }] }
96
+ * ]);
97
+ * @since 1.1.94
98
+ */
99
+ setGroups(groups: CellGroupIf[], columnDefs?: ColumnDefIf[]): ColumnDefIf[];
83
100
  /** The toggleable groups by id (header order). */
84
101
  private getToggleGroups;
85
102
  /**
@@ -5,6 +5,10 @@ import { FilterFunction } from '../../common/filter-function.js';
5
5
  import { SorterService } from '../../../service/sorter.service.js';
6
6
  import { SortItem } from '../../common/sort-item.js';
7
7
  import { ObjectArrayHolderIf } from './object-array-holder.if.js';
8
+ import { CellRendererIf } from '../../../renderer/cell-render.if.js';
9
+ import { DerivedValuesChangeIf } from './derived-values-change.if.js';
10
+ import { ShowValuesAsIf } from '../../../calc/show-values-as.type.js';
11
+ import { RowDropTargetIf } from '../../../row-drag/row-drop-target.if.js';
8
12
 
9
13
  /**
10
14
  * @class AreaModelObjectArray
@@ -171,6 +175,8 @@ export declare class AreaModelObjectArray<T> extends AbstractAreaModel<T> implem
171
175
  * @internal
172
176
  */
173
177
  protected sorterService: SorterService;
178
+ /** Cached „Show values as“ values per property (cleared on filter, sorting and invalidation). */
179
+ private showValuesCache;
174
180
  /**
175
181
  * Creates an instance of AreaModelObjectArray.
176
182
  *
@@ -280,6 +286,36 @@ export declare class AreaModelObjectArray<T> extends AbstractAreaModel<T> implem
280
286
  * @since 1.1.92
281
287
  */
282
288
  getUnfilteredRowCount(): number;
289
+ /**
290
+ * Row drag: true if the rows can be moved – no filter hides a row and the target is a row of this model
291
+ * (`"before"` / `"after"`).
292
+ *
293
+ * @param {number[]} rowIndices - display indices of the dragged rows
294
+ * @param {RowDropTargetIf} target - the drop target
295
+ * @returns {boolean} true if {@link moveRows} would move them
296
+ *
297
+ * @example
298
+ * bodyAreaModel.canMoveRows([2], { rowIndex: 6, position: "after" }); // true without a filter
299
+ *
300
+ * @since 1.1.95
301
+ */
302
+ canMoveRows(rowIndices: number[], target: RowDropTargetIf): boolean;
303
+ /**
304
+ * Row drag: moves the rows in the display order and writes that order back to {@link getAllRows} – **in place**
305
+ * (like `Array.prototype.sort`: with `TableFactory.createTableModel({ rows })` your array has the new order).
306
+ * Works only without a hiding filter ({@link canMoveRows}). The table repaints after a drop; from code use
307
+ * `tableApi.moveRows()`.
308
+ *
309
+ * @param {number[]} rowIndices - display indices of the dragged rows
310
+ * @param {RowDropTargetIf} target - the drop target (display index before the move)
311
+ * @returns {boolean} true if moved
312
+ *
313
+ * @example
314
+ * bodyAreaModel.moveRows([2], { rowIndex: 6, position: "after" }); // row 3 becomes row 7
315
+ *
316
+ * @since 1.1.95
317
+ */
318
+ moveRows(rowIndices: number[], target: RowDropTargetIf): boolean;
283
319
  /**
284
320
  * Gets the value at the specified row and column indices.
285
321
  * This method handles tree rows and nested properties.
@@ -302,6 +338,35 @@ export declare class AreaModelObjectArray<T> extends AbstractAreaModel<T> implem
302
338
  * }
303
339
  */
304
340
  getValueAt(rowIndex: number, columnIndex: number): any;
341
+ /**
342
+ * The text of a cell: for a „Show values as“ column the formatted value (`"66.7%"`), for a formula error its code;
343
+ * otherwise like before (`'' + value`).
344
+ *
345
+ * @param {number} rowIndex - the row
346
+ * @param {number} columnIndex - the column
347
+ * @returns {string} the text
348
+ */
349
+ getTextValueAt(rowIndex: number, columnIndex: number): string;
350
+ /** A „Show values as“ column without an own body renderer shows the formatted text. */
351
+ getCellRenderer(rowIndex: number, columnIndex: number): CellRendererIf | undefined;
352
+ /** The message of a formula error as tooltip. */
353
+ getTooltipAt(rowIndex: number, columnIndex: number): any;
354
+ /**
355
+ * Drops derived values: cached aggregates of formulas and „Show values as“ (called by the table on filter,
356
+ * sorting, editing, `updateCells` and `api.repaint()`).
357
+ *
358
+ * @param {DerivedValuesChangeIf} [_change] - the changed rows (not used)
359
+ * @since 1.1.95
360
+ */
361
+ invalidateDerivedValues(_change?: DerivedValuesChangeIf): void;
362
+ /** „Show values as“ of one cell; the reference are the filtered rows in display order. */
363
+ protected applyShowValuesAs(rowIndex: number, property: string, value: unknown, options: ShowValuesAsIf): unknown;
364
+ protected readPropertyPath(t: any, property: string): unknown;
365
+ protected getAggregationSourceForValues(): {
366
+ signal: unknown;
367
+ length: number;
368
+ rows: () => ArrayLike<unknown>;
369
+ };
305
370
  /**
306
371
  * Retrieves the filtered and sorted rows from the dataset.
307
372
  * These rows are used for rendering the table.
@@ -565,6 +630,11 @@ export declare class AreaModelObjectArray<T> extends AbstractAreaModel<T> implem
565
630
  * const sortedRows = [...this.getFilteredRows()].sort(comparator);
566
631
  */
567
632
  protected genericFlatTableSortComparator(property: string, f: number): (a: T, b: T) => number;
633
+ /**
634
+ * The value of a row for a sort pass: a calculated column is computed once per row (memo for this pass, not
635
+ * 2·n·log n times); formula errors sort last (as `undefined`).
636
+ */
637
+ protected createSortValueReader(property: string): (row: T) => unknown;
568
638
  /**
569
639
  * Recursively gets a value from an object using an array of property names.
570
640
  * This is a helper method used by `getValueByT` to access nested properties.