@guiexpert/table 1.1.96 → 1.1.98

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 (41) hide show
  1. package/README.md +7 -0
  2. package/css/main.css +192 -27
  3. package/css/table-classes.css +33 -9
  4. package/css/table-color-vars.css +12 -0
  5. package/css/table-fill-batch.css +9 -2
  6. package/css/table-master-detail.css +115 -0
  7. package/css/table-tree.css +23 -16
  8. package/index.cjs +50 -50
  9. package/index.d.ts +12 -0
  10. package/index.js +3996 -2631
  11. package/lib/table/aria/aria-ids.d.ts +7 -0
  12. package/lib/table/aria/aria-texts.d.ts +2 -0
  13. package/lib/table/aria/aria-texts.if.d.ts +14 -0
  14. package/lib/table/crud/crud-object-edit.d.ts +2 -0
  15. package/lib/table/crud/crud-object-view.d.ts +2 -0
  16. package/lib/table/crud/crud-table-model-factory.d.ts +2 -0
  17. package/lib/table/data/common/event/ge-detail-event.d.ts +40 -0
  18. package/lib/table/data/options/auto-restore-options.d.ts +2 -0
  19. package/lib/table/data/options/auto-restore-options.if.d.ts +6 -0
  20. package/lib/table/data/options/table-options.d.ts +8 -0
  21. package/lib/table/data/options/table-options.if.d.ts +35 -0
  22. package/lib/table/ele-scope.d.ts +51 -0
  23. package/lib/table/event-adapter.d.ts +3 -0
  24. package/lib/table/event-listener.if.d.ts +7 -0
  25. package/lib/table/master-detail/detail-renderer.if.d.ts +99 -0
  26. package/lib/table/master-detail/master-detail-options.d.ts +30 -0
  27. package/lib/table/master-detail/master-detail-options.if.d.ts +60 -0
  28. package/lib/table/master-detail/master-detail-service.d.ts +223 -0
  29. package/lib/table/render-scope.d.ts +73 -0
  30. package/lib/table/row-layout/row-layout.d.ts +120 -0
  31. package/lib/table/rtl/rtl-util.d.ts +126 -0
  32. package/lib/table/rtl/table-direction.type.d.ts +23 -0
  33. package/lib/table/service/convenience-dom.service.d.ts +49 -1
  34. package/lib/table/service/excel-export-options.if.d.ts +17 -0
  35. package/lib/table/service/excel-service.d.ts +8 -2
  36. package/lib/table/service/excel-service.if.d.ts +3 -1
  37. package/lib/table/state/table-state.if.d.ts +4 -1
  38. package/lib/table/state/table-state.service.d.ts +4 -0
  39. package/lib/table/table-api.d.ts +123 -2
  40. package/lib/table/table-scope.d.ts +69 -0
  41. package/package.json +1 -1
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Options of an Excel export ({@link ExcelService.downloadExcel}, {@link ExcelService.createExcelBlob}).
3
+ *
4
+ * @example
5
+ * // an Arabic sheet: column A on the right, like the table
6
+ * await new ExcelService().downloadExcel(matrix, "sales.xlsx", "", { rightToLeft: true });
7
+ *
8
+ * @since 1.1.98
9
+ * @category Export & clipboard
10
+ */
11
+ export interface ExcelExportOptionsIf {
12
+ /**
13
+ * The worksheet runs from right to left (`<sheetView rightToLeft="1">`): column A on the right.
14
+ * {@link TableApi.downloadExcel} sets it for a right-to-left table ({@link TableOptionsIf.direction}).
15
+ */
16
+ rightToLeft?: boolean;
17
+ }
@@ -1,3 +1,4 @@
1
+ import { ExcelExportOptionsIf } from './excel-export-options.if.js';
1
2
  import { ExcelServiceIf } from './excel-service.if.js';
2
3
 
