@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
@@ -13,6 +13,15 @@ import { AreaModelIf } from './data/tablemodel/areamodel/area-model.if.js';
13
13
  import { PivotConfigIf } from './pivot/pivot-config.if.js';
14
14
  import { RowGroupField } from './rowgroup/row-group-field.type.js';
15
15
  import { SelectionAggregatesIf } from './aggregation/selection-aggregates.if.js';
16
+ import { ShowValuesAsConfig } from './calc/show-values-as.type.js';
17
+ import { FormulaOptionsIf } from './formula/formula-options.if.js';
18
+ import { FormulaError } from './formula/formula-error.js';
19
+ import { RowDropTargetIf } from './row-drag/row-drop-target.if.js';
20
+ import { CellChangeIf, CellChangeSource } from './batch-edit/cell-change.if.js';
21
+ import { CellValueChangeIf } from './batch-edit/cell-value-change.if.js';
22
+ import { FillDirection } from './fill/fill-direction.type.js';
23
+ import { FillMode } from './fill/fill-series.js';
24
+ import { CellRange } from './data/common/cell-range.js';
16
25
 
17
26
  /**
18
27
  * The public API of one table – everything you do with a table after it has been created:
@@ -37,7 +46,8 @@ import { SelectionAggregatesIf } from './aggregation/selection-aggregates.if.js'
37
46
  * | sort | {@link sortByColumn}, {@link sort}, {@link reSort} |
38
47
  * | filter | {@link externalFilterChanged} (with {@link TableOptionsIf.externalFilterFunction}) |
39
48
  * | export | {@link copyToClipboard}, {@link downloadExcel} |
40
- * | change data | {@link updateCells}, {@link setRows}, {@link addRows}, {@link updateRows}, {@link removeRows} |
49
+ * | change data | {@link updateCells}, {@link setRows}, {@link addRows}, {@link updateRows}, {@link removeRows}, {@link applyCellChanges} |
50
+ * | fill, batch edit | {@link fillRange}, {@link startBatchEdit}, {@link getPendingChanges}, {@link commitBatchEdit}, {@link cancelBatchEdit} |
41
51
  * | redraw | {@link repaint} (same row count), {@link repaintHard} (row count or heights changed) |
42
52
  * | columns | {@link autoResizeColumns}, {@link setColumnWidth}, {@link recalcColumnWidths}, {@link getColumnIndex} |
43
53
  * | tree | {@link expandAll}, {@link collapseAll}, {@link setRowExpanded} |
@@ -529,7 +539,7 @@ export declare class TableApi {
529
539
  * @param {ActionId} actionId - The ID of the action to trigger.
530
540
  * @return {void}
531
541
  */
532
- triggerAction(actionId: ActionId): void;
542
+ triggerAction(actionId: ActionId): boolean;
533
543
  /**
534
544
  * Retrieves the shortcut action mapping from the table's shortcut service.
535
545
  *
@@ -1297,6 +1307,69 @@ export declare class TableApi {
1297
1307
  * @since 1.1.94
1298
1308
  */
1299
1309
  getPivotConfig(): PivotConfigIf | undefined;
