@guiexpert/table 1.1.94 → 1.1.97

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 (85) hide show
  1. package/README.md +16 -0
  2. package/css/main.css +299 -0
  3. package/css/table-calc.css +7 -0
  4. package/css/table-color-vars.css +58 -0
  5. package/css/table-fill-batch.css +58 -0
  6. package/css/table-master-detail.css +95 -0
  7. package/css/table-row-drag.css +81 -0
  8. package/index.cjs +37 -35
  9. package/index.d.ts +34 -0
  10. package/index.js +9749 -5000
  11. package/lib/table/action/action-id.type.d.ts +1 -1
  12. package/lib/table/aria/aria-ids.d.ts +7 -0
  13. package/lib/table/aria/aria-texts.d.ts +13 -0
  14. package/lib/table/aria/aria-texts.if.d.ts +101 -0
  15. package/lib/table/batch-edit/batch-edit-options.d.ts +21 -0
  16. package/lib/table/batch-edit/batch-edit-options.if.d.ts +20 -0
  17. package/lib/table/batch-edit/batch-edit-service.d.ts +104 -0
  18. package/lib/table/batch-edit/cell-change.if.d.ts +39 -0
  19. package/lib/table/batch-edit/cell-value-change.if.d.ts +18 -0
  20. package/lib/table/batch-edit/pending-cell-changes.d.ts +104 -0
  21. package/lib/table/calc/column-values.d.ts +55 -0
  22. package/lib/table/calc/show-values-as-internals.d.ts +4 -0
  23. package/lib/table/calc/show-values-as.d.ts +87 -0
  24. package/lib/table/calc/show-values-as.type.d.ts +55 -0
  25. package/lib/table/calc/value-getter-context.if.d.ts +35 -0
  26. package/lib/table/calc/value-getter.type.d.ts +36 -0
  27. package/lib/table/data/common/event/ge-batch-edit-event.d.ts +46 -0
  28. package/lib/table/data/common/event/ge-detail-event.d.ts +40 -0
  29. package/lib/table/data/common/event/ge-model-change-event.d.ts +27 -1
  30. package/lib/table/data/common/event/ge-row-drag-event.d.ts +89 -0
  31. package/lib/table/data/options/auto-restore-options.d.ts +2 -0
  32. package/lib/table/data/options/auto-restore-options.if.d.ts +6 -0
  33. package/lib/table/data/options/table-options.d.ts +8 -0
  34. package/lib/table/data/options/table-options.if.d.ts +62 -0
  35. package/lib/table/data/tablemodel/areamodel/abstract-area-model.d.ts +66 -0
  36. package/lib/table/data/tablemodel/areamodel/area-model-aggregate-footer.d.ts +5 -0
  37. package/lib/table/data/tablemodel/areamodel/area-model-array-of-arrays.d.ts +24 -0
  38. package/lib/table/data/tablemodel/areamodel/area-model-object-array.d.ts +70 -0
  39. package/lib/table/data/tablemodel/areamodel/area-model-pivot.d.ts +31 -0
  40. package/lib/table/data/tablemodel/areamodel/area-model-row-groups.d.ts +23 -1
  41. package/lib/table/data/tablemodel/areamodel/area-model-tree.d.ts +83 -0
  42. package/lib/table/data/tablemodel/areamodel/area-model.if.d.ts +55 -0
  43. package/lib/table/data/tablemodel/column/column-def.d.ts +31 -1
  44. package/lib/table/data/tablemodel/column/column-def.if.d.ts +86 -0
  45. package/lib/table/ele-scope.d.ts +14 -0
  46. package/lib/table/event-adapter.d.ts +10 -0
  47. package/lib/table/event-listener.if.d.ts +39 -0
  48. package/lib/table/factory/table-factory.d.ts +11 -0
  49. package/lib/table/fill/compute-fill-changes.d.ts +67 -0
  50. package/lib/table/fill/fill-direction.type.d.ts +7 -0
  51. package/lib/table/fill/fill-handle-options.d.ts +29 -0
  52. package/lib/table/fill/fill-handle-options.if.d.ts +35 -0
  53. package/lib/table/fill/fill-handle-service.d.ts +55 -0
  54. package/lib/table/fill/fill-series.d.ts +60 -0
  55. package/lib/table/fill/fill-value-context.if.d.ts +36 -0
  56. package/lib/table/formula/compile-formula.d.ts +86 -0
  57. package/lib/table/formula/evaluator.d.ts +19 -0
  58. package/lib/table/formula/formula-ast.if.d.ts +42 -0
  59. package/lib/table/formula/formula-error.d.ts +59 -0
  60. package/lib/table/formula/formula-options.if.d.ts +41 -0
  61. package/lib/table/formula/formula-values.d.ts +13 -0
  62. package/lib/table/formula/functions.d.ts +18 -0
  63. package/lib/table/formula/parser.d.ts +10 -0
  64. package/lib/table/formula/tokenizer.d.ts +22 -0
  65. package/lib/table/master-detail/detail-renderer.if.d.ts +99 -0
  66. package/lib/table/master-detail/master-detail-options.d.ts +30 -0
  67. package/lib/table/master-detail/master-detail-options.if.d.ts +60 -0
  68. package/lib/table/master-detail/master-detail-service.d.ts +223 -0
  69. package/lib/table/pivot/pivot-value.type.d.ts +13 -0
  70. package/lib/table/render-scope.d.ts +154 -0
  71. package/lib/table/renderer/edit/input-cell-renderer.d.ts +1 -1
  72. package/lib/table/renderer/edit/select-cell-renderer.d.ts +1 -1
  73. package/lib/table/row-drag/move-rows.d.ts +157 -0
  74. package/lib/table/row-drag/row-drag-options.d.ts +28 -0
  75. package/lib/table/row-drag/row-drag-options.if.d.ts +36 -0
  76. package/lib/table/row-drag/row-drag-service.d.ts +130 -0
  77. package/lib/table/row-drag/row-drop-target.if.d.ts +29 -0
  78. package/lib/table/row-layout/row-layout.d.ts +120 -0
  79. package/lib/table/service/excel-service.d.ts +6 -0
  80. package/lib/table/service/excel-service.if.d.ts +35 -0
  81. package/lib/table/state/table-state.if.d.ts +4 -1
  82. package/lib/table/state/table-state.service.d.ts +4 -0
  83. package/lib/table/table-api.d.ts +378 -2
  84. package/lib/table/table-scope.d.ts +185 -0
  85. package/package.json +1 -1
