@guiexpert/table 1.1.107 → 1.1.108

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.
@@ -28,6 +28,7 @@ import { EventListenerIf } from './event-listener.if.js';
28
28
  import { ExcelExportOptionsIf } from './service/excel-export-options.if.js';
29
29
  import { SheetEngine } from './sheet/sheet-engine.js';
30
30
  import { ScrollPositionIf } from './data/common/scroll-position.if.js';
31
+ import { AppendRowsOptionsIf, AppendRowsResultIf, RowsAppendedOptionsIf } from './live-append/append-rows-options.if.js';
31
32
 
32
33
  /**
33
34
  * The public API of one table – everything you do with a table after it has been created:
@@ -201,7 +202,7 @@ export declare class TableApi {
201
202
  * };
202
203
  *
203
204
  * // Initialize your table with these options
204
- * const tableApi = new TableApi(tableScope);
205
+ * const tableApi = tableScope.getApi();
205
206
  *
206
207
  * // Later, when filter criteria change (e.g., in a search input handler)
207
208
  * function onFilterTextChanged() {
@@ -433,7 +434,7 @@ export declare class TableApi {
433
434
  *
434
435
  * @example
435
436
  * // Update data and repaint the table
436
- * const tableApi = new TableApi(tableScope);
437
+ * const tableApi = tableScope.getApi();
437
438
  * tableApi.setRows(newData);
438
439
  * tableApi.repaint();
439
440
  *
@@ -905,7 +906,7 @@ export declare class TableApi {
905
906
  *
906
907
  * @example
907
908
  * // Access the TableScope to perform a custom operation
908
- * const tableApi = new TableApi(myTableScope);
909
+ * const tableApi = myTableScope.getApi();
909
910
  * const tableScope = tableApi.getTableScope();
910
911
  *
911
912
  * // Example: Manually trigger a context menu at a specific location
@@ -1040,6 +1041,8 @@ export declare class TableApi {
1040
1041
  * Only works with an {@link AreaModelObjectArray} body – logs a console warning otherwise.
1041
1042
  * Does **not** repaint and resets filtering and sorting of the body: afterwards call
1042
1043
  * {@link externalFilterChanged} (if a filter function is set) and {@link repaintHard} (the row count changed).
1044
+ * Copies all rows (O(n)) – for live data (rows arriving all the time) use {@link appendRows}: in place, filter and
1045
+ * order only for the new rows, selection and position stay.
1043
1046
  *
1044
1047
  * @template T - The type of elements in the rows array
1045
1048
  * @param {T[]} rows - An array of data objects to append to the table's existing rows
@@ -1056,6 +1059,81 @@ export declare class TableApi {
1056
1059
  * tableApi.repaintHard();
1057
1060
  * tableApi.ensureRowIsVisible(tableApi.getDisplayedRowCount() - 1);
1058
1061
  */
