@guiexpert/table 1.1.106 → 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.
@@ -0,0 +1,28 @@
1
+ /**
2
+ * A scroll position – the argument of {@link TableApi.repaintHard} (since 1.1.107): the rows are counted again and the
3
+ * table shows this position in the same render pass. A missing axis keeps its position.
4
+ *
5
+ * - `px`: the horizontal scroll position from the start (0 = the first center column at the start; right-to-left
6
+ * measured from the right edge, see {@link TableOptionsIf.direction}) – the same as the first argument of
7
+ * {@link TableApi.scrollToPixel} (scroll bar pixels, not a column offset: for a column use `scrollToIndex`).
8
+ * - `py`: a **content** position from the top of the first body row (with the heights of open detail rows above) – the same as the second
9
+ * argument of {@link TableApi.scrollToPixel}, also exact with a capped scroll height (more than
10
+ * {@link EleScope.maxScrollHeightInPx} px of rows).
11
+ *
12
+ * @example
13
+ * // live tail: rows were appended – count them and show row 500,000 (20 px rows) at the top
14
+ * tableApi.repaintHard({ py: 500_000 * 20 });
15
+ *
16
+ * @example
17
+ * // only horizontally: back to the first column, the vertical position stays
18
+ * tableApi.repaintHard({ px: 0 });
19
+ *
20
+ * @category Table & API
21
+ * @since 1.1.107
22
+ */
23
+ export interface ScrollPositionIf {
24
+ /** Horizontal scroll position in px from the start (like `scrollToPixel`). Missing: the horizontal position stays. */
25
+ px?: number;
26
+ /** Vertical content position in px from the top of the first body row. Missing: the vertical position stays. */
27
+ py?: number;
28
+ }
@@ -118,15 +118,25 @@ export interface TableOptionsIf {
118
118
  */
119
119
  horizontalBorderVisible: boolean;
120
120
  /**
121
- * The value of the css `overflowX` determines the behavior of horizontal overflow for the table body (scroll port).
121
+ * The css `overflow-x` of the scroll viewport (`.ge-table-scroll-viewport`): `"auto"` (default) shows the horizontal
122
+ * scroll bar only when the columns are wider than the table, `"scroll"` always.
122
123
  *
123
- * @typedef {'auto' | 'scroll'} OverflowX
124
+ * To hide the scroll bars completely use CSS – there is no `"hidden"` (wheel, touch, keyboard and the API still
125
+ * scroll, the table lays out without the bars):
126
+ * ```css
127
+ * .my-table .ge-table-scroll-viewport { scrollbar-width: none; }
128
+ * ```
124
129
  */
125
130
  overflowX: "auto" | "scroll";
126
131
  /**
127
- * The value of the css `overflowX` determines the behavior of vertical overflow for the table body (scroll port).
132
+ * The css `overflow-y` of the scroll viewport (`.ge-table-scroll-viewport`): `"auto"` (default) shows the vertical
133
+ * scroll bar only when the rows are higher than the body, `"scroll"` always.
128
134
  *
129
- * @typedef {'auto' | 'scroll'} OverflowX
135
+ * To hide the scroll bars completely use CSS – there is no `"hidden"` (wheel, touch, keyboard and the API still
136
+ * scroll, the table lays out without the bars):
137
+ * ```css
138
+ * .my-table .ge-table-scroll-viewport { scrollbar-width: none; }
139
+ * ```
130
140
  */
131
141
  overflowY: "auto" | "scroll";
132
142
  /**
@@ -19,6 +19,10 @@ export declare abstract class AbstractAreaModel<T> implements AreaModelIf, HasDe
19
19
  private columnValuesInstance?;
20
20
  private calcTextRenderer?;
21
21
  protected cellRenderers: (CellRendererIf | undefined)[];
22
+ /**
23
+ * @deprecated since 1.1.107 – filled by `init()` for `defaultRowHeight -1`, but no longer read:
24
+ * {@link getYPosByRowIndex} sums `getRowHeight()` (the cache missed rows added after `init()`).
25
+ */
22
26
  protected yPositions: number[];
23
27
  /** Cell formulas of the table (since 1.1.99, set by the table for a flat body). @internal */
24
28
  sheetCells?: SheetCellsIf;
@@ -41,6 +45,25 @@ export declare abstract class AbstractAreaModel<T> implements AreaModelIf, HasDe
41
45
  setRowChecked(rowIndex: number, checked: boolean): void;
42
46
  getMaxColspan(): number;
43
47
  getMaxRowspan(): number;
48
+ /**
49
+ * The top of a row in px from the first row of this area.
50
+ *
51
+ * With a constant row height (`getConstantRowHeight()` of the subclass – the built-in models return their
52
+ * `defaultRowHeight` as long as `getRowHeight()` is not overridden) it is `rowIndex × height` (O(1)); otherwise the
53
+ * sum of `getRowHeight()` of all rows above (O(n), always up to date). Before 1.1.107 it was
54
+ * `defaultRowHeight × rowIndex` also when a subclass overrode `getRowHeight()`, and for `defaultRowHeight -1` a cache
55
+ * from `init()` that missed rows added later. The table itself positions the body rows with its own row layout.
56
+ *
57
+ * @param {number} rowIndex - the index of the row
58
+ * @returns {number} px from the top of the first row
59
+ *
60
+ * @example
61
+ * // a subclass with taller even rows: the positions follow the overridden getRowHeight()
62
+ * class Striped extends AreaModelObjectArray<any> {
63
+ * override getRowHeight(rowIndex: number): number { return rowIndex % 2 ? 20 : 40; }
64
+ * }
65
+ * const y = new Striped("body", rows, 34, columnDefs).getYPosByRowIndex(3); // 40 + 20 + 40 = 100
66
+ */
44
67
  getYPosByRowIndex(rowIndex: number): number;
45
68
  init(): void;
46
69
  abstract externalFilterChanged<T>(predictFn: FilterFunction<T>): void;
@@ -47,7 +47,7 @@ export declare class AreaModelCellGroups implements AreaModelIf {
47
47
  getTooltipAt(_rowIndex: number, _columnIndex: number): any;
48
48
  getValueAt(rowIndex: number, columnIndex: number): any;
49
49
  getTextValueAt(rowIndex: number, columnIndex: number): string;
50
- getYPosByRowIndex(_rowIndex: number): number;
50
+ getYPosByRowIndex(rowIndex: number): number;
51
51
  getRowHeight(_rowIndex: number): number;
52
52
  isEditable(_rowIndex: number, _columnIndex: number): boolean;
53
53
  isFilterable(): boolean;
@@ -9,6 +9,7 @@ import { CellRendererIf } from '../../../renderer/cell-render.if.js';
9
9
  import { DerivedValuesChangeIf } from './derived-values-change.if.js';
10
10
  import { ShowValuesAsIf } from '../../../calc/show-values-as.type.js';
11
11
  import { RowDropTargetIf } from '../../../row-drag/row-drop-target.if.js';
12
+ import { AppendRowsContextIf, AppendRowsModelResultIf } from '../../../live-append/append-rows-options.if.js';
12
13
 
13
14
  /**
14
15
  * @class AreaModelObjectArray
@@ -175,6 +176,11 @@ export declare class AreaModelObjectArray<T> extends AbstractAreaModel<T> implem
175
176
  * @internal
176
177
  */
177
178
  protected sorterService: SorterService;
179
+ /**
180
+ * True while the displayed rows are in the order of {@link getAllRows} (unsorted, maybe filtered) – then the rows a
181
+ * ring buffer removes at the start are a prefix of the displayed rows (no O(n) search, see {@link appendRows}).
182
+ */
183
+ private displayInDataOrder;
178
184
  /** Cached „Show values as“ values per property (cleared on filter, sorting and invalidation). */
179
185
  private showValuesCache;
180
186
  /**
@@ -251,6 +257,43 @@ export declare class AreaModelObjectArray<T> extends AbstractAreaModel<T> implem
251
257
  * bodyAreaModel.setRows(newRows);
252
258
  */
253
259
  setRows(rows: T[]): void;
260
+ /**
261
+ * Live data (since 1.1.108): appends rows **in place** to {@link getAllRows} (your array – no copy) and to the
262
+ * displayed rows: the active filter runs only for the new rows (O(k)), with an active sort order they are sorted
263
+ * and merged in (O(n + k), stable – behind equal rows, like a sort of all rows). With `ctx.maxRows` the oldest
264
+ * rows are removed from the start (ring buffer) – in blocks of at least 1 % of `maxRows` (moving the rows of an
265
+ * array is O(n)), so the row count stays between 99 % and 100 % of `maxRows`. Use `tableApi.appendRows()` – it passes filter and order and moves
266
+ * selection, focus and scroll position along.
267
+ *
268
+ * @param {unknown[]} newRows - the new rows (appended in this order)
269
+ * @param {AppendRowsContextIf<T>} [ctx] - active filter, sort order, ring buffer size
270
+ * @returns {AppendRowsModelResultIf} the removed and inserted display positions
271
+ *
272
+ * @example
273
+ * const result = bodyAreaModel.appendRows(lines, { maxRows: 100_000 });
274
+ * console.log(result.insertedPositions); // where the new rows are shown
275
+ *
276
+ * @since 1.1.108
277
+ */
278
+ appendRows(newRows: unknown[], ctx?: AppendRowsContextIf<T>): AppendRowsModelResultIf;
279
+ /** Removes the first `count` rows of the data (in place) and of the displayed rows; their old display positions. */
280
+ private removeFirstRows;
281
+ private appendShown;
282
+ /** Sorts the new rows and merges them into the (sorted) displayed rows from the back – stable, in place. */
283
+ private mergeSorted;
284
+ /**
285
+ * The comparator of sort items (first item = primary key, the following ones break ties), as used by
286
+ * {@link doSort} – e.g. to sort new rows in (live data). `undefined` if no item sorts (state "").
287
+ *
288
+ * @param {SortItem[]} sortItems - column index and sort state per sorted column
289
+ * @returns {((a: T, b: T) => number) | undefined} the comparator
290
+ *
291
+ * @example
292
+ * const compare = bodyAreaModel.createSortComparator([new SortItem(2, "desc")]);
293
+ *
294
+ * @since 1.1.108
295
+ */
296
+ createSortComparator(sortItems: SortItem[]): ((a: T, b: T) => number) | undefined;
254
297
  /**
255
298
  * Filters both the original rows and filtered rows arrays using the provided predicate function.
256
299
  * Unlike `externalFilterChanged`, this method permanently modifies the original rows array.
@@ -1,4 +1,5 @@
1
1
  import { RowDropTargetIf } from '../../../row-drag/row-drop-target.if.js';
2
+ import { AppendRowsContextIf, AppendRowsModelResultIf } from '../../../live-append/append-rows-options.if.js';
2
3
  import { DerivedValuesChangeIf } from './derived-values-change.if.js';
3
4
  import { CellRendererIf } from '../../../renderer/cell-render.if.js';
4
5
  import { CheckedType } from '../../common/checked-type.js';
@@ -316,6 +317,19 @@ export interface AreaModelIf {
316
317
  * @since 1.1.95
317
318
  */
318
319
  moveRows?(rowIndices: number[], target: RowDropTargetIf): boolean;
320
+ /**
321
+ * Optional (live data, since 1.1.108): appends rows **in place** – the active filter only for the new rows, sorted
322
+ * in by the active order, oldest rows removed for a ring buffer – and reports the display positions, so the table
323
+ * moves selection, focus and scroll position along (`tableApi.appendRows()`). Implemented by
324
+ * {@link AreaModelObjectArray}; `false` = not supported (the table logs a warning and changes nothing).
325
+ *
326
+ * @param {unknown[]} rows - the new rows
327
+ * @param {AppendRowsContextIf} ctx - active filter, sort order, ring buffer size
328
+ * @returns {AppendRowsModelResultIf | false} what changed, or false
329
+ * @example
330
+ * const result = bodyModel.appendRows?.(lines, { maxRows: 100_000 });
331
+ */
332
+ appendRows?(rows: unknown[], ctx: AppendRowsContextIf): AppendRowsModelResultIf | false;
319
333
  /**
320
334
  * Optional: drops cached, derived values (e.g. the totals of an {@link AreaModelAggregateFooter}). The table calls
321
335
  * it for all three areas when data may have changed: filter, sorting, editing, `updateCells()` and the public
@@ -663,6 +677,10 @@ export interface AreaModelIf {
663
677
  * Returns the height of the row at the specified index, in pixels.
664
678
  * This method allows for variable row heights in the table.
665
679
  *
680
+ * The table draws the rows with these heights, and since 1.1.107 the height of the area (scroll height of the body,
681
+ * height of header and footer) is their sum as well – {@link getConstantRowHeight} × row count if given.
682
+ * `defaultRowHeights` of the table model only counts if every row height is 0.
683
+ *
666
684
  * @param {number} rowIndex - The index of the row
667
685
  * @returns {number} The height of the row in pixels
668
686
  *
@@ -699,6 +717,8 @@ export interface AreaModelIf {
699
717
  * of all rows above it – with millions of rows this keeps scrolling at the end of the table as fast as at
700
718
  * the beginning. Only return a number if `getRowHeight(i)` returns exactly this value for every row.
701
719
  * If the method is missing or returns `undefined`, the renderer falls back to iterating the row heights.
720
+ * The height of the area (the scroll height of the body) is then this value × row count – O(1) – and wins over
721
+ * `defaultRowHeights` of the table model (since 1.1.107; before, `defaultRowHeights` came first).
702
722
  *
703
723
  * @returns {number | undefined} The constant row height in pixels, or `undefined` if row heights may vary.
704
724
  *
@@ -15,6 +15,18 @@ export declare class ColAndRowspanModel {
15
15
  /** Row count of the area model at the last {@link init} (−1: not initialized yet). */
16
16
  protected rowCountAtInit: number;
17
17
  init(): void;
18
+ /** Adds the spans starting in a row. */
19
+ private scanRow;
20
+ /**
21
+ * Live data (since 1.1.108): rows were inserted (and maybe removed at the start) – scans only the inserted rows
22
+ * (O(k)) instead of all rows ({@link init}: O(n) per update, because most area models report a possible span of 31).
23
+ * Only possible while no span exists; otherwise (or before the first init) false – then {@link initIfRowCountChanged}
24
+ * builds the ranges again.
25
+ *
26
+ * @param {ArrayLike<number>} insertedPositions - display indices of the inserted rows (after the step), ascending
27
+ * @returns {boolean} true if the ranges are up to date
28
+ */
29
+ updateForInsertedRows(insertedPositions: ArrayLike<number>): boolean;
18
30
  /**
19
31
  * Builds the ranges again if the row count of the area model changed since the last {@link init} – e.g. after
20
32
  * `setRows()` or a filter (the old ranges could point to rows that no longer exist). Cheap otherwise.
@@ -157,10 +157,24 @@ export declare class TableModel implements TableModelIf {
157
157
  */
158
158
  getModel(rowAreaIdent: AreaIdent): AreaModelIf;
159
159
  /**
160
- * Calculates the height of the area identified by the given area identifier.
160
+ * Calculates the height of the area identified by the given area identifier – the height of the rows the table
161
+ * draws (since 1.1.107 the same source as the rendering):
162
+ *
163
+ * 1. `getConstantRowHeight()` of the area model (> 0): row count × that height – O(1), also for millions of rows,
164
+ * 2. otherwise the sum of `getRowHeight(i)` of all rows (variable heights; O(n), like the row layout of the body),
165
+ * 3. only if the model has no row heights of its own (every `getRowHeight(i)` is 0): row count ×
166
+ * `defaultRowHeights[area]`.
167
+ *
168
+ * Before 1.1.107 `defaultRowHeights[area]` came first: an area model with other heights got a wrong scroll height
169
+ * (a scroll bar without overflow, a wrong thumb). The built-in area models take their row height from
170
+ * `defaultRowHeights` anyway, so nothing changes for them.
161
171
  *
162
172
  * @param {AreaIdent} areaIdent - The identifier of the area.
163
- * @return {number} - The height of the area.
173
+ * @return {number} - The height of the area in px.
174
+ *
175
+ * @example
176
+ * // an own body with 20 px rows: 3000 rows are 60,000 px high – also if defaultRowHeights.body is 34
177
+ * const height = (tableModel as TableModel).getAreaHeight("body");
164
178
  */
165
179
  getAreaHeight(areaIdent: AreaIdent): number;
166
180
  /**
@@ -140,11 +140,20 @@ export declare class EleScope {
140
140
  /** The element that holds the rows of an area (`DivScope.child`). */
141
141
  protected getAreaChild(area: AreaIdent, side: SideIdent): HTMLDivElement;
142
142
  /**
143
- * Adjusts the containers and rows of the table based on the current state.
143
+ * Adjusts the containers and rows of the table based on the current state: lays out the areas
144
+ * ({@link layoutContainers}) and renders the visible rows ({@link adjustAfterScrolling}).
144
145
  *
145
146
  * @return {void}
146
147
  */
147
148
  adjustContainersAndRows(): void;
149
+ /**
150
+ * Lays out the scroll viewport, the nine areas, the separator borders, the overlay and the detail layer for the
151
+ * current size of the host and the current padding – without rendering the rows (that is
152
+ * {@link adjustAfterScrolling}). `repaintHard()` uses it to set a new scroll position before its one render pass.
153
+ *
154
+ * @since 1.1.107
155
+ */
156
+ protected layoutContainers(): void;
148
157
  /**
149
158
  * The height the table lays out header, body and footer in: the host height minus a horizontal scroll bar that
150
159
  * takes space (see {@link horizontalScrollBarHeight}).
@@ -103,7 +103,12 @@ export interface CreateTableModelPara {
103
103
  * @since 1.1.99
104
104
  */
105
105
  columnLetters: boolean;
106
- /** Row heights of header, body and footer in px. Default: `tableOptions.defaultRowHeights`, otherwise 34 / 34 / 34. */
106
+ /**
107
+ * Row heights of header, body and footer in px. Default: `tableOptions.defaultRowHeights`, otherwise 34 / 34 / 34.
108
+ * The area models built by the factory get these heights. An own area model (`bodyAreaModel` …) keeps its own:
109
+ * since 1.1.107 the table measures every area with the heights of its model (`getConstantRowHeight()`, otherwise
110
+ * `getRowHeight(i)`) – `defaultRowHeights` only counts for a model whose `getRowHeight()` returns 0.
111
+ */
107
112
  defaultRowHeights: DefaultRowHeightsIf;
108
113
  /** The columns: property, header label, width, classes, renderers, sorting, editing. See {@link ColumnDef}. */
109
114
  columnDefs: ColumnDefIf[];
@@ -1,6 +1,7 @@
1
1
  import { FocusType } from './focus.type.js';
2
2
  import { FocusModelIf } from './focus-model.if.js';
3
3
  import { EventFocusChangedListenerIf } from './event-focus-changed-listener.if.js';
4
+ import { RowIndexShift } from '../live-append/row-index-shift.js';
4
5
 
5
6
  /**
6
7
  * The `FocusModel` class is an implementation of the `FocusModelIf` interface.
@@ -63,6 +64,17 @@ export declare class FocusModel implements FocusModelIf {
63
64
  hasChanged(): boolean;
64
65
  clear(): void;
65
66
  setFocus(rowIndex: number, columnIndex: number): void;
67
+ /**
68
+ * Live data (since 1.1.108): the focused row moves along when rows were removed at the start or inserted
69
+ * (`tableApi.appendRows()`). The same row keeps the focus without an event; if it was removed, the next remaining
70
+ * row gets it (with an event – the table keeps it inside the rows).
71
+ *
72
+ * @param {RowIndexShift} shift - how the display indices move
73
+ *
74
+ * @example
75
+ * focusModel.adjustForRowChanges?.(new RowIndexShift([0, 1], [])); // row 5 → row 3
76
+ */
77
+ adjustForRowChanges(shift: RowIndexShift): void;
66
78
  hasFocus(rowIndex: number, columnIndex: number): boolean;
67
79
  getFocus(): [number, number];
68
80
  }
@@ -1,4 +1,5 @@
1
1
  import { EventFocusChangedListenerIf } from './event-focus-changed-listener.if.js';
2
+ import { RowIndexShift } from '../live-append/row-index-shift.js';
2
3
 
3
4
  /**
4
5
  * The focused cell of a table (moved by click and arrow keys; F2 edits it). The implementation is {@link FocusModel};
@@ -31,4 +32,14 @@ export interface FocusModelIf {
31
32
  hasChanged(): boolean;
32
33
  /** Resets {@link hasChanged}. */
33
34
  clearChanged(): void;
35
+ /**
36
+ * Optional (live data, since 1.1.108): the focused row moves along when rows were removed at the start or
37
+ * inserted (`tableApi.appendRows()`). Same row: no event. Focused row removed: the next remaining row gets the
38
+ * focus (with an event).
39
+ *
40
+ * @param {RowIndexShift} shift - how the display indices move
41
+ * @example
42
+ * focusModel.adjustForRowChanges?.(new RowIndexShift([0], []));
43
+ */
44
+ adjustForRowChanges?(shift: RowIndexShift): void;
34
45
  }
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Where the view goes after new rows came in (`tableApi.appendRows()` / `rowsAppended()`):
3
+ * - `"auto"` (default): it follows the end if it was at the end before ("tail -f"), otherwise the first visible row
4
+ * stays where it is;
5
+ * - `true`: always to the end;
6
+ * - `false`: the first visible row always stays (a "freeze scroll" button of the app).
7
+ *
8
+ * While the mouse button is held in the table (a click, a drag), `"auto"` keeps the row – the content does not slide
9
+ * away under the pointer.
10
+ *
11
+ * @category Live data
12
+ * @since 1.1.108
13
+ */
14
+ export type AppendFollowMode = "auto" | boolean;
15
+ /**
16
+ * Options of `tableApi.appendRows(rows, options)`.
17
+ *
18
+ * @example
19
+ * const result = tableApi.appendRows(lines, { follow: "auto", maxRows: 1_000_000 });
20
+ *
21
+ * @category Live data
22
+ * @since 1.1.108
23
+ */
24
+ export interface AppendRowsOptionsIf {
25
+ /** Where the view goes – see {@link AppendFollowMode}. Default `"auto"`. */
26
+ follow?: AppendFollowMode;
27
+ /**
28
+ * Ring buffer: keep at most this many rows – the oldest (first) rows of the data are removed from the start, in
29
+ * blocks of at least 1 % of `maxRows` (removing from the start of an array moves all rows: O(n) – the blocks make
30
+ * that rare), so the row count stays between 99 % and 100 % of `maxRows`.
31
+ * Selection, focus and the visible row stay at their rows; a selected row that is removed leaves the selection.
32
+ */
33
+ maxRows?: number;
34
+ }
35
+ /**
36
+ * Options of `tableApi.rowsAppended(count, options)` – an own area model that appended (and maybe removed) rows
37
+ * itself.
38
+ *
39
+ * @example
40
+ * const logLines: string[] = []; // the data of an own area model
41
+ * for (const line of newLines) logLines.push(line);
42
+ * const dropped = Math.max(0, logLines.length - 100_000);
43
+ * logLines.splice(0, dropped); // the app keeps its own ring buffer
44
+ * tableApi.rowsAppended(newLines.length, { follow: "auto", removedAtStart: dropped });
45
+ *
46
+ * @category Live data
47
+ * @since 1.1.108
48
+ */
49
+ export interface RowsAppendedOptionsIf {
50
+ /** Where the view goes – see {@link AppendFollowMode}. Default `"auto"`. */
51
+ follow?: AppendFollowMode;
52
+ /** Number of rows the app removed at the start (display indices 0 … n-1 before the step). Default 0. */
53
+ removedAtStart?: number;
54
+ }
55
+ /**
56
+ * Result of `tableApi.appendRows()` / `rowsAppended()`.
57
+ *
58
+ * @category Live data
59
+ * @since 1.1.108
60
+ */
61
+ export interface AppendRowsResultIf {
62
+ /** rows added to the data */
63
+ added: number;
64
+ /** of them shown (an active filter hides the others) */
65
+ shown: number;
66
+ /** rows removed at the start (`maxRows` / `removedAtStart`) */
67
+ removed: number;
68
+ /** true if the view follows the end after this update */
69
+ following: boolean;
70
+ }
71
+ /**
72
+ * What a table passes to {@link AreaModelIf.appendRows}: the active filter and sort order, so the model treats only
73
+ * the new rows (O(k) unsorted, O(n + k) sorted).
74
+ *
75
+ * @category Live data
76
+ * @since 1.1.108
77
+ */
78
+ export interface AppendRowsContextIf<T = unknown> {
79
+ /** the active filter (`tableOptions.externalFilterFunction`) – new rows it rejects are kept but not shown */
80
+ filter?: (row: T) => boolean;
81
+ /** the active sort order – new rows are inserted at their place (stable: behind equal rows) */
82
+ compare?: (a: T, b: T) => number;
83
+ /** ring buffer size, see {@link AppendRowsOptionsIf.maxRows} */
84
+ maxRows?: number;
85
+ }
86
+ /**
87
+ * What {@link AreaModelIf.appendRows} did – the table moves selection, focus and position along.
88
+ *
89
+ * @category Live data
90
+ * @since 1.1.108
91
+ */
92
+ export interface AppendRowsModelResultIf {
93
+ added: number;
94
+ shown: number;
95
+ /** rows removed from the data */
96
+ removed: number;
97
+ /** display indices **before** the step of the removed rows that were shown, ascending */
98
+ removedPositions: Int32Array;
99
+ /** display indices **after** the step of the new shown rows, ascending */
100
+ insertedPositions: Int32Array;
101
+ }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * How the display indices of the body rows move when rows are removed and inserted in one step (live data:
3
+ * `tableApi.appendRows()` / `rowsAppended()`, since 1.1.108) – so selection, focus and anchor stay at the same rows
4
+ * instead of being cleared. DOM-free.
5
+ *
6
+ * Order: first the `removed` rows leave (indices **before** the step), then the `inserted` rows come in (indices
7
+ * **after** the step). An old row keeps its relative order.
8
+ *
9
+ * @example
10
+ * // 2 rows dropped at the start (ring buffer), 3 appended at the end of 10 → 11 rows
11
+ * const shift = new RowIndexShift([0, 1], [8, 9, 10]);
12
+ * shift.mapIndex(5); // 3
13
+ * shift.mapIndex(1); // -1 (removed)
14
+ *
15
+ * @example
16
+ * // sorted: one row inserted at display index 2 (between the old rows 1 and 2)
17
+ * const shift = new RowIndexShift([], [2]);
18
+ * shift.mapRange(0, 4); // [[0, 1], [3, 5]] – the new row splits the range (it is not selected)
19
+ *
20
+ * @category Live data
21
+ * @since 1.1.108
22
+ */
23
+ export declare class RowIndexShift {
24
+ private readonly removed;
25
+ private readonly inserted;
26
+ /** inserted[j] - j: the number of old rows in front of the inserted row j (non-decreasing) */
27
+ private readonly insertBefore;
28
+ /**
29
+ * @param {ArrayLike<number>} removed - display indices before the step of the removed rows, ascending, unique
30
+ * @param {ArrayLike<number>} inserted - display indices after the step of the inserted rows, ascending, unique
31
+ */
32
+ constructor(removed?: ArrayLike<number>, inserted?: ArrayLike<number>);
33
+ /** True if nothing moves (no row removed, none inserted). */
34
+ isEmpty(): boolean;
35
+ /** Number of removed rows. */
36
+ getRemovedCount(): number;
37
+ /** Number of inserted rows. */
38
+ getInsertedCount(): number;
39
+ /**
40
+ * True if a row at an old display index `<= index` is removed or a row is inserted in front of the old row `index`
41
+ * (i.e. the rows `0 … index` are not the same as before).
42
+ *
43
+ * @param {number} index - an old display index
44
+ * @returns {boolean} true if the rows up to this index changed
45
+ */
46
+ touchesUpTo(index: number): boolean;
47
+ /**
48
+ * The new display index of an old one.
49
+ *
50
+ * @param {number} index - display index before the step
51
+ * @returns {number} the index after the step, -1 if the row was removed (or `index` is negative)
52
+ */
53
+ mapIndex(index: number): number;
54
+ /**
55
+ * The display index after the step of the first remaining row at or after an old index – for a position that
56
+ * must stay on a row (e.g. the first visible row) when that row itself was removed.
57
+ *
58
+ * @param {number} index - display index before the step
59
+ * @returns {number} the index after the step (the end if no old row at or after `index` remains)
60
+ */
61
+ mapIndexOrNext(index: number): number;
62
+ /**
63
+ * The new ranges of an old range of rows `lo … hi` (inclusive): removed rows leave it, inserted rows inside split
64
+ * it (they are not part of it). `hi = Number.MAX_SAFE_INTEGER` (whole columns) stays open at the end.
65
+ *
66
+ * @param {number} lo - first row of the range before the step
67
+ * @param {number} hi - last row of the range before the step (inclusive)
68
+ * @returns {[number, number][]} the pieces after the step, ascending, `[]` if all its rows were removed
69
+ */
70
+ mapRange(lo: number, hi: number): [number, number][];
71
+ }
@@ -1,5 +1,6 @@
1
1
  import { TableScope } from './table-scope.js';