@@ -0,0 +1,35 @@
1
+ import { AggregatorRef } from '../aggregation/aggregator-name.type.js';
2
+
3
+ /**
4
+ * The second parameter of a {@link ValueGetter}: access to other columns of the same table – also calculated ones –
5
+ * and to column aggregates over the (filtered) rows. Formulas ({@link compileFormula}) use it for `[total]` and
6
+ * `SUM([total])`.
7
+ *
8
+ * @example
9
+ * ColumnDef.create({
10
+ * property: "share", headerLabel: "Share",
11
+ * valueGetter: (row: Item, ctx: ValueGetterContextIf) =>
12
+ * Number(ctx.getValue(row, "total")) / Number(ctx.aggregate("total", "sum"))
13
+ * });
14
+ *
15
+ * @since 1.1.95
16
+ * @category Calculated columns & formulas
17
+ */
18
+ export interface ValueGetterContextIf {
19
+ /** The property of the column that is computed. */
20
+ readonly property: string;
21
+ /**
22
+ * The value of another column of the row: its `valueGetter` (with protection against circular references –
23
+ * a cycle returns a `#CIRC!` {@link FormulaError}) or the property path.
24
+ */
25
+ getValue(row: any, property: string): unknown;
26
+ /** True if a column of the table shows this property. */
27
+ hasColumn(property: string): boolean;
28
+ /** True if that column is calculated (`valueGetter` or `formula`). */
29
+ isCalculated(property: string): boolean;
30
+ /**
31
+ * An aggregate of a column over the filtered rows of the body (cached until the data, filter or sorting changes),
32
+ * e.g. `aggregate("total", "sum")`. Values that are no numbers are skipped (see {@link Aggregators}).
33
+ */
34
+ aggregate(property: string, aggregator: AggregatorRef): unknown;
35
+ }
@@ -0,0 +1,36 @@
1
+ import { ValueGetterContextIf } from './value-getter-context.if.js';
2
+
3
+ /**
4
+ * Computes the value of a calculated column from the row (`ColumnDefIf.valueGetter`). The row is the data object
5
+ * (in a tree the `TreeRow.data`). The data is not changed – sorting, filter (`tableApi.getRowValue`), copy, Excel and
6
+ * totals use the computed value.
7
+ *
8
+ * Keep it fast and free of side effects: it runs for every visible cell on each render (values are not cached) and
9
+ * once per row for sorting and totals.
10
+ *
11
+ * @example
12
+ * const total: ValueGetter<Item> = row => row.price * row.quantity;
13
+ * ColumnDef.create({ property: "total", headerLabel: "Total", valueGetter: total });
14
+ *
15
+ * @typeParam T - the row type
16
+ * @since 1.1.95
17
+ * @category Calculated columns & formulas
18
+ */
19
+ export type ValueGetter<T = any> = (row: T, context: ValueGetterContextIf) => unknown;
20
+ /**
21
+ * Writes an edited value of a calculated column back into the row (`ColumnDefIf.valueSetter`). Without a setter a
22
+ * calculated column cannot be edited. Return `false` to reject the value.
23
+ *
24
+ * @example
25
+ * // "total" is price × quantity – editing the total changes the price
26
+ * const setTotal: ValueSetter<Item> = (row, value) => {
27
+ * const total = Number(value);
28
+ * if (!Number.isFinite(total) || !row.quantity) return false;
29
+ * row.price = total / row.quantity;
30
+ * };
31
+ *
32
+ * @typeParam T - the row type
33
+ * @since 1.1.95
34
+ * @category Calculated columns & formulas
35
+ */
36
+ export type ValueSetter<T = any> = (row: T, value: unknown) => boolean | void;
@@ -0,0 +1,46 @@
1
+ import { CellChangeIf } from '../../../batch-edit/cell-change.if.js';
2
+
3
+ /**
4
+ * What happened in a batch edit: `"start"` ({@link TableApi.startBatchEdit}), `"change"` (values were marked or a
5
+ * value was set back), `"commit"` (written), `"cancel"` (discarded).
6
+ *
7
+ * @category Fill handle & batch edit
8
+ * @since 1.1.96
9
+ */
10
+ export type GeBatchEditEventType = "start" | "change" | "commit" | "cancel";
11
+ /**
12
+ * Reported by {@link EventListenerIf.onBatchEditChanged}: the pending changes of a batch edit changed – e.g. to enable
13
+ * a "Save" button.
14
+ *
15
+ * @example
16
+ * const listener = new EventAdapter();
17
+ * listener.onBatchEditChanged = (evt: GeBatchEditEvent) => {
18
+ * saveButton.disabled = evt.pendingCount === 0 || evt.invalidCount > 0;
19
+ * discardButton.disabled = evt.pendingCount === 0;
20
+ * };
21
+ *
22
+ * @category Fill handle & batch edit
23
+ * @since 1.1.96
24
+ */
25
+ export declare class GeBatchEditEvent {
26
+ /** What happened. */
27
+ readonly type: GeBatchEditEventType;
28
+ /** The cells of this step (`"commit"`: the written cells, `"cancel"`: the discarded ones). */
29
+ readonly changes: CellChangeIf[];
30
+ /** The number of pending cells after this step. */
31
+ readonly pendingCount: number;
32
+ /** The number of pending cells with an invalid value. */
33
+ readonly invalidCount: number;
34
+ private readonly pendingProvider;
35
+ constructor(
36
+ /** What happened. */
37
+ type: GeBatchEditEventType,
38
+ /** The cells of this step (`"commit"`: the written cells, `"cancel"`: the discarded ones). */
39
+ changes: CellChangeIf[],
40
+ /** The number of pending cells after this step. */
41
+ pendingCount: number,
42
+ /** The number of pending cells with an invalid value. */
43
+ invalidCount: number, pendingProvider?: () => CellChangeIf[]);
44
+ /** All pending changes after this step (computed when read – it scans the shown rows once). */
45
+ get pendingChanges(): CellChangeIf[];
46
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * What opened or closed a detail row: a click on the icon (`"mouse"`), Enter (`"keyboard"`), the {@link TableApi}
3
+ * (`"api"`) or a restored table state (`"state"`).
4
+ *
5
+ * @category Master/detail
6
+ * @since 1.1.97
7
+ */
8
+ export type GeDetailEventSource = "mouse" | "keyboard" | "api" | "state";
9
+ /**
10
+ * Reported by {@link EventListenerIf.onDetailExpandedChanged}: a detail row was expanded or collapsed (master/detail,
11
+ * {@link MasterDetailOptionsIf}).
12
+ *
13
+ * @example
14
+ * const listener = new EventAdapter();
15
+ * listener.onDetailExpandedChanged = (evt: GeDetailEvent) => {
16
+ * console.log(`order ${evt.row.id} ${evt.expanded ? "opened" : "closed"} (${evt.source})`);
17
+ * };
18
+ *
19
+ * @category Master/detail
20
+ * @since 1.1.97
21
+ */
22
+ export declare class GeDetailEvent<T = any> {
23
+ /** The master row. */
24
+ readonly row: T;
25
+ /** Its display index (`-1` if it is not shown, e.g. filtered out). */
26
+ readonly rowIndex: number;
27
+ /** `true` = expanded, `false` = collapsed. */
28
+ readonly expanded: boolean;
29
+ /** What changed it. */
30
+ readonly source: GeDetailEventSource;
31
+ constructor(
32
+ /** The master row. */
33
+ row: T,
34
+ /** Its display index (`-1` if it is not shown, e.g. filtered out). */
35
+ rowIndex: number,
36
+ /** `true` = expanded, `false` = collapsed. */
37
+ expanded: boolean,
38
+ /** What changed it. */
39
+ source: GeDetailEventSource);
40
+ }
@@ -1,9 +1,13 @@
1
1
  import { GeCellIndices } from './ge-cell-indices.js';
2
+ import { CellChangeIf, CellChangeSource } from '../../../batch-edit/cell-change.if.js';
2
3
 
3
4
  /**
4
5
  * Reported by {@link EventListenerIf.onModelChanged} after cells were edited: the row and column indices
5
6
  * of the changed cells. The new values are already written into the area model.
6
7
  *
8
+ * Since 1.1.96 the event comes **once per change** – an edit, a fill (fill handle, Ctrl/⌘+D), a commit of a batch
9
+ * edit or {@link TableApi.applyCellChanges} – with all cells, and {@link changes} tells old and new values.
10
+ *
7
11
  * @example
8
12
  * override onModelChanged(evt: GeModelChangeEvent): void {
9
13
  * for (const { rowIndex } of evt.cells) {
@@ -19,6 +23,28 @@ import { GeCellIndices } from './ge-cell-indices.js';
19
23
  */
20
24
  export declare class GeModelChangeEvent {
21
25
  cells: GeCellIndices[];
22
- constructor(cells: GeCellIndices[]);
26
+ /**
27
+ * The changed cells with row object, property, old and new value (since 1.1.96; `undefined` for events created by
28
+ * own code with the old constructor).
29
+ */
30
+ changes?: CellChangeIf<any>[] | undefined;
31
+ /** What changed the cells: `"edit"`, `"fill"`, `"commit"` or `"api"` (since 1.1.96). */
32
+ source?: CellChangeSource | undefined;
33
+ constructor(cells: GeCellIndices[],
34
+ /**
35
+ * The changed cells with row object, property, old and new value (since 1.1.96; `undefined` for events created by
36
+ * own code with the old constructor).
37
+ */
38
+ changes?: CellChangeIf<any>[] | undefined,
39
+ /** What changed the cells: `"edit"`, `"fill"`, `"commit"` or `"api"` (since 1.1.96). */
40
+ source?: CellChangeSource | undefined);
41
+ /**
42
+ * An event for a change set (since 1.1.96): `cells` from the row and column indices of the changes.
43
+ *
44
+ * @param {CellChangeIf[]} changes - the changed cells
45
+ * @param {CellChangeSource} source - what changed them
46
+ * @returns {GeModelChangeEvent} the event
47
+ */
48
+ static createForChanges(changes: CellChangeIf[], source: CellChangeSource): GeModelChangeEvent;
23
49
  static createSingle(rowIndex: number, columnIndex: number): GeModelChangeEvent;
24
50
  }
@@ -0,0 +1,89 @@
1
+ import { RowDropTargetIf } from '../../../row-drag/row-drop-target.if.js';
2
+
3
+ /**
4
+ * Phase of a row drag: `"start"` (the pointer moved past the threshold, or a move key), `"move"` (the drop target
5
+ * changed), `"end"` (dropped) or `"cancel"` (Escape, `pointercancel`, `tableApi.cancelRowDrag()`, destroy).
6
+ *
7
+ * @category Row drag
8
+ * @since 1.1.95
9
+ */
10
+ export type GeRowDragEventType = "start" | "move" | "end" | "cancel";
11
+ /**
12
+ * What started the drag: the pointer (mouse, touch, pen) on a drag handle, or the keyboard
13
+ * (`MOVE_ROW_UP` / `MOVE_ROW_DOWN`, Ctrl+Alt+↑/↓).
14
+ *
15
+ * @category Row drag
16
+ * @since 1.1.95
17
+ */
18
+ export type GeRowDragSource = "pointer" | "keyboard";
19
+ /**
20
+ * A row drag ({@link EventListenerIf.onRowDragStart}, {@link EventListenerIf.onRowDragMove},
21
+ * {@link EventListenerIf.onRowDragEnd}).
22
+ *
23
+ * Call {@link preventDefault} to veto: in `"start"` no drag happens, in `"move"` the target is invalid (no drop
24
+ * marker, no drop). In managed mode `"end"` comes after the move (`moved`, `toIndex` = the real position).
25
+ *
26
+ * @example
27
+ * const listener = new EventAdapter();
28
+ * listener.onRowDragEnd = (evt: GeRowDragEvent<Order>) => {
29
+ * if (evt.moved) {
30
+ * const ids = evt.rows.map(order => order.id);
31
+ * void fetch("/api/orders/move", { method: "POST", body: JSON.stringify({ ids, toIndex: evt.toIndex }) });
32
+ * }
33
+ * };
34
+ *
35
+ * @category Row drag
36
+ * @since 1.1.95
37
+ */
38
+ export declare class GeRowDragEvent<T = any> {
39
+ /** Phase of the drag. */
40
+ readonly type: GeRowDragEventType;
41
+ /** Pointer or keyboard. */
42
+ readonly source: GeRowDragSource;
43
+ /** The dragged row objects (tree: the `TreeRow`s) in display order. */
44
+ readonly rows: T[];
45
+ /** Display indices of the dragged rows when the drag started (ascending). */
46
+ readonly rowIndices: number[];
47
+ /** The current drop target – `undefined` if there is none (invalid, vetoed, outside the table). */
48
+ readonly target: RowDropTargetIf | undefined;
49
+ /**
50
+ * Flat tables: display index of the first dragged row after the drop (`0` = first row), `-1` without target.
51
+ * Trees: `-1` before the move; in a managed `"end"` the index the row really got.
52
+ */
53
+ toIndex: number;
54
+ /** true if the pointer is outside the table – e.g. over an own drop zone of the page. */
55
+ readonly outside: boolean;
56
+ /** true if the table moves the rows itself ({@link RowDragOptionsIf.managed}). */
57
+ readonly managed: boolean;
58
+ /** The browser event behind this event (pointer or keyboard). */
59
+ readonly originalEvent?: PointerEvent | KeyboardEvent | undefined;
60
+ /** true if the moved rows changed in the table (managed mode, `"end"`, not prevented). */
61
+ moved: boolean;
62
+ private prevented;
63
+ constructor(
64
+ /** Phase of the drag. */
65
+ type: GeRowDragEventType,
66
+ /** Pointer or keyboard. */
67
+ source: GeRowDragSource,
68
+ /** The dragged row objects (tree: the `TreeRow`s) in display order. */
69
+ rows: T[],
70
+ /** Display indices of the dragged rows when the drag started (ascending). */
71
+ rowIndices: number[],
72
+ /** The current drop target – `undefined` if there is none (invalid, vetoed, outside the table). */
73
+ target: RowDropTargetIf | undefined,
74
+ /**
75
+ * Flat tables: display index of the first dragged row after the drop (`0` = first row), `-1` without target.
76
+ * Trees: `-1` before the move; in a managed `"end"` the index the row really got.
77
+ */
78
+ toIndex: number,
79
+ /** true if the pointer is outside the table – e.g. over an own drop zone of the page. */
80
+ outside: boolean,
81
+ /** true if the table moves the rows itself ({@link RowDragOptionsIf.managed}). */
82
+ managed: boolean,
83
+ /** The browser event behind this event (pointer or keyboard). */
84
+ originalEvent?: PointerEvent | KeyboardEvent | undefined);
85
+ /** true after {@link preventDefault}. */
86
+ get defaultPrevented(): boolean;
87
+ /** Veto: no drag (`"start"`), invalid target (`"move"`); no effect in `"end"` and `"cancel"`. */
88
+ preventDefault(): void;
89
+ }
@@ -44,6 +44,8 @@ export declare class AutoRestoreOptions<T> implements AutoRestoreOptionsIf<T> {
44
44
  autoRestoreHeaderGroupState: boolean;
45
45
  /** @since 1.1.92 */
46
46
  autoRestoreCustomState: boolean;
47
+ /** @since 1.1.97 */
48
+ autoRestoreDetailState: boolean;
47
49
  getStorageKeyFn: undefined;
48
50
  /** @deprecated since 1.1.92 – never used by the table. */
49
51
  isSame: (r1: T, r2: T, options: AutoRestoreOptionsIf<T>) => boolean;
@@ -51,6 +51,7 @@ export type GetStorageKeyFn = (() => string);
51
51
  * | {@link autoRestoreCheckedState} | checked rows | 1.1.92 (declared before, without effect) |
52
52
  * | {@link autoRestoreSelectedState} | selection | 1.1.92 (declared before, without effect) |
53
53
  * | {@link autoRestoreCustomState} | your data ({@link TableApi.setCustomState}) | 1.1.92 |
54
+ * | {@link autoRestoreDetailState} | expanded detail rows (master/detail) | 1.1.97 |
54
55
  *
55
56
  * Before 1.1.92 sorting, scroll position and tree were stored under three keys (`<key>sortingState`,
56
57
  * `<key>scrollState`, `<key>collapsedExpandedState`); they are read once and removed with the next save.
@@ -103,6 +104,11 @@ export interface AutoRestoreOptionsIf<T> {
103
104
  * @since 1.1.92
104
105
  */
105
106
  autoRestoreCustomState?: boolean;
107
+ /**
108
+ * Remember the expanded detail rows of master/detail ({@link TableOptionsIf.masterDetail}, by {@link getRowId}).
109
+ * @since 1.1.97
110
+ */
111
+ autoRestoreDetailState?: boolean;
106
112
  /**
107
113
  * A unique, stable id per row (tree: per node). With the default of {@link AutoRestoreOptions} the table uses the
108
114
  * property `id`, then `uuid`; rows without them are not stored (since 1.1.92 – the JSON of the row would write
@@ -1,3 +1,4 @@
1
+ import { MasterDetailOptionsIf } from '../../master-detail/master-detail-options.if.js';
1
2
  import { DefaultRowHeightsIf } from './default-row-heights.if.js';
2
3
  import { TableOptionsIf } from './table-options.if.js';
3
4
  import { TreeOptions } from './tree-options.js';
@@ -12,6 +13,9 @@ import { ShortcutActionIdMapping } from '../../action/shortcut-actionid-mapping.
12
13
  import { HeaderGroupOptions } from './header-group-options.js';
13
14
  import { AriaTextsIf } from '../../aria/aria-texts.if.js';
14
15
  import { OverlayOptionsIf } from './overlay-options.if.js';
16
+ import { RowDragOptionsIf } from '../../row-drag/row-drag-options.if.js';
17
+ import { FillHandleOptionsIf } from '../../fill/fill-handle-options.if.js';
18
+ import { BatchEditOptionsIf } from '../../batch-edit/batch-edit-options.if.js';
15
19
 
16
20
  /**
17
21
  * All default options of a table – see {@link TableOptionsIf} for the meaning of each field.
@@ -80,4 +84,8 @@ export declare class TableOptions implements TableOptionsIf {
80
84
  ariaTexts?: Partial<AriaTextsIf>;
81
85
  /** Texts and behaviour of the "Loading…" / "No rows" overlays; missing fields use {@link OverlayOptions}. @since 1.1.92 */
82
86
  overlayOptions?: Partial<OverlayOptionsIf>;
87
+ rowDragOptions?: Partial<RowDragOptionsIf>;
88
+ fillHandle?: boolean | Partial<FillHandleOptionsIf>;
89
+ masterDetail?: Partial<MasterDetailOptionsIf>;
90
+ batchEdit?: boolean | Partial<BatchEditOptionsIf>;
83
91
  }
@@ -1,3 +1,7 @@
1
+ import { MasterDetailOptionsIf } from '../../master-detail/master-detail-options.if.js';
2
+ import { RowDragOptionsIf } from '../../row-drag/row-drag-options.if.js';
3
+ import { FillHandleOptionsIf } from '../../fill/fill-handle-options.if.js';
4
+ import { BatchEditOptionsIf } from '../../batch-edit/batch-edit-options.if.js';
1
5
  import { DefaultRowHeightsIf } from './default-row-heights.if.js';
2
6
  import { TreeOptionsIf } from './tree-options.if.js';
3
7
  import { FilterFunction } from '../common/filter-function.js';
@@ -317,6 +321,64 @@ export interface TableOptionsIf {
317
321
  * @since 1.1.92
318
322
  */
319
323
  overlayOptions?: Partial<OverlayOptionsIf>;
324
+ /**
325
+ * Row drag: managed or events only, multi-row drag, tree drop, auto-scroll (see {@link RowDragOptionsIf}). The drag
326
+ * handles come from {@link ColumnDefIf.rowDrag}. Fields you leave out keep their default ({@link RowDragOptions}).
327
+ *
328
+ * @example
329
+ * const tableOptions: TableOptionsIf = {
330
+ * ...new TableOptions(),
331
+ * rowDragOptions: { managed: false }, // only events – e.g. the server decides
332
+ * };
333
+ *
334
+ * @since 1.1.95
335
+ */
336
+ rowDragOptions?: Partial<RowDragOptionsIf>;
337
+ /**
338
+ * The fill handle: a small square at the bottom right corner of the selected range. Dragging it fills the
339
+ * neighbouring cells (copy, or a series: 1, 2, 3 …, dates, `Item 1` → `Item 2`); ⌥/Alt switches between series and
340
+ * copy. Keyboard: Ctrl/⌘+D fills down, Ctrl/⌘+R fills right. Needs a selection model of the type `"range"`;
341
+ * only editable cells are filled. `true` = defaults ({@link FillHandleOptions}).
342
+ *
343
+ * @example
344
+ * const selectionModel = new SelectionModel("range", "multi"); // one model – not a new one per call
345
+ * const tableOptions: TableOptionsIf = {
346
+ * ...new TableOptions(),
347
+ * getSelectionModel: () => selectionModel,
348
+ * fillHandle: true,
349
+ * };
350
+ *
351
+ * @since 1.1.96
352
+ */
353
+ fillHandle?: boolean | Partial<FillHandleOptionsIf>;
354
+ /**
355
+ * Batch edit always on: edited and filled values are collected (marked `ge-table-cell-pending`) and written with
356
+ * {@link TableApi.commitBatchEdit} or discarded with {@link TableApi.cancelBatchEdit}. Without it, start a batch
357
+ * edit with {@link TableApi.startBatchEdit} (it ends with commit or cancel).
358
+ *
359
+ * @example
360
+ * const tableOptions: TableOptionsIf = { ...new TableOptions(), batchEdit: true };
361
+ *
362
+ * @since 1.1.96
363
+ */
364
+ batchEdit?: boolean | Partial<BatchEditOptionsIf>;
365
+ /**
366
+ * Master/detail: master rows get an expand icon; below an expanded row a detail row over the full width shows any
367
+ * content (a nested table, a form, a framework component – see {@link MasterDetailOptionsIf}). Only for flat body
368
+ * models. Fields you leave out keep their default ({@link MasterDetailOptions}); `detailRenderer` is required.
369
+ *
370
+ * @example
371
+ * const tableOptions: TableOptionsIf = {
372
+ * ...new TableOptions(),
373
+ * masterDetail: {
374
+ * detailHeight: "auto",
375
+ * detailRenderer: (container, row) => { container.textContent = `Notes: ${row.notes}`; }
376
+ * }
377
+ * };
378
+ *
379
+ * @since 1.1.97
380
+ */
381
+ masterDetail?: Partial<MasterDetailOptionsIf>;
320
382
  /**
321
383
  * The state the table starts with: column layout, sorting, tree, checked and selected rows … – e.g. loaded from
322
384
  * 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
  }
@@ -140,6 +140,11 @@ export declare class AreaModelAggregateFooter<T = any> extends AreaModel impleme
140
140
  * @internal
141
141
  */
142
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;
143
148
  private compute;
144
149
  }
145
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;
@@ -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.