1062
+ /**
1063
+ * **Live data** (a log tail, a ticker – since 1.1.108): appends rows to the body and shows them in **one** render
1064
+ * pass – O(k) for k new rows, not O(n).
1065
+ *
1066
+ * - **In place**: the rows are pushed to your array (`getAllRows()`), nothing is copied.
1067
+ * - **Filter and order** (`externalFilterFunction`, a sorted column or `sort(fn)`) apply to the new rows only:
1068
+ * filtered rows are kept but not shown, sorted rows are merged in (O(n + k), behind equal rows).
1069
+ * - **Selection, focus, anchor, checked rows** stay at their rows (the indices move along; no `onSelectionChanged`
1070
+ * unless a selected row was removed).
1071
+ * - **Position**: `follow: "auto"` (default) stays at the end if the view was at the end ("tail -f"), otherwise the
1072
+ * first visible row stays; `true` = always the end, `false` = always keep the row ("freeze scroll"). While the
1073
+ * mouse button is held in the table, `"auto"` keeps the row.
1074
+ * - **Ring buffer**: `maxRows` removes the oldest rows from the start.
1075
+ * - Rows behind the visible ones (a reader in the middle of the log): the cells are not rebuilt – only scroll height
1076
+ * and scroll bar change.
1077
+ *
1078
+ * Works with an {@link AreaModelObjectArray} body (`TableFactory.createTableModel({ rows, … })`) without cell
1079
+ * formulas; a tree, row grouping, pivot or spreadsheet → `false` and a console warning. Own area model:
1080
+ * {@link rowsAppended}.
1081
+ *
1082
+ * @param {T[]} rows - the new rows (appended in this order)
1083
+ * @param {AppendRowsOptionsIf} [options] - `follow` ("auto" | true | false), `maxRows`
1084
+ * @returns {AppendRowsResultIf | false} `{ added, shown, removed, following }`, or false
1085
+ *
1086
+ * @example
1087
+ * // a log viewer: new lines every animation frame, at most one million
1088
+ * const result = tableApi.appendRows(newLines, { follow: "auto", maxRows: 1_000_000 });
1089
+ * if (result && !result.following) {
1090
+ * console.log(`+${result.shown} new lines below`);
1091
+ * }
1092
+ *
1093
+ * @example
1094
+ * // "Freeze scroll" / "Scroll to end" buttons of the app
1095
+ * let frozen = false;
1096
+ * function onLines(lines: { time: string; text: string }[]) {
1097
+ * tableApi.appendRows(lines, { follow: frozen ? false : "auto" });
1098
+ * }
1099
+ * function scrollToEnd() {
1100
+ * frozen = false;
1101
+ * tableApi.repaintHard({ py: Number.MAX_SAFE_INTEGER });
1102
+ * }
1103
+ *
1104
+ * @since 1.1.108
1105
+ */
1106
+ appendRows<T>(rows: T[], options?: AppendRowsOptionsIf): AppendRowsResultIf | false;
1107
+ /**
1108
+ * **Live data with an own area model** (since 1.1.108): your model appended `count` rows at the end (and maybe
1109
+ * removed `removedAtStart` rows at the start) – the table moves selection, focus and anchor along, keeps the first
1110
+ * visible row or follows the end (`follow` as in {@link appendRows}) and renders once (not at all if only rows behind
1111
+ * the visible ones came in). Filtering and sorting are up to your model.
1112
+ *
1113
+ * @param {number} count - rows appended at the end
1114
+ * @param {RowsAppendedOptionsIf} [options] - `follow`, `removedAtStart`
1115
+ * @returns {AppendRowsResultIf} `{ added, shown, removed, following }`
1116
+ *
1117
+ * @example
1118
+ * class LogBody extends AreaModel {
1119
+ * readonly lines: string[] = [];
1120
+ * override getRowCount(): number { return this.lines.length; }
1121
+ * override getRowHeight(): number { return 20; }
1122
+ * override getConstantRowHeight(): number { return 20; }
1123
+ * override getValueAt(row: number): string { return this.lines[row]; }
1124
+ * }
1125
+ * const body = new LogBody();
1126
+ *
1127
+ * function onNewLines(newLines: string[]) {
1128
+ * for (const line of newLines) body.lines.push(line); // a loop: push(...lines) overflows above ~100,000
1129
+ * const dropped = Math.max(0, body.lines.length - 1_000_000);
1130
+ * body.lines.splice(0, dropped); // the app's own ring buffer
1131
+ * tableApi.rowsAppended(newLines.length, { follow: "auto", removedAtStart: dropped });
1132
+ * }
1133
+ *
1134
+ * @since 1.1.108
1135
+ */
1136
+ rowsAppended(count: number, options?: RowsAppendedOptionsIf): AppendRowsResultIf;
1059
1137
  addRows<T>(rows: T[]): void;
1060
1138
  /**
1061
1139
  * Inserts new rows at a specific position in the table body.
@@ -2342,7 +2420,7 @@ export declare class TableApi {
2342
2420
  * @example
2343
2421
  * ```typescript
2344
2422
  * // Get the first visible row index
2345
- * const tableApi = new TableApi(tableScope);
2423
+ * const tableApi = tableScope.getApi();
2346
2424
  * const firstVisibleIndex = tableApi.getFirstVisibleRowIndex();
2347
2425
  *
2348
2426
  * // Use the index for scroll position calculations
@@ -2,6 +2,7 @@ import { TableOverlayChange } from './overlay/table-overlay.js';
2
2
  import { GeOverlayKind } from './overlay/ge-overlay-kind.type.js';
3
3
  import { TableModelIf } from './data/tablemodel/table-model.if.js';
4
4
  import { AreaIdent } from './data/tablemodel/area-ident.type.js';
5
+ import { SideIdent } from './data/side-ident.type.js';
5
6
  import { GeMouseEvent } from './data/common/event/ge-mouse-event.js';
6
7
  import { DomServiceIf } from './service/dom-service.if.js';
7
8
  import { TableOptionsIf } from './data/options/table-options.if.js';
@@ -50,6 +51,7 @@ import { ClipboardService } from './clipboard/clipboard-service.js';
50
51
  import { UndoOptionsIf } from './undo/undo-options.if.js';
51
52
  import { UndoService } from './undo/undo-service.js';
52
53
  import { ShortcutActionIdMapping } from './action/shortcut-actionid-mapping.type.js';
54
+ import { AppendRowsOptionsIf, AppendRowsResultIf, RowsAppendedOptionsIf } from './live-append/append-rows-options.if.js';
53
55
 
54
56
  /**
55
57
  * Creates and runs one table in a host element – the core that every framework wrapper uses.
@@ -130,6 +132,8 @@ export declare class TableScope extends RenderScope implements OnActionTriggered
130
132
  private firstDraggingRendering;
131
133
  /** Last comparator passed to `sort()`; re-applied after filtering. Cleared by a header sort. */