1310
+ /**
1311
+ * The value of a column for a row object – computed for a calculated column (`valueGetter`, `formula`), otherwise
1312
+ * the property (path). Use it in the external filter to filter by calculated values; a `TreeRow` is unwrapped.
1313
+ *
1314
+ * @param {any} row - a row object (or the `TreeRow` a tree filter gets)
1315
+ * @param {string} property - the property of the column
1316
+ * @returns {unknown} the value (a {@link FormulaError} for a formula error)
1317
+ *
1318
+ * @example
1319
+ * tableOptions.externalFilterFunction = (row: Item) => Number(tableApi.getRowValue(row, "total")) > 500;
1320
+ * tableApi.externalFilterChanged();
1321
+ *
1322
+ * @since 1.1.95
1323
+ */
1324
+ getRowValue(row: any, property: string): unknown;
1325
+ /**
1326
+ * „Show values as“: shows the numbers of a column differently – share of the column total, running total, rank …
1327
+ * (see {@link ShowValuesAs}) – or normal again (`undefined`). No new model: the table repaints.
1328
+ *
1329
+ * @param {string} property - the property of the column
1330
+ * @param {ShowValuesAsConfig | undefined} showValuesAs - the mode (with options) or `undefined` for the values
1331
+ * @returns {boolean} false if there is no such column or the mode is unknown
1332
+ *
1333
+ * @example
1334
+ * select.addEventListener("change", () =>
1335
+ * tableApi.setShowValuesAs("total", select.value ? select.value as ShowValuesAs : undefined));
1336
+ *
1337
+ * @since 1.1.95
1338
+ */
1339
+ setShowValuesAs(property: string, showValuesAs: ShowValuesAsConfig | undefined): boolean;
1340
+ /**
1341
+ * „Show values as“ of a column.
1342
+ *
1343
+ * @param {string} property - the property of the column
1344
+ * @returns {ShowValuesAsConfig | undefined} the configuration, `undefined` for none
1345
+ *
1346
+ * @example
1347
+ * const mode = tableApi.getShowValuesAs("total"); // "percentOfColumnTotal"
1348
+ *
1349
+ * @since 1.1.95
1350
+ */
1351
+ getShowValuesAs(property: string): ShowValuesAsConfig | undefined;
1352
+ /**
1353
+ * Sets (or replaces) the formula of a column – e.g. from an input field of your app. The formula is compiled
1354
+ * once; an invalid formula is set anyway (the cells show `#ERROR!` / `#NAME?`) and its error is returned, so you can
1355
+ * show the message next to the input. No code is ever executed (no `eval`). The table repaints; totals, sorting
1356
+ * and filter see the new values after `repaint`, `reSort()` resp. `externalFilterChanged()`.
1357
+ *
1358
+ * @param {string} property - the property of the column (it becomes a calculated column)
1359
+ * @param {string} formula - the formula, e.g. `"[price] * [quantity] * 1.19"`
1360
+ * @param {FormulaOptionsIf} [options] - own functions
1361
+ * @returns {FormulaError | undefined} the error of the formula itself, `undefined` if it is valid
1362
+ * (`#NAME?` also if there is no such column)
1363
+ *
1364
+ * @example
1365
+ * input.addEventListener("change", () => {
1366
+ * const error = tableApi.setColumnFormula("custom", input.value);
1367
+ * hint.textContent = error ? `${error.code} ${error.message}` : "";
1368
+ * });
1369
+ *
1370
+ * @since 1.1.95
1371
+ */
1372
+ setColumnFormula(property: string, formula: string, options?: FormulaOptionsIf): FormulaError | undefined;
1300
1373
  /**
1301
1374
  * Sorts the table data using a custom comparison function.
1302
1375
  *
@@ -1347,6 +1420,309 @@ export declare class TableApi {
1347
1420
  * tableApi.sort((a: TreeRowIf<OrderNode>, b: TreeRowIf<OrderNode>) => b.data.total - a.data.total);
1348
1421
  */
1349
1422
  sort(compareFn: (a: any, b: any) => number): void;