3
4
  /**
@@ -32,18 +33,23 @@ export declare class ExcelService implements ExcelServiceIf {
32
33
  * @param {Array<Array<any>>} matrix - A 2D array representing the data to be included in the Excel file. Each inner array corresponds to a row in the sheet.
33
34
  * @param {string} [filename='file.xlsx'] - The name of the Excel file to be downloaded. Defaults to 'file.xlsx'.
34
35
  * @param {string} [author=''] - The author's name to include as metadata in the Excel file. Optional parameter.
36
+ * @param {ExcelExportOptionsIf} [options] - e.g. `{ rightToLeft: true }` for a sheet from right to left (since 1.1.98)
35
37
  * @return {void} Does not return a value.
36
38
  * @throws {Error} Throws an error if the input matrix is not a valid 2D array or if the filename is an empty string.
37
39
  */
38
- downloadExcel(matrix: Array<Array<any>>, filename?: string, author?: string): Promise<void>;
40
+ downloadExcel(matrix: Array<Array<any>>, filename?: string, author?: string, options?: ExcelExportOptionsIf): Promise<void>;
39
41
  /**
40
42
  * Creates the .xlsx file as a Blob (compressed), e.g. to upload it instead of downloading it.
41
43
  *
42
44
  * @example
43
45
  * const blob = await new ExcelService().createExcelBlob([["Name", "City"], ["Ann", "Berlin"]]);
44
46
  * await fetch("/api/upload", { method: "POST", body: blob });
47
+ *
48
+ * @example
49
+ * // a sheet from right to left (column A on the right), since 1.1.98
50
+ * const rtlBlob = await new ExcelService().createExcelBlob([["المدينة"], ["الرياض"]], "", { rightToLeft: true });
45
51
  */
46
- createExcelBlob(matrix: Array<Array<any>>, author?: string): Promise<Blob>;
52
+ createExcelBlob(matrix: Array<Array<any>>, author?: string, options?: ExcelExportOptionsIf): Promise<Blob>;
47
53
  private generateMap;
48
54
  private getX1WorksheetsSheet1Xml;
49
55
  /** Number format → cell style index (1-based; 0 = default), filled by withNumberFormats() before the sheet is written. */
@@ -1,3 +1,5 @@
1
+ import { ExcelExportOptionsIf } from './excel-export-options.if.js';
2
+
1
3
  /**
2
4
  * Writes an Excel file from a matrix of values. The default implementation is {@link ExcelService};
3
5
  * pass your own as last argument of the `TableScope` constructor to change how {@link TableApi.downloadExcel} exports.
@@ -6,7 +8,7 @@
6
8
  */