2
2
  import { GeMouseEvent } from './data/common/event/ge-mouse-event.js';
3
+ import { RowIndexShift } from './live-append/row-index-shift.js';
3
4
 
4
5
  /**
5
6
  * Class representing a MouseHandler.
@@ -17,11 +18,26 @@ export declare class MouseHandler {
17
18
  * @type {number}
18
19
  */
19
20
  doubleClickDelay: number;
21
+ /**
22
+ * A render between mousedown and mouseup replaces the pressed cell, and the browser sends no "click" (both must hit
23
+ * the same element). The handler then reports the click itself for the pressed cell – if the mouse moved at most
24
+ * this many pixels (screen coordinates) between mousedown and mouseup. Since 1.1.108.
25
+ *
26
+ * @type {number}
27
+ */
28
+ clickMoveTolerance: number;
20
29
  mouseEvent?: MouseEvent;
21
30
  startMouseEvent?: GeMouseEvent;
22
31
  geMouseEvent?: GeMouseEvent;
23
32
  geMouseEventOld?: GeMouseEvent;
24
33
  private lastClickedCellKey;
34
+ /** The cell of the last left-button mousedown (see {@link PressedCell}). */
35
+ private pressedCell?;
36
+ /** Counts native "click" / "dblclick" events: a report of a lost click is dropped if the browser sent one after all. */
37
+ private nativeClickCount;
38
+ private nativeDblClickCount;
39
+ /** Timer of a pending report of a lost click (cleared by destroy via the abort signal). */
40
+ private lostClickTimer?;
25
41
  /** The second click of a double click that was already reported (clickCount 2) – its "dblclick" is not. */