1423
+ /**
1424
+ * Moves body rows – the code way to do what row drag does (e.g. "up"/"down" buttons, the pointer alternative of
1425
+ * WCAG 2.5.7, or after the server confirmed an unmanaged drag). The body model must support it
1426
+ * ({@link AreaModelIf.moveRows}: object arrays, arrays of arrays, trees); no filter may hide rows and no column may be
1427
+ * sorted (asc/desc – a sorted view has no order of its own). Focus and a multi-row selection follow the rows; screen
1428
+ * readers hear "Row moved to position 7 of 20". No row drag events. `false` if nothing moved (also for a target
1429
+ * where the rows already are).
1430
+ *
1431
+ * @param {number[] | number} rowIndices - display index or indices of the rows to move
1432
+ * @param {RowDropTargetIf | number} target - drop target; a number = in front of that display index
1433
+ * (`getDisplayedRowCount()` = behind the last row)
1434
+ * @returns {boolean} true if the rows were moved
1435
+ *
1436
+ * @example
1437
+ * tableApi.moveRows(2, { rowIndex: 6, position: "after" }); // row 3 becomes row 7
1438
+ * tableApi.moveRows([0, 1], tableApi.getDisplayedRowCount()); // the first two rows to the end
1439
+ *
1440
+ * @example
1441
+ * // unmanaged drag: the server decides, then the table moves the rows
1442
+ * const listener = new EventAdapter();
1443
+ * listener.onRowDragEnd = async (evt: GeRowDragEvent) => {
1444
+ * if (evt.type === "end" && evt.target && (await fetch("/api/move", { method: "POST" })).ok) {
1445
+ * tableApi.moveRows(evt.rowIndices, evt.target);
1446
+ * }
1447
+ * };
1448
+ *
1449
+ * @since 1.1.95
1450
+ */
1451
+ moveRows(rowIndices: number[] | number, target: RowDropTargetIf | number): boolean;
1452
+ /**
1453
+ * True while rows are dragged with the pointer (mouse, touch, pen).
1454
+ *
1455
+ * @returns {boolean} true during a row drag
1456
+ *
1457
+ * @example
1458
+ * if (!tableApi.isRowDragging()) {
1459
+ * tableApi.updateCells([], true); // e.g. live data: wait until the drag is done
1460
+ * }
1461
+ *
1462
+ * @since 1.1.95
1463
+ */
1464
+ isRowDragging(): boolean;
1465
+ /**
1466
+ * Cancels a running row drag (like Escape): nothing is moved, `onRowDragEnd` gets the type `"cancel"`.
1467
+ *
1468
+ * @example
1469
+ * tableApi.cancelRowDrag(); // e.g. new data arrived from the server
1470
+ *
1471
+ * @since 1.1.95
1472
+ */
1473
+ cancelRowDrag(): void;
1474
+ /**
1475
+ * Writes values into body cells like the cell editor does – the one way the table changes cells (editor, fill
1476
+ * handle and batch commit use it too): cells that are not editable are skipped (`editable`, calculated columns
1477
+ * without `valueSetter`, group and pivot rows), values run through `editInputPipe` and {@link ColumnDefIf.validate}
1478
+ * (invalid ones are not written), totals and calculated columns follow, and **one** `onModelChanged` reports all
1479
+ * cells with old and new values. In a batch edit the values are marked as pending instead.
1480
+ *
1481
+ * Paste from the clipboard and undo/redo can be built on it: a {@link CellChangeIf} has the old and the new value.
1482
+ *
1483
+ * @param {CellValueChangeIf[]} changes - display row index, column (index or property) and value per cell
1484
+ * @param {CellChangeSource} [source="api"] - reported in {@link GeModelChangeEvent.source}
1485
+ * @returns {CellChangeIf[]} the written (batch edit: newly pending) cells
1486
+ *
1487
+ * @example
1488
+ * tableApi.applyCellChanges([
1489
+ * { rowIndex: 0, column: "price", value: 9.9 },
1490
+ * { rowIndex: 1, column: "price", value: 12 },
1491
+ * ]);
1492
+ *
1493
+ * @example
1494
+ * // undo the last change set (from onModelChanged)
1495
+ * const undo = (changes: CellChangeIf[]) => tableApi.applyCellChanges(
1496
+ * changes.map(c => ({ rowIndex: c.rowIndex, column: c.property, value: c.oldValue })));
1497
+ *
1498
+ * @since 1.1.96
1499
+ */
1500
+ applyCellChanges(changes: CellValueChangeIf[], source?: CellChangeSource): CellChangeIf[];
1501
+ /**
1502
+ * Fills cells next to a range – what dragging the fill handle does ({@link TableOptionsIf.fillHandle} is not
1503
+ * needed for this call): continues series (1, 2 → 3, 4; dates; `Item 1` → `Item 2`) or copies, writes with
1504
+ * {@link applyCellChanges} (one `onModelChanged`, source `"fill"`) and selects source and filled cells.
1505
+ *
1506
+ * @param {CellRange} source - the source range (body display indices)
1507
+ * @param {FillDirection} direction - `"down"`, `"up"`, `"right"` or `"left"`
1508
+ * @param {number} count - rows or columns to fill (clamped to the table)
1509
+ * @param {FillMode} [mode="auto"] - `"auto"`, `"alternate"` (like ⌥/Alt while dragging) or `"copy"`
1510
+ * @returns {number} the number of filled cells (batch edit: marked); `-1` if more than `maxCells`
1511
+ *
1512
+ * @example
1513
+ * // rows 0 and 1 of column 2 hold 1 and 2: rows 2…9 get 3…10
1514
+ * tableApi.fillRange(new CellRange(0, 2, 1, 2), "down", 8);
1515
+ *
1516
+ * @since 1.1.96
1517
+ */
1518
+ fillRange(source: CellRange, direction: FillDirection, count: number, mode?: FillMode): number;
1519
+ /**
1520
+ * True while the fill handle is dragged.
1521
+ *
1522
+ * @returns {boolean} true during a fill
1523
+ *
1524
+ * @example
1525
+ * if (!tableApi.isFilling()) {
1526
+ * tableApi.updateCells([], true); // e.g. live data: wait until the fill is done
1527
+ * }
1528
+ *
1529
+ * @since 1.1.96
1530
+ */
1531
+ isFilling(): boolean;
1532
+ /**
1533
+ * Cancels a running fill (like Escape): nothing is changed.
1534
+ *
1535
+ * @example
1536
+ * tableApi.cancelFill();
1537
+ *
1538
+ * @since 1.1.96
1539
+ */
1540
+ cancelFill(): void;
1541
+ /**
1542
+ * Expands or collapses the detail row of a master row (master/detail, {@link TableOptionsIf.masterDetail}). The
1543
+ * content is created when the detail row becomes visible; collapsing destroys it. Reports
1544
+ * {@link EventListenerIf.onDetailExpandedChanged} (source `"api"`).
1545
+ *
1546
+ * @param {number | object} rowOrIndex - display index or row object
1547
+ * @param {boolean} expanded - `true` = expand, `false` = collapse
1548
+ * @returns {boolean} false if nothing changed (no master row, already in that state, no master/detail)
1549
+ *
1550
+ * @example
1551
+ * tableApi.setDetailExpanded(0, true); // the first shown row
1552
+ * tableApi.setDetailExpanded(orders[42], true); // a row object (also if it is filtered out)
1553
+ *
1554
+ * @since 1.1.97
1555
+ */
1556
+ setDetailExpanded(rowOrIndex: number | object, expanded: boolean): boolean;
1557
+ /**
1558
+ * Expands a collapsed or collapses an expanded detail row (master/detail).
1559
+ *
1560
+ * @param {number | object} rowOrIndex - display index or row object
1561
+ * @returns {boolean} false if it is no master row (or no master/detail)
1562
+ *
1563
+ * @example
1564
+ * tableApi.toggleDetail(3); // e.g. from a button outside the table
1565
+ *
1566
+ * @since 1.1.97
1567
+ */
1568
+ toggleDetail(rowOrIndex: number | object): boolean;
1569
+ /**
1570
+ * True if the detail row of a master row is expanded (master/detail).
1571
+ *
1572
+ * @param {number | object} rowOrIndex - display index or row object
1573
+ * @returns {boolean} whether it is expanded
1574
+ *
1575
+ * @example
1576
+ * const open = tableApi.isDetailExpanded(3);
1577
+ *
1578
+ * @since 1.1.97
1579
+ */
1580
+ isDetailExpanded(rowOrIndex: number | object): boolean;
1581
+ /**
1582
+ * The master rows whose detail rows are expanded, in the order they were expanded (also rows that are filtered out).
1583
+ *
1584
+ * @returns {T[]} the row objects
1585
+ *
1586
+ * @example
1587
+ * const openOrders = tableApi.getExpandedDetailRows<Order>().map(order => order.id);
1588
+ *
1589
+ * @since 1.1.97
1590
+ */
1591
+ getExpandedDetailRows<T = any>(): T[];
1592
+ /**
1593
+ * Expands the detail rows of all shown master rows. The contents are created only when they scroll into view, so this
1594
+ * is cheap also for many rows. No event per row.
1595
+ *
1596
+ * @returns {number} the number of rows that were expanded
1597
+ *
1598
+ * @example
1599
+ * document.querySelector("#expand-all")!.addEventListener("click", () => tableApi.expandAllDetails());
1600
+ *
1601
+ * @since 1.1.97
1602
+ */
1603
+ expandAllDetails(): number;
1604
+ /**
1605
+ * Collapses all detail rows (their contents are destroyed). No event per row.
1606
+ *
1607
+ * @returns {number} the number of rows that were collapsed
1608
+ *
1609
+ * @example
1610
+ * tableApi.collapseAllDetails();
1611
+ *
1612
+ * @since 1.1.97
1613
+ */
1614
+ collapseAllDetails(): number;
1615
+ /**
1616
+ * Destroys the content of an expanded detail row and creates it again (`context.recreated` is `true`) – e.g. after
1617
+ * the data it shows changed.
1618
+ *
1619
+ * @param {number | object} rowOrIndex - display index or row object
1620
+ * @returns {boolean} false if the row is not expanded
1621
+ *
1622
+ * @example
1623
+ * order.items.push(newItem);
1624
+ * tableApi.refreshDetail(order);
1625
+ *
1626
+ * @since 1.1.97
1627
+ */
1628
+ refreshDetail(rowOrIndex: number | object): boolean;
1629
+ /**
1630
+ * The container of a detail row (the element your `detailRenderer` renders into) – only while its content exists
1631
+ * (it is created when the detail row becomes visible).
1632
+ *
1633
+ * @param {number | object} rowOrIndex - display index or row object
1634
+ * @returns {HTMLDivElement | undefined} the container
1635
+ *
1636
+ * @example
1637
+ * const notes = tableApi.getDetailElement(order)?.querySelector("textarea");
1638
+ *
1639
+ * @since 1.1.97
1640
+ */
1641
+ getDetailElement(rowOrIndex: number | object): HTMLDivElement | undefined;
1642
+ /**
1643
+ * Starts a batch edit: from now on edited and filled values are **collected** – shown in the cells (class
1644
+ * `ge-table-cell-pending`), but not written into your rows – until {@link commitBatchEdit} or
1645
+ * {@link cancelBatchEdit}. Sorting, filtering, totals, copy and Excel use the written data. For a batch edit that is
1646
+ * always on use {@link TableOptionsIf.batchEdit}.
1647
+ *
1648
+ * @returns {boolean} false if a batch edit is already running
1649
+ *
1650
+ * @example
1651
+ * editButton.onclick = () => tableApi.startBatchEdit();
1652
+ * saveButton.onclick = () => tableApi.commitBatchEdit();
1653
+ * discardButton.onclick = () => tableApi.cancelBatchEdit();
1654
+ *
1655
+ * @since 1.1.96
1656
+ */
1657
+ startBatchEdit(): boolean;
1658
+ /**
1659
+ * True while values are collected (after {@link startBatchEdit}, or always with {@link TableOptionsIf.batchEdit}).
1660
+ *
1661
+ * @returns {boolean} whether a batch edit runs
1662
+ *
1663
+ * @example
1664
+ * saveButton.hidden = !tableApi.isBatchEditing();
1665
+ *
1666
+ * @since 1.1.96
1667
+ */
1668
+ isBatchEditing(): boolean;
1669
+ /**
1670
+ * True if a batch edit has values that are not written yet.
1671
+ *
1672
+ * @returns {boolean} whether there are pending changes
1673
+ *
1674
+ * @example
1675
+ * window.addEventListener("beforeunload", evt => {
1676
+ * if (tableApi.hasPendingChanges()) evt.preventDefault(); // "Leave site? Changes may not be saved."
1677
+ * });
1678
+ *
1679
+ * @since 1.1.96
1680
+ */
1681
+ hasPendingChanges(): boolean;
1682
+ /**
1683
+ * The values of a batch edit that are not written yet: row object, property, old and new value, the current display
1684
+ * index (`-1` while a filter hides the row) and the message of an invalid value. The changes belong to the rows, not
1685
+ * to their positions – sorting and filtering don't mix them up.
1686
+ *
1687
+ * @returns {CellChangeIf[]} the pending cells
1688
+ *
1689
+ * @example
1690
+ * saveButton.onclick = async () => {
1691
+ * const changes = tableApi.getPendingChanges()
1692
+ * .map(c => ({ id: (c.row as { id: number }).id, field: c.property, value: c.newValue }));
1693
+ * const response = await fetch("/api/prices", { method: "PATCH", body: JSON.stringify(changes) });
1694
+ * if (response.ok) tableApi.commitBatchEdit();
1695
+ * };
1696
+ *
1697
+ * @since 1.1.96
1698
+ */
1699
+ getPendingChanges(): CellChangeIf[];
1700
+ /**
1701
+ * Writes the pending values of a batch edit into the rows (through `setValue`, like the editor) and ends a batch edit
1702
+ * started with {@link startBatchEdit}: **one** `onModelChanged` (source `"commit"`) with all cells, then
1703
+ * `onBatchEditChanged` with the type `"commit"`. While a value is invalid ({@link ColumnDefIf.validate}) nothing is
1704
+ * written and the result is `false` (unless {@link BatchEditOptionsIf.blockCommitOnInvalid} is `false`).
1705
+ *
1706
+ * @returns {boolean} true if written
1707
+ *
1708
+ * @example
1709
+ * if (!tableApi.commitBatchEdit()) {
1710
+ * alert("Please fix the marked cells first.");
1711
+ * }
1712
+ *
1713
+ * @since 1.1.96
1714
+ */
1715
+ commitBatchEdit(): boolean;
1716
+ /**
1717
+ * Discards the pending values of a batch edit (the cells show the data again) and ends a batch edit started with
1718
+ * {@link startBatchEdit}.
1719
+ *
1720
+ * @example
1721
+ * discardButton.onclick = () => tableApi.cancelBatchEdit();
1722
+ *
1723
+ * @since 1.1.96
1724
+ */
1725
+ cancelBatchEdit(): void;
1350
1726
  /**
1351
1727
  * Ensures that a specific row is visible in the viewport by scrolling if necessary.
1352
1728
  * This method checks if the target row is within the currently visible range and
@@ -36,6 +36,14 @@ import { ResizeHandler } from './resize-handler.js';
36
36
  import { ExcelServiceIf } from './service/excel-service.if.js';
37
37
  import { ColumnDefIf } from './data/tablemodel/column/column-def.if.js';
38
38
  import { LiveAnnouncer } from './aria/live-announcer.js';
39
+ import { GeDetailEventSource } from './data/common/event/ge-detail-event.js';
40
+ import { FillHandleOptionsIf } from './fill/fill-handle-options.if.js';
41
+ import { CellChangeIf, CellChangeSource } from './batch-edit/cell-change.if.js';
42
+ import { CellValueChangeIf } from './batch-edit/cell-value-change.if.js';
43
+ import { FillDirection } from './fill/fill-direction.type.js';
44
+ import { FillMode } from './fill/fill-series.js';
45
+ import { CellRange } from './data/common/cell-range.js';
46
+ import { AriaTextsIf } from './aria/aria-texts.if.js';
39
47
 
40
48
  /**
41
49
  * Creates and runs one table in a host element – the core that every framework wrapper uses.
@@ -146,6 +154,21 @@ export declare class TableScope extends RenderScope implements OnActionTriggered
146
154
  * @return {boolean} - Returns true if the action was triggered successfully, false otherwise.
147
155
  */