7
9
  export interface ExcelServiceIf {
8
10
  /** Creates the file from `matrix` (rows of values) and hands it to the browser as download. */
9
- downloadExcel(matrix: Array<Array<any>>, filename: string, author?: string): void | Promise<void>;
11
+ downloadExcel(matrix: Array<Array<any>>, filename: string, author?: string, options?: ExcelExportOptionsIf): void | Promise<void>;
10
12
  /**
11
13
  * True if the service understands cells of the form {@link ExcelFormattedValueIf} (`{ value, numberFormat }`).
12
14
  * Only then {@link TableApi.downloadExcel} exports percentages of „Show values as“ columns with the format `0.0%`;
@@ -21,11 +21,12 @@ export type TableStateRowId = string | number;
21
21
  * | `selection` | selected rows, columns, cells or ranges | `autoRestoreSelectedState` |
22
22
  * | `headerGroups` | collapsed header groups | `autoRestoreHeaderGroupState` |
23
23
  * | `custom` | data of your application ({@link TableApi.setCustomState}) | `autoRestoreCustomState` |
24
+ * | `details` | expanded detail rows of master/detail (since 1.1.97) | `autoRestoreDetailState` |
24
25
  *
25
26
  * @category State
26
27
  * @since 1.1.92
27
28
  */
28
- export type TableStatePart = "columns" | "sort" | "scroll" | "tree" | "checked" | "selection" | "headerGroups" | "custom";
29
+ export type TableStatePart = "columns" | "sort" | "scroll" | "tree" | "checked" | "selection" | "headerGroups" | "custom" | "details";
29
30
  /**
30
31
  * The state of a table as plain JSON: column layout, sorting, scroll position, tree, checked and selected rows,
31
32
  * header groups and data of your own. Get it with {@link TableApi.getState}, apply it with
@@ -72,6 +73,8 @@ export interface TableStateIf {
72
73
  tree?: TableTreeStateIf;
73
74
  /** The ids of the checked rows. */
74
75
  checkedRowIds?: TableStateRowId[];
76
+ /** The ids of the master rows with an expanded detail row (master/detail). @since 1.1.97 */
77
+ detailRowIds?: TableStateRowId[];
75
78
  /** The selection. */
76
79
  selection?: TableSelectionStateIf;
77
80
  /** The state of the header groups with a toggle. */
@@ -115,6 +115,10 @@ export declare class TableStateService {
115
115
  private computeScroll;
116
116
  private computeTree;
117
117
  private computeChecked;
118
+ /** The ids of the expanded master rows (master/detail, since 1.1.97); `undefined` without master/detail. */
119
+ private computeDetails;
120
+ /** Expands exactly the master rows with these ids (master/detail, since 1.1.97). */
121
+ private applyDetails;
118
122
  private computeSelection;
119
123
  /**
120
124
  * A cell range → ids and properties. A range to the last row / column (`MAX_SAFE_INTEGER`) has no `toRowId` /
@@ -1,3 +1,4 @@
1
+ import { ResolvedTableDirection } from './rtl/table-direction.type.js';
1
2
  import { GeOverlayKind } from './overlay/ge-overlay-kind.type.js';
2
3
  import { TableStateIf, TableStatePart } from './state/table-state.if.js';
3
4
  import { SortState } from './data/common/sort-state.type.js';
@@ -52,6 +53,7 @@ import { CellRange } from './data/common/cell-range.js';
52
53
  * | columns | {@link autoResizeColumns}, {@link setColumnWidth}, {@link recalcColumnWidths}, {@link getColumnIndex} |
53
54
  * | tree | {@link expandAll}, {@link collapseAll}, {@link setRowExpanded} |
54
55
  * | state | {@link getState}, {@link applyState}, {@link resetState} (with {@link TableOptionsIf.autoRestoreOptions}) |
56
+ * | right-to-left | {@link getDirection} (with {@link TableOptionsIf.direction}) |
55
57
  * | lifecycle | {@link destroy} |
56
58
  *
57
59
  * @example
@@ -218,7 +220,8 @@ export declare class TableApi {
218
220
  * This method allows programmatic scrolling of the table viewport to exact pixel positions.
219
221
  * It delegates the scrolling operation to the underlying TableScope instance.
220
222
  *
221
- * @param {number} px - The horizontal pixel coordinate to scroll to. Defaults to 0 (leftmost position).
223
+ * @param {number} px - The horizontal pixel coordinate to scroll to: px from the start of the row – 0 = the first
224
+ * column (leftmost position; right-to-left the rightmost, see {@link TableOptionsIf.direction}). Defaults to 0.
222
225
  * @param {number} py - The vertical pixel coordinate to scroll to. Defaults to 0 (topmost position).
223
226
  * @returns {void}
224
227
  *
@@ -233,7 +236,7 @@ export declare class TableApi {
233
236
  * @example
234
237
  * // Scroll only vertically, keeping the current horizontal position
235
238
  * // (an omitted or undefined px means 0; for rows use scrollToIndex(-1, rowIndex))
236
- * tableApi.scrollToPixel(tableApi.getTableScope().scrollViewport.scrollLeft, 300);
239
+ * tableApi.scrollToPixel(tableApi.getTableScope().getViewportScrollLeft(), 300);
237
240
  *
238
241
  * @example
239
242
  * // Example in a component that needs precise scrolling control
@@ -263,6 +266,23 @@ export declare class TableApi {
263
266
  * @see {@link ensureRowIsVisible} - For scrolling to make a specific row visible
264
267
  */
265
268
  scrollToPixel(px?: number, py?: number): void;
269
+ /**
270
+ * The writing direction of the table – {@link TableOptionsIf.direction} with `"auto"` already decided (from the page
271
+ * when the table was built).
272
+ *
273
+ * In `"rtl"` the first column is on the right; positions of the API stay "from the start": {@link scrollToPixel},
274
+ * {@link GeScrollEvent.scrollLeft} and the scroll part of {@link getState} count from the right edge.
275
+ *
276
+ * @returns {ResolvedTableDirection} `"ltr"` or `"rtl"`
277
+ *
278
+ * @example
279
+ * if (tableApi.getDirection() === "rtl") {
280
+ * console.info("first column on the right");
281
+ * }
282
+ *
283
+ * @since 1.1.98
284
+ */
285
+ getDirection(): ResolvedTableDirection;
266
286
  /**
267
287
  * Scrolls the table to display the specified row and column indices at the top-left corner of the viewport.
268
288
  *
@@ -1538,6 +1558,107 @@ export declare class TableApi {
1538
1558
  * @since 1.1.96
1539
1559
  */
1540
1560
  cancelFill(): void;
1561
+ /**
1562
+ * Expands or collapses the detail row of a master row (master/detail, {@link TableOptionsIf.masterDetail}). The
1563
+ * content is created when the detail row becomes visible; collapsing destroys it. Reports
1564
+ * {@link EventListenerIf.onDetailExpandedChanged} (source `"api"`).
1565
+ *
1566
+ * @param {number | object} rowOrIndex - display index or row object
1567
+ * @param {boolean} expanded - `true` = expand, `false` = collapse
1568
+ * @returns {boolean} false if nothing changed (no master row, already in that state, no master/detail)
1569
+ *
1570
+ * @example
1571
+ * tableApi.setDetailExpanded(0, true); // the first shown row
1572
+ * tableApi.setDetailExpanded(orders[42], true); // a row object (also if it is filtered out)
1573
+ *
1574
+ * @since 1.1.97
1575
+ */
1576
+ setDetailExpanded(rowOrIndex: number | object, expanded: boolean): boolean;
1577
+ /**
1578
+ * Expands a collapsed or collapses an expanded detail row (master/detail).
1579
+ *
1580
+ * @param {number | object} rowOrIndex - display index or row object
1581
+ * @returns {boolean} false if it is no master row (or no master/detail)
1582
+ *
1583
+ * @example
1584
+ * tableApi.toggleDetail(3); // e.g. from a button outside the table
1585
+ *
1586
+ * @since 1.1.97
1587
+ */
1588
+ toggleDetail(rowOrIndex: number | object): boolean;
1589
+ /**
1590
+ * True if the detail row of a master row is expanded (master/detail).
1591
+ *
1592
+ * @param {number | object} rowOrIndex - display index or row object
1593
+ * @returns {boolean} whether it is expanded
1594
+ *
1595
+ * @example
1596
+ * const open = tableApi.isDetailExpanded(3);
1597
+ *
1598
+ * @since 1.1.97
1599
+ */
1600
+ isDetailExpanded(rowOrIndex: number | object): boolean;
1601
+ /**
1602
+ * The master rows whose detail rows are expanded, in the order they were expanded (also rows that are filtered out).
1603
+ *
1604
+ * @returns {T[]} the row objects
1605
+ *
1606
+ * @example
1607
+ * const openOrders = tableApi.getExpandedDetailRows<Order>().map(order => order.id);
1608
+ *
1609
+ * @since 1.1.97
1610
+ */
1611
+ getExpandedDetailRows<T = any>(): T[];
1612
+ /**
1613
+ * Expands the detail rows of all shown master rows. The contents are created only when they scroll into view, so this
1614
+ * is cheap also for many rows. No event per row.
1615
+ *
1616
+ * @returns {number} the number of rows that were expanded
1617
+ *
1618
+ * @example
1619
+ * document.querySelector("#expand-all")!.addEventListener("click", () => tableApi.expandAllDetails());
1620
+ *
1621
+ * @since 1.1.97
1622
+ */
1623
+ expandAllDetails(): number;
1624
+ /**
1625
+ * Collapses all detail rows (their contents are destroyed). No event per row.
1626
+ *
1627
+ * @returns {number} the number of rows that were collapsed
1628
+ *
1629
+ * @example
1630
+ * tableApi.collapseAllDetails();
1631
+ *
1632
+ * @since 1.1.97
1633
+ */
1634
+ collapseAllDetails(): number;
1635
+ /**
1636
+ * Destroys the content of an expanded detail row and creates it again (`context.recreated` is `true`) – e.g. after
1637
+ * the data it shows changed.
1638
+ *
1639
+ * @param {number | object} rowOrIndex - display index or row object
1640
+ * @returns {boolean} false if the row is not expanded
1641
+ *
1642
+ * @example
1643
+ * order.items.push(newItem);
1644
+ * tableApi.refreshDetail(order);
1645
+ *
1646
+ * @since 1.1.97
1647
+ */
1648
+ refreshDetail(rowOrIndex: number | object): boolean;
1649
+ /**
1650
+ * The container of a detail row (the element your `detailRenderer` renders into) – only while its content exists
1651
+ * (it is created when the detail row becomes visible).
1652
+ *
1653
+ * @param {number | object} rowOrIndex - display index or row object
1654
+ * @returns {HTMLDivElement | undefined} the container
1655
+ *
1656
+ * @example
1657
+ * const notes = tableApi.getDetailElement(order)?.querySelector("textarea");
1658
+ *
1659
+ * @since 1.1.97
1660
+ */
1661
+ getDetailElement(rowOrIndex: number | object): HTMLDivElement | undefined;
1541
1662
  /**
1542
1663
  * Starts a batch edit: from now on edited and filled values are **collected** – shown in the cells (class
1543
1664
  * `ge-table-cell-pending`), but not written into your rows – until {@link commitBatchEdit} or
@@ -36,6 +36,7 @@ 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';
39
40
  import { FillHandleOptionsIf } from './fill/fill-handle-options.if.js';
40
41
  import { CellChangeIf, CellChangeSource } from './batch-edit/cell-change.if.js';
41
42
  import { CellValueChangeIf } from './batch-edit/cell-value-change.if.js';
@@ -124,6 +125,11 @@ export declare class TableScope extends RenderScope implements OnActionTriggered
124
125
  /** Last comparator passed to `sort()`; re-applied after filtering. Cleared by a header sort. */
125
126
  private lastSortCompareFn?;
126
127
  constructor(hostElement: HTMLDivElement, tableModel: TableModelIf, domService: DomServiceIf, tableOptions: TableOptionsIf, eventListener: EventListenerIf, copyService?: CopyServiceIf, excelService?: ExcelServiceIf);
128
+ /**
129
+ * The options of a nested table (master/detail `ctx.createTable()`): as given; without an own `direction` in a
130
+ * right-to-left table an object that reads everything else from the given options (prototype) and adds `"rtl"`.
131
+ */
132
+ private nestedTableOptions;
127
133
  /** Time of the last Tab key press on the page (the host is focused right after it). */
128
134
  private lastTabKeyTime;
129
135
  /**
@@ -377,6 +383,69 @@ export declare class TableScope extends RenderScope implements OnActionTriggered
377
383
  * new TableScope(hostDiv, newTableModel, new SimpleDomService(), new TableOptions(), new EventAdapter()).firstInit();
378
384
  */
379
385
  destroy(): void;
386
+ /**
387
+ * True if a DOM event comes from a detail row (master/detail) – e.g. from a nested table. The mouse, keyboard and
388
+ * pointer handlers of this table ignore such events.
389
+ *
390
+ * @param {Event} evt - an event while it is dispatched
391
+ * @returns {boolean} whether it comes from the detail layer
392
+ * @since 1.1.97
393
+ */
394
+ isEventInDetail(evt: Event): boolean;
395
+ /**
396
+ * Expands or collapses the detail row of a master row (see {@link TableApi.setDetailExpanded}).
397
+ *
398
+ * @param {number | object} rowOrIndex - display index or row object
399
+ * @param {boolean} expanded - the new state
400
+ * @param {GeDetailEventSource} [source] - what changed it
401
+ * @returns {boolean} false if nothing changed
402
+ * @since 1.1.97
403
+ */
404
+ setDetailExpanded(rowOrIndex: unknown, expanded: boolean, source?: GeDetailEventSource): boolean;
405
+ /**
406
+ * Toggles the detail row of a master row (see {@link TableApi.toggleDetail}).
407
+ *
408
+ * @param {number | object} rowOrIndex - display index or row object
409
+ * @param {GeDetailEventSource} [source] - what changed it
410
+ * @returns {boolean} false if it is no master row
411
+ * @since 1.1.97
412
+ */
413
+ toggleDetail(rowOrIndex: unknown, source?: GeDetailEventSource): boolean;
414
+ /**
415
+ * Called by {@link MasterDetailService} after detail rows were expanded or collapsed: new row positions and scroll
416
+ * height, repaint, event, announcement, table state.
417
+ *
418
+ * @param {unknown} row - the master row (`undefined` for several rows)
419
+ * @param {number} rowIndex - its display index (-1 for several rows or a row that is not shown)
420
+ * @param {boolean} expanded - the new state
421
+ * @param {GeDetailEventSource} source - what changed it
422
+ * @param {boolean} [emit] - false: no event (several rows at once)
423
+ * @param {boolean} [layoutUpdated] - true: the row layout was already updated for this one row
424
+ * @since 1.1.97
425
+ */
426
+ onDetailToggled(row: unknown, rowIndex: number, expanded: boolean, source: GeDetailEventSource, emit?: boolean, layoutUpdated?: boolean): void;
427
+ /**
428
+ * Called by {@link MasterDetailService} after a detail row with `detailHeight: "auto"` got a new height: new scroll
429
+ * height and repaint (the rows below move).
430
+ *
431
+ * @since 1.1.97
432
+ */
433
+ onDetailHeightChanged(): void;
434
+ /**
435
+ * The focus came back from a detail row (Shift+Tab): its master row gets the focus (same column).
436
+ *
437
+ * @param {number} rowIndex - display index of the master row
438
+ * @since 1.1.97
439
+ */
440
+ /**
441
+ * A detail row that holds the keyboard focus goes away (collapsed, destroyed, scrolled out of view): the host takes
442
+ * the focus and the master row becomes the focused row – without a repaint (the caller repaints).
443
+ *
444
+ * @param {number} rowIndex - display index of the master row (-1: only the host)
445
+ * @since 1.1.97
446
+ */
447
+ takeFocusFromDetail(rowIndex: number): void;
448
+ focusDetailMaster(rowIndex: number): void;
380
449
  /**
381
450
  * Returns `true` once {@link destroy} has been called.
382
451
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@guiexpert/table",
3
- "version": "1.1.96",
3
+ "version": "1.1.98",
4
4
  "license": "MIT",
5
5
  "author": "Dr. Marc Kronberg <info@gui.expert> (https://gui.expert)",
6
6
  "repository": {