26
42
  private reportedDoubleClick?;
27
43
  private mouseDown;
@@ -49,6 +65,16 @@ export declare class MouseHandler {
49
65
  * @return {void}
50
66
  */
51
67
  private onHostElementClicked;
68
+ /**
69
+ * Handles a click: on `eventTarget`, or – if `cell` is given – on that cell (a click the browser did not send
70
+ * because a render replaced the pressed element; `eventTarget` is then the detached pressed element).
71
+ */
72
+ private handleClick;
73
+ /** "area:row:column" of a cell given explicitly (same format as {@link getCellKey}). */
74
+ private toCellKey;
75
+ /** Target data of a replaced element: area, row and column come from the pressed cell (current row index). */
76
+ private applyCell;
77
+ private createCellEvent;
52
78
  /** "area:row:column" of the cell of an event target ('' outside of a cell). */
53
79
  private getCellKey;
54
80
  /** A click that triggers an action on its own: row checkbox, tree arrow, "expand/collapse all", header group toggle. */
@@ -60,6 +86,8 @@ export declare class MouseHandler {
60
86
  * @param {MouseEvent} event - The double click event.
61
87
  */
62
88
  private onHostElementDblClicked;
89
+ /** Handles a double click on `eventTarget`, or on `cell` (see {@link handleClick}). */
90
+ private handleDblClick;
63
91
  /**
64
92
  * Publishes a GeMouseEvent.
65
93
  *
@@ -77,12 +105,41 @@ export declare class MouseHandler {
77
105
  private isInteractiveCellElement;
78
106
  private getArrowColumnIndex;
79
107
  private onMouseDown;
108
+ /** A mouseup that did not reach the host (released outside the table). */
109
+ private onDocumentMouseUp;
110
+ /**
111
+ * Live data (since 1.1.108): a drag in progress (range selection) keeps its start row when rows were removed at the
112
+ * start or inserted.
113
+ *
114
+ * @param {RowIndexShift} shift - how the display indices move
115
+ */
116
+ adjustForRowChanges(shift: RowIndexShift): void;
80
117
  private onMouseMove;
81
118
  private onMouseUp;
119
+ /** The cell under a mousedown, or undefined outside of a cell. */
120
+ private readPressedCell;
121
+ /**
122
+ * After a mouseup: if a render replaced the pressed element, the browser sends no "click" (nor "dblclick"). Then the
123
+ * click is reported for the pressed cell – once this task is through and only if no native click came after all.
124
+ */
125
+ private checkLostClick;
126
+ /**
127
+ * The current index of the pressed row: rows may have been added, removed or sorted in while the button was held.
128
+ * -1 if the row is gone.
129
+ */
130
+ private resolveRowIndex;
131
+ private rowAt;
82
132
  private mouseDraggingOnFrame;
83
133
  /** Handles the end of the last drag now (in its frame, or earlier when the next mousedown comes first). */
84
134
  private flushDraggingEnd;
85
135
  private mouseMoveOnFrame;
136
+ /**
137
+ * True while the mouse button is held in the table (a click or a drag in progress) – live data does not jump to
138
+ * the end then (`tableApi.appendRows({ follow: "auto" })`, since 1.1.108).
139
+ *
140
+ * @returns {boolean} true between mousedown and mouseup
141
+ */
142
+ isButtonDown(): boolean;
86
143
  /** A mouse event on a blocking loading overlay ({@link TableOverlay.isEventBlocked}): the table ignores it. */
87
144
  private isBlocked;
88
145
  }