148
156
  onActionTriggered(actionId: ActionId): boolean;
157
+ /**
158
+ * The event listener of this table (the one given to the constructor, or an empty {@link EventAdapter}).
159
+ *
160
+ * @returns {EventListenerIf} the listener
161
+ * @since 1.1.95
162
+ */
163
+ getEventListener(): EventListenerIf;
164
+ /**
165
+ * Called after rows were moved in the body model (row drag, {@link TableApi.moveRows}): drops derived values
166
+ * (totals, „Show values as“) and forgets a sort function of `tableApi.sort(fn)` – the moved order wins.
167
+ * The caller repaints.
168
+ *
169
+ * @since 1.1.95
170
+ */
171
+ afterRowsMoved(): void;
149
172
  /**
150
173
  * Speaks a message to screen reader users (polite live region, see {@link LiveAnnouncer}).
151
174
  *
@@ -223,7 +246,106 @@ export declare class TableScope extends RenderScope implements OnActionTriggered
223
246
  * first, so the blur caused by closing does not commit the typed text ("change" event)
224
247
  */
225
248
  cancelEditing(editor?: HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement): void;
249
+ /**
250
+ * Writes the text of a cell editor (called by the input handler on "change"). Batch edit: the value is marked as
251
+ * pending instead. Uses {@link applyCellChanges} – one `onModelChanged` per edit, as before.
252
+ *
253
+ * @param {AreaIdent} areaIdent - only `"body"` is edited
254
+ * @param {number} rowIndex - display index
255
+ * @param {number} columnIndex - model index
256
+ * @param {string} value - the text of the editor
257
+ */
226
258
  updateModelValueAfterEdit(areaIdent: AreaIdent, rowIndex: number, columnIndex: number, value: string): void;