132
134
  private lastSortCompareFn?;
135
+ /** Live data: the content position held while the mouse button was down in a view that followed the end. */
136
+ private followHeldAt?;
133
137
  /** Excel-style editing (`tableOptions.editing`), `undefined` = off. @since 1.1.99 */
134
138
  private readonly editingOptions?;
135
139
  /** Listeners of {@link TableApi.subscribe}. @since 1.1.99 */
@@ -750,6 +754,22 @@ export declare class TableScope extends RenderScope implements OnActionTriggered
750
754
  * @return {GeMouseEvent} - The created GeMouseEvent object.
751
755
  */
752
756
  createGeMouseEvent(mouseEvent: MouseEvent): GeMouseEvent;
757
+ /**
758
+ * Creates a GeMouseEvent for a given cell – used when the cell element of a mouse event is gone (a render replaced
759
+ * it between mousedown and mouseup, see {@link MouseHandler}) and the cell is known from the mousedown.
760
+ *
761
+ * @param mouseEvent - the original browser event
762
+ * @param cell - area, side, row and column of the cell, and the action of the pressed element
763
+ * @returns the event with row/column positions of the cell
764
+ * @since 1.1.108
765
+ */
766
+ createGeMouseEventForCell(mouseEvent: MouseEvent, cell: {
767
+ areaIdent?: AreaIdent;
768
+ sideIdent?: SideIdent;
769
+ rowIndex: number;
770
+ columnIndex: number;
771
+ action: string | null;
772
+ }): GeMouseEvent;
753
773
  /**
754
774
  * The element a mouse event belongs to – usually `evt.target`. The scroll viewport lies above the footer areas (its
755
775
  * horizontal scroll bar must stay usable), so a mouse event on a footer cell arrives at the viewport: then the footer
@@ -1039,6 +1059,37 @@ export declare class TableScope extends RenderScope implements OnActionTriggered
1039
1059
  private setAreaVisible;
1040
1060
  autoResizeColumns(recalcWrappers?: boolean): void;
1041
1061
  recalcWrappers(): void;
1062
+ /**
1063
+ * Live data (since 1.1.108): appends rows to an {@link AreaModelObjectArray} body in place – filter and sort order
1064
+ * only for the new rows, optional ring buffer – moves selection, focus and anchor along, keeps the first visible row
1065
+ * or follows the end, and renders once (not at all if only rows behind the visible ones came in).
1066
+ * See {@link TableApi.appendRows}.
1067
+ *
1068
+ * @param {T[]} rows - the new rows
1069
+ * @param {AppendRowsOptionsIf} [options] - follow mode, ring buffer
1070
+ * @returns {AppendRowsResultIf | false} what changed; false (with a warning) for other bodies (tree, row groups,
1071
+ * pivot, spreadsheet, cell formulas)
1072
+ */
1073
+ appendRows<T>(rows: T[], options?: AppendRowsOptionsIf): AppendRowsResultIf | false;
1074
+ /**
1075
+ * Live data (since 1.1.108): an own area model appended `count` rows at the end (and maybe removed some at the
1076
+ * start) – moves selection, focus and anchor along, keeps the first visible row or follows the end, renders once.
1077
+ * See {@link TableApi.rowsAppended}.
1078
+ *
1079
+ * @param {number} count - rows the model appended at the end (display order)
1080
+ * @param {RowsAppendedOptionsIf} [options] - follow mode, rows removed at the start
1081
+ * @returns {AppendRowsResultIf} what changed
1082
+ */
1083
+ notifyRowsAppended(count: number, options?: RowsAppendedOptionsIf): AppendRowsResultIf;
1084
+ /** The order new rows are sorted in: the last `sort(fn)`, otherwise the columns with a sort state. */
1085
+ protected getActiveSortComparator(): ((a: unknown, b: unknown) => number) | undefined;
1086
+ /** Moves the index-based state along, chooses the position and renders once (or only updates the row count). */
1087
+ private afterRowsAppended;
1088
+ /**
1089
+ * True if nothing else on the screen depends on the row count: no row number column (its width may grow), no
1090
+ * totals footer, no calculated/„show values as“ columns, no open detail rows.
1091
+ */
1092
+ private canKeepDrawnRows;
1042
1093
  reSort(): void;
1043
1094
  sort(compareFn: (a: any, b: any) => number): void;
1044
1095
  /**