259
+ /**
260
+ * The one place that changes body cells (editor, fill handle, batch commit, {@link TableApi.applyCellChanges}):
261
+ * skips cells that are not editable (`editable`, calculated columns without `valueSetter`, group/pivot rows),
262
+ * applies `editInputPipe` and {@link ColumnDefIf.validate}, then writes – or, in a batch edit, marks the values as
263
+ * pending. Writing reports **one** `onModelChanged` with all cells; derived values (totals, calculated columns) are
264
+ * recomputed and the table repaints.
265
+ *
266
+ * @param {CellValueChangeIf[]} changes - cells and values (column as index or property)
267
+ * @param {CellChangeSource} [source="api"] - reported in the event
268
+ * @returns {CellChangeIf[]} the written (or, in a batch edit, newly pending) cells
269
+ * @since 1.1.96
270
+ */
271
+ applyCellChanges(changes: CellValueChangeIf[], source?: CellChangeSource): CellChangeIf[];
272
+ /**
273
+ * Writes cell values directly (no batch edit) – used by {@link applyCellChanges} and the batch commit.
274
+ *
275
+ * @param {{ rowIndex: number, columnIndex: number, value: unknown }[]} cells - resolved cells (display/model indices)
276
+ * @param {CellChangeSource} source - reported in the event
277
+ * @param {{ extraChanges?: CellChangeIf[], skipValidation?: boolean }} [opts] - changes written elsewhere (reported
278
+ * in the same event), no validation (commit: already checked)
279
+ * @returns {CellChangeIf[]} the written cells (including `extraChanges`)
280
+ * @since 1.1.96
281
+ */
282
+ writeCells(cells: {
283
+ rowIndex: number;
284
+ columnIndex: number;
285
+ value: unknown;
286
+ }[], source: CellChangeSource, opts?: {
287
+ extraChanges?: CellChangeIf[];
288
+ skipValidation?: boolean;
289
+ }): CellChangeIf[];
290
+ private writeCellsInternal;
291
+ /** Marks values as pending (batch edit): pipe, validation, then the batch edit service. */
292
+ private markPending;
293
+ /**
294
+ * Column property → index, range check, editable check (not for the editor: it was opened on an editable cell).
295
+ */
296
+ private resolveCellChanges;
297
+ /** The value after the column's `editInputPipe` (as `setValue` will store it). */
298
+ private pipeCellValue;
299
+ /** The message of {@link ColumnDefIf.validate} – `undefined` if valid or without validator. */
300
+ private validateCellValue;
301
+ /** The row object of a body row for events: the data of a `TreeRow`, `undefined` without objects. */
302
+ private rowObjectOf;
303
+ /**
304
+ * The options of the fill handle, `undefined` if it is off ({@link TableOptionsIf.fillHandle}).
305
+ *
306
+ * @returns {FillHandleOptionsIf | undefined} complete options
307
+ * @since 1.1.96
308
+ */
309
+ getFillHandleOptions(): FillHandleOptionsIf | undefined;
310
+ /**
311
+ * Fills cells next to a range (fill handle, Ctrl/⌘+D, {@link TableApi.fillRange}): computes the values
312
+ * ({@link computeFillChanges}, shown values – a batch edit's pending values count), writes them with
313
+ * {@link applyCellChanges} and selects source and filled cells.
314
+ *
315
+ * @param {CellRange} source - the source range (body display indices)
316
+ * @param {FillDirection} direction - down, up, right or left
317
+ * @param {number} count - rows or columns to fill (clamped to the table)
318
+ * @param {FillMode} [mode="auto"] - series or copy
319
+ * @param {CellRange} [selectAfter] - the selection afterwards (default: source and filled cells)
320
+ * @returns {number} the number of filled (or, in a batch edit, marked) cells; `-1` if refused (too many cells)
321
+ * @since 1.1.96
322
+ */
323
+ fillRange(source: CellRange, direction: FillDirection, count: number, mode?: FillMode, selectAfter?: CellRange): number;
324
+ /** The last selected range, clamped to the body (`undefined` without one). */
325
+ getFillSourceRange(): CellRange | undefined;
326
+ private normalizeRange;
327
+ /**
328
+ * Ctrl/⌘+D (down) and Ctrl/⌘+R (right): the first row (column) of the selected range is copied into the rest; a
329
+ * range of one row (column) gets the cell above (left of) it – like a spreadsheet.
330
+ *
331
+ * @param {"down" | "right"} direction - the key
332
+ * @returns {boolean} true if the key was used (the browser's own action – bookmark, reload – is prevented then)
333
+ */
334
+ private fillByKeyboard;
335
+ /**
336
+ * Sets `aria-keyshortcuts` of the host (the fill handle's keyboard alternative); removed again by {@link destroy}.
337
+ *
338
+ * @param {string | undefined} value - e.g. `"Control+D Control+R"`; `undefined` restores the value before
339
+ * @since 1.1.96
340
+ */
341
+ setHostKeyShortcuts(value: string | undefined): void;
342
+ /**
343
+ * The texts of fill and batch edit with defaults (an own {@link AriaTextsIf} implementation may lack them).
344
+ *
345
+ * @returns the texts
346
+ * @since 1.1.96
347
+ */
348
+ getEditTexts(): Required<Pick<AriaTextsIf, 'cellsFilled' | 'fillTooLarge' | 'pendingChanges' | 'changesCommitted' | 'changesDiscarded' | 'commitBlocked' | 'invalidValue'>>;
227
349
  /**
228
350
  * Retrieves the TableApi object.
229
351
  *
@@ -256,6 +378,69 @@ export declare class TableScope extends RenderScope implements OnActionTriggered
256
378
  * new TableScope(hostDiv, newTableModel, new SimpleDomService(), new TableOptions(), new EventAdapter()).firstInit();
257
379
  */
258
380
  destroy(): void;
381
+ /**
382
+ * True if a DOM event comes from a detail row (master/detail) – e.g. from a nested table. The mouse, keyboard and
383
+ * pointer handlers of this table ignore such events.
384
+ *
385
+ * @param {Event} evt - an event while it is dispatched
386
+ * @returns {boolean} whether it comes from the detail layer
387
+ * @since 1.1.97
388
+ */
389
+ isEventInDetail(evt: Event): boolean;
390
+ /**
391
+ * Expands or collapses the detail row of a master row (see {@link TableApi.setDetailExpanded}).
392
+ *
393
+ * @param {number | object} rowOrIndex - display index or row object
394
+ * @param {boolean} expanded - the new state
395
+ * @param {GeDetailEventSource} [source] - what changed it
396
+ * @returns {boolean} false if nothing changed
397
+ * @since 1.1.97
398
+ */
399
+ setDetailExpanded(rowOrIndex: unknown, expanded: boolean, source?: GeDetailEventSource): boolean;
400
+ /**
401
+ * Toggles the detail row of a master row (see {@link TableApi.toggleDetail}).
402
+ *
403
+ * @param {number | object} rowOrIndex - display index or row object
404
+ * @param {GeDetailEventSource} [source] - what changed it
405
+ * @returns {boolean} false if it is no master row
406
+ * @since 1.1.97
407
+ */
408
+ toggleDetail(rowOrIndex: unknown, source?: GeDetailEventSource): boolean;
409
+ /**
410
+ * Called by {@link MasterDetailService} after detail rows were expanded or collapsed: new row positions and scroll
411
+ * height, repaint, event, announcement, table state.
412
+ *
413
+ * @param {unknown} row - the master row (`undefined` for several rows)
414
+ * @param {number} rowIndex - its display index (-1 for several rows or a row that is not shown)
415
+ * @param {boolean} expanded - the new state
416
+ * @param {GeDetailEventSource} source - what changed it
417
+ * @param {boolean} [emit] - false: no event (several rows at once)
418
+ * @param {boolean} [layoutUpdated] - true: the row layout was already updated for this one row
419
+ * @since 1.1.97
420
+ */
421
+ onDetailToggled(row: unknown, rowIndex: number, expanded: boolean, source: GeDetailEventSource, emit?: boolean, layoutUpdated?: boolean): void;
422
+ /**
423
+ * Called by {@link MasterDetailService} after a detail row with `detailHeight: "auto"` got a new height: new scroll
424
+ * height and repaint (the rows below move).
425
+ *
426
+ * @since 1.1.97
427
+ */
428
+ onDetailHeightChanged(): void;
429
+ /**
430
+ * The focus came back from a detail row (Shift+Tab): its master row gets the focus (same column).
431
+ *
432
+ * @param {number} rowIndex - display index of the master row
433
+ * @since 1.1.97
434
+ */
435
+ /**
436
+ * A detail row that holds the keyboard focus goes away (collapsed, destroyed, scrolled out of view): the host takes
437
+ * the focus and the master row becomes the focused row – without a repaint (the caller repaints).
438
+ *
439
+ * @param {number} rowIndex - display index of the master row (-1: only the host)
440
+ * @since 1.1.97
441
+ */
442
+ takeFocusFromDetail(rowIndex: number): void;
443
+ focusDetailMaster(rowIndex: number): void;
259
444
  /**
260
445
  * Returns `true` once {@link destroy} has been called.
261
446
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@guiexpert/table",
3
- "version": "1.1.94",
3
+ "version": "1.1.97",
4
4
  "license": "MIT",
5
5
  "author": "Dr. Marc Kronberg <info@gui.expert> (https://gui.expert)",
6
6
  "repository": {