@guiexpert/table 1.1.96 → 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.
@@ -47,6 +47,13 @@ export declare class AriaIds {
47
47
  * @returns {string} e.g. `"ge-t1-rg-body-center"`
48
48
  */
49
49
  rowGroup(area: AreaIdent, side: SideIdent): string;
50
+ /**
51
+ * The ID of the row group of the detail rows (master/detail), referenced by `aria-owns` of the host.
52
+ *
53
+ * @returns {string} e.g. `"ge-t1-rg-detail"`
54
+ * @since 1.1.97
55
+ */
56
+ detailRowGroup(): string;
50
57
  /**
51
58
  * The ID of the live region (the element that speaks announcements, see {@link LiveAnnouncer}).
52
59
  *
@@ -36,6 +36,8 @@ export declare class AriaTexts implements AriaTextsIf {
36
36
  rowMoveNotPossible(): string;
37
37
  rowDragCancelled(): string;
38
38
  cellsFilled(cellCount: number): string;
39
+ detailExpanded(): string;
40
+ detailCollapsed(): string;
39
41
  fillTooLarge(maxCells: number): string;
40
42
  pendingChanges(pendingCount: number): string;
41
43
  changesCommitted(cellCount: number): string;
@@ -172,4 +172,18 @@ export interface AriaTextsIf {
172
172
  * @since 1.1.96
173
173
  */
174
174
  invalidValue?(message: string): string;
175
+ /**
176
+ * Announced after a detail row was expanded (master/detail).
177
+ *
178
+ * @returns {string} default: `"Details expanded"`
179
+ * @since 1.1.97
180
+ */
181
+ detailExpanded?(): string;
182
+ /**
183
+ * Announced after a detail row was collapsed (master/detail).
184
+ *
185
+ * @returns {string} default: `"Details collapsed"`
186
+ * @since 1.1.97
187
+ */
188
+ detailCollapsed?(): string;
175
189
  }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * What opened or closed a detail row: a click on the icon (`"mouse"`), Enter (`"keyboard"`), the {@link TableApi}
3
+ * (`"api"`) or a restored table state (`"state"`).
4
+ *
5
+ * @category Master/detail
6
+ * @since 1.1.97
7
+ */
8
+ export type GeDetailEventSource = "mouse" | "keyboard" | "api" | "state";
9
+ /**
10
+ * Reported by {@link EventListenerIf.onDetailExpandedChanged}: a detail row was expanded or collapsed (master/detail,
11
+ * {@link MasterDetailOptionsIf}).
12
+ *
13
+ * @example
14
+ * const listener = new EventAdapter();
15
+ * listener.onDetailExpandedChanged = (evt: GeDetailEvent) => {
16
+ * console.log(`order ${evt.row.id} ${evt.expanded ? "opened" : "closed"} (${evt.source})`);
17
+ * };
18
+ *
19
+ * @category Master/detail
20
+ * @since 1.1.97
21
+ */
22
+ export declare class GeDetailEvent<T = any> {
23
+ /** The master row. */
24
+ readonly row: T;
25
+ /** Its display index (`-1` if it is not shown, e.g. filtered out). */
26
+ readonly rowIndex: number;
27
+ /** `true` = expanded, `false` = collapsed. */
28
+ readonly expanded: boolean;
29
+ /** What changed it. */
30
+ readonly source: GeDetailEventSource;
31
+ constructor(
32
+ /** The master row. */
33
+ row: T,
34
+ /** Its display index (`-1` if it is not shown, e.g. filtered out). */
35
+ rowIndex: number,
36
+ /** `true` = expanded, `false` = collapsed. */
37
+ expanded: boolean,
38
+ /** What changed it. */
39
+ source: GeDetailEventSource);
40
+ }
@@ -44,6 +44,8 @@ export declare class AutoRestoreOptions<T> implements AutoRestoreOptionsIf<T> {
44
44
  autoRestoreHeaderGroupState: boolean;
45
45
  /** @since 1.1.92 */
46
46
  autoRestoreCustomState: boolean;
47
+ /** @since 1.1.97 */
48
+ autoRestoreDetailState: boolean;
47
49
  getStorageKeyFn: undefined;
48
50
  /** @deprecated since 1.1.92 – never used by the table. */
49
51
  isSame: (r1: T, r2: T, options: AutoRestoreOptionsIf<T>) => boolean;
@@ -51,6 +51,7 @@ export type GetStorageKeyFn = (() => string);
51
51
  * | {@link autoRestoreCheckedState} | checked rows | 1.1.92 (declared before, without effect) |
52
52
  * | {@link autoRestoreSelectedState} | selection | 1.1.92 (declared before, without effect) |
53
53
  * | {@link autoRestoreCustomState} | your data ({@link TableApi.setCustomState}) | 1.1.92 |
54
+ * | {@link autoRestoreDetailState} | expanded detail rows (master/detail) | 1.1.97 |
54
55
  *
55
56
  * Before 1.1.92 sorting, scroll position and tree were stored under three keys (`<key>sortingState`,
56
57
  * `<key>scrollState`, `<key>collapsedExpandedState`); they are read once and removed with the next save.
@@ -103,6 +104,11 @@ export interface AutoRestoreOptionsIf<T> {
103
104
  * @since 1.1.92
104
105
  */
105
106
  autoRestoreCustomState?: boolean;
107
+ /**
108
+ * Remember the expanded detail rows of master/detail ({@link TableOptionsIf.masterDetail}, by {@link getRowId}).
109
+ * @since 1.1.97
110
+ */
111
+ autoRestoreDetailState?: boolean;
106
112
  /**
107
113
  * A unique, stable id per row (tree: per node). With the default of {@link AutoRestoreOptions} the table uses the
108
114
  * property `id`, then `uuid`; rows without them are not stored (since 1.1.92 – the JSON of the row would write
@@ -1,3 +1,4 @@
1
+ import { MasterDetailOptionsIf } from '../../master-detail/master-detail-options.if.js';
1
2
  import { DefaultRowHeightsIf } from './default-row-heights.if.js';
2
3
  import { TableOptionsIf } from './table-options.if.js';
3
4
  import { TreeOptions } from './tree-options.js';
@@ -85,5 +86,6 @@ export declare class TableOptions implements TableOptionsIf {
85
86
  overlayOptions?: Partial<OverlayOptionsIf>;
86
87
  rowDragOptions?: Partial<RowDragOptionsIf>;
87
88
  fillHandle?: boolean | Partial<FillHandleOptionsIf>;
89
+ masterDetail?: Partial<MasterDetailOptionsIf>;
88
90
  batchEdit?: boolean | Partial<BatchEditOptionsIf>;
89
91
  }
@@ -1,3 +1,4 @@
1
+ import { MasterDetailOptionsIf } from '../../master-detail/master-detail-options.if.js';
1
2
  import { RowDragOptionsIf } from '../../row-drag/row-drag-options.if.js';
2
3
  import { FillHandleOptionsIf } from '../../fill/fill-handle-options.if.js';
3
4
  import { BatchEditOptionsIf } from '../../batch-edit/batch-edit-options.if.js';
@@ -361,6 +362,23 @@ export interface TableOptionsIf {
361
362
  * @since 1.1.96
362
363
  */
363
364
  batchEdit?: boolean | Partial<BatchEditOptionsIf>;
365
+ /**
366
+ * Master/detail: master rows get an expand icon; below an expanded row a detail row over the full width shows any
367
+ * content (a nested table, a form, a framework component – see {@link MasterDetailOptionsIf}). Only for flat body
368
+ * models. Fields you leave out keep their default ({@link MasterDetailOptions}); `detailRenderer` is required.
369
+ *
370
+ * @example
371
+ * const tableOptions: TableOptionsIf = {
372
+ * ...new TableOptions(),
373
+ * masterDetail: {
374
+ * detailHeight: "auto",
375
+ * detailRenderer: (container, row) => { container.textContent = `Notes: ${row.notes}`; }
376
+ * }
377
+ * };
378
+ *
379
+ * @since 1.1.97
380
+ */
381
+ masterDetail?: Partial<MasterDetailOptionsIf>;
364
382
  /**
365
383
  * The state the table starts with: column layout, sorting, tree, checked and selected rows … – e.g. loaded from
366
384
  * your server ({@link TableApi.getState}, {@link EventListenerIf.onStateChanged}). A state stored by
@@ -98,6 +98,12 @@ export declare class EleScope {
98
98
  * @return {void}
99
99
  */
100
100
  adjustContainersAndRows(): void;
101
+ /**
102
+ * Position and size of the detail layer (master/detail) in the host – overwritten in {@link RenderScope}.
103
+ *
104
+ * @since 1.1.97
105
+ */
106
+ protected adjustDetailLayerBounds(_left: number, _top: number, _width: number, _height: number): void;
101
107
  /**
102
108
  * Adjusts the position or appearance of elements after scrolling.
103
109
  * This method must be overwritten in child classes.
@@ -105,6 +111,14 @@ export declare class EleScope {
105
111
  * @return {void}
106
112
  */
107
113
  adjustAfterScrolling(): void;
114
+ /**
115
+ * The height of all body rows in px – the model's content height plus, in {@link RenderScope}, the heights of open
116
+ * detail rows (master/detail).
117
+ *
118
+ * @returns {number} px
119
+ * @since 1.1.97
120
+ */
121
+ protected getBodyContentHeight(): number;
108
122
  /**
109
123
  * Resets the size of the wrapper div based on the content dimensions.
110
124
  *
@@ -1,3 +1,4 @@
1
+ import { GeDetailEvent } from './data/common/event/ge-detail-event.js';
1
2
  import { GeModelChangeEvent } from './data/common/event/ge-model-change-event.js';
2
3
  import { GeMouseEvent } from './data/common/event/ge-mouse-event.js';
3
4
  import { GeScrollEvent } from './data/common/event/ge-scroll-event.js';
@@ -40,4 +41,6 @@ export declare class EventAdapter implements EventListenerIf {
40
41
  onRowDragEnd(_evt: GeRowDragEvent): void;
41
42
  /** @since 1.1.96 */
42
43
  onBatchEditChanged(_evt: GeBatchEditEvent): void;
44
+ /** @since 1.1.97 */
45
+ onDetailExpandedChanged(_evt: GeDetailEvent): void;
43
46
  }
@@ -1,3 +1,4 @@
1
+ import { GeDetailEvent } from './data/common/event/ge-detail-event.js';
1
2
  import { GeMouseEvent } from './data/common/event/ge-mouse-event.js';
2
3
  import { GeModelChangeEvent } from './data/common/event/ge-model-change-event.js';
3
4
  import { GeScrollEvent } from './data/common/event/ge-scroll-event.js';
@@ -95,4 +96,10 @@ export interface EventListenerIf extends EventFocusChangedListenerIf, EventSelec
95
96
  * @since 1.1.96
96
97
  */
97
98
  onBatchEditChanged?(evt: GeBatchEditEvent): void;
99
+ /**
100
+ * A detail row was expanded or collapsed (master/detail, {@link TableOptionsIf.masterDetail}).
101
+ *
102
+ * @since 1.1.97
103
+ */
104
+ onDetailExpandedChanged?(evt: GeDetailEvent): void;
98
105
  }
@@ -0,0 +1,99 @@
1
+ import { TableApi } from '../table-api.js';
2
+ import { TableModelIf } from '../data/tablemodel/table-model.if.js';
3
+ import { TableOptionsIf } from '../data/options/table-options.if.js';
4
+ import { EventListenerIf } from '../event-listener.if.js';
5
+
6
+ /**
7
+ * What {@link DetailRenderContextIf.createTable} needs for a nested table in a detail row.
8
+ *
9
+ * @category Master/detail
10
+ * @since 1.1.97
11
+ */
12
+ export interface DetailTableParamsIf {
13
+ /** The model of the nested table, e.g. `TableFactory.createTableModel({ rows: order.items, properties: [...] })`. */
14
+ tableModel: TableModelIf;
15
+ /** Its options (default: `new TableOptions()`). */
16
+ tableOptions?: TableOptionsIf;
17
+ /** Its event listener (default: none). */
18
+ eventListener?: EventListenerIf;
19
+ /**
20
+ * The height of the nested table: px or a CSS length. Default: `"100%"` with a fixed
21
+ * {@link MasterDetailOptionsIf.detailHeight}, otherwise {@link MasterDetailOptionsIf.estimatedDetailHeight} px.
22
+ */
23
+ height?: number | string;
24
+ }
25
+ /**
26
+ * The context a {@link DetailRendererIf} gets: the master row, the outer table and helpers whose resources are released
27
+ * together with the detail row.
28
+ *
29
+ * @example
30
+ * const renderDetail: DetailRenderFnType<Order> = (container, order, ctx) => {
31
+ * const button = document.createElement("button");
32
+ * button.textContent = `Open order ${order.id}`;
33
+ * button.addEventListener("click", () => ctx.tableApi.announce(`Order ${order.id}`), { signal: ctx.signal });
34
+ * container.appendChild(button);
35
+ * };
36
+ *
37
+ * @category Master/detail
38
+ * @since 1.1.97
39
+ */
40
+ export interface DetailRenderContextIf<T = any> {
41
+ /** The master row (the row object of the body model). */
42
+ readonly row: T;
43
+ /** The display index of the master row when the detail was created (it changes with sorting and filtering). */
44
+ readonly rowIndex: number;
45
+ /** The API of the outer (master) table. */
46
+ readonly tableApi: TableApi;
47
+ /**
48
+ * `true` if the content of this detail row existed before and was destroyed (too many rendered details, see
49
+ * {@link MasterDetailOptionsIf.maxRenderedDetails}, or {@link TableApi.refreshDetail}) – state that lives only in the
50
+ * DOM (inputs, scroll positions) is gone then.
51
+ */
52
+ readonly recreated: boolean;
53
+ /** Aborted when the detail row is destroyed – pass it to `addEventListener(…, { signal })`. */
54
+ readonly signal: AbortSignal;
55
+ /**
56
+ * Creates a GuiExpert table inside the detail row; it is destroyed (`TableScope.destroy()`) together with the detail
57
+ * row. The CSS variables of the page (light/dark theme) apply to it as well.
58
+ *
59
+ * @param {DetailTableParamsIf} params - model, options, listener, height
60
+ * @returns {TableApi} the API of the nested table
61
+ */
62
+ createTable(params: DetailTableParamsIf): TableApi;
63
+ }
64
+ /**
65
+ * Renders the content of a detail row – a function (see {@link DetailRendererIf}). It may return a cleanup function,
66
+ * called when the detail row is destroyed (collapsed, too many rendered details, new table model).
67
+ *
68
+ * @example
69
+ * const renderDetail: DetailRenderFnType<Order> = (container, order) => {
70
+ * container.textContent = `${order.items.length} items, ordered on ${order.date}`; // textContent: no HTML injection
71
+ * };
72
+ *
73
+ * @category Master/detail
74
+ * @since 1.1.97
75
+ */
76
+ export type DetailRenderFnType<T = any> = (container: HTMLDivElement, row: T, context: DetailRenderContextIf<T>) => (() => void) | void;
77
+ /**
78
+ * Renders the content of a detail row – the object form of {@link DetailRenderFnType}, e.g. the
79
+ * `DetailComponentRendererWrapper` of the framework wrappers (React, Vue, Svelte …).
80
+ *
81
+ * The content is created once, when the detail row becomes visible for the first time, and lives until the row is
82
+ * collapsed – scrolling only hides and shows it (inputs, selections and scroll positions stay).
83
+ *
84
+ * @example
85
+ * class OrderNotesRenderer implements DetailRendererIf<Order> {
86
+ * render(container: HTMLDivElement, order: Order): () => void {
87
+ * const textarea = document.createElement("textarea");
88
+ * textarea.value = order.notes ?? "";
89
+ * container.appendChild(textarea);
90
+ * return () => { order.notes = textarea.value; }; // cleanup: keep the text
91
+ * }
92
+ * }
93
+ *
94
+ * @category Master/detail
95
+ * @since 1.1.97
96
+ */
97
+ export interface DetailRendererIf<T = any> {
98
+ render(container: HTMLDivElement, row: T, context: DetailRenderContextIf<T>): (() => void) | void;
99
+ }
@@ -0,0 +1,30 @@
1
+ import { DetailHeightType, MasterDetailOptionsIf } from './master-detail-options.if.js';
2
+ import { DetailRendererIf, DetailRenderFnType } from './detail-renderer.if.js';
3
+
4
+ /**
5
+ * The defaults of {@link MasterDetailOptionsIf}.
6
+ *
7
+ * @example
8
+ * const options = MasterDetailOptions.create({ detailRenderer: (el, row) => { el.textContent = String(row.id); } });
9
+ * options.detailHeight; // "auto"
10
+ * options.maxRenderedDetails; // 50
11
+ *
12
+ * @category Master/detail
13
+ * @since 1.1.97
14
+ */
15
+ export declare class MasterDetailOptions<T = any> implements MasterDetailOptionsIf<T> {
16
+ detailRenderer: DetailRendererIf<T> | DetailRenderFnType<T>;
17
+ isMaster?: (row: T, rowIndex: number) => boolean;
18
+ detailHeight: DetailHeightType | ((row: T, rowIndex: number) => DetailHeightType);
19
+ estimatedDetailHeight: number;
20
+ toggleColumn?: number | string;
21
+ maxRenderedDetails: number;
22
+ getRowId?: (row: T) => string | number;
23
+ /**
24
+ * The defaults, overridden by the given values (`undefined` keeps the default).
25
+ *
26
+ * @param {Partial<MasterDetailOptionsIf>} [options] - e.g. `tableOptions.masterDetail`
27
+ * @returns {MasterDetailOptionsIf} complete options
28
+ */
29
+ static create<T = any>(options?: Partial<MasterDetailOptionsIf<T>>): MasterDetailOptionsIf<T>;
30
+ }
@@ -0,0 +1,60 @@
1
+ import { DetailRendererIf, DetailRenderFnType } from './detail-renderer.if.js';
2
+
3
+ /**
4
+ * The height of a detail row: px, or `"auto"` = measured from its content (a `ResizeObserver` follows changes).
5
+ *
6
+ * @category Master/detail
7
+ * @since 1.1.97
8
+ */
9
+ export type DetailHeightType = number | "auto";
10
+ /**
11
+ * Master/detail: a master row can be expanded; below it a detail row over the full width of the table (fixed columns
12
+ * included) shows any content – a nested GuiExpert table, a form, a chart, a framework component. Works with virtual
13
+ * scrolling and millions of rows: the content is created when the detail row becomes visible and lives until it is
14
+ * collapsed (scrolling only hides and shows it). Only for flat body models (not for trees, row groups and pivot tables).
15
+ *
16
+ * Fields you leave out keep their default ({@link MasterDetailOptions}).
17
+ *
18
+ * @example
19
+ * const tableOptions: TableOptionsIf = {
20
+ * ...new TableOptions(),
21
+ * masterDetail: {
22
+ * detailHeight: 180,
23
+ * detailRenderer: (_container, order: Order, ctx) => {
24
+ * ctx.createTable({
25
+ * tableModel: TableFactory.createTableModel({ rows: order.items, properties: ["sku", "name", "qty", "price"] })
26
+ * });
27
+ * }
28
+ * }
29
+ * };
30
+ *
31
+ * @see Training: [Master/detail](https://gui.expert/training/master-detail/)
32
+ *
33
+ * @category Master/detail
34
+ * @since 1.1.97
35
+ */
36
+ export interface MasterDetailOptionsIf<T = any> {
37
+ /** Renders the content of a detail row (function or object with `render()`). */
38
+ detailRenderer: DetailRendererIf<T> | DetailRenderFnType<T>;
39
+ /** Which rows can be expanded. Default: every row. */
40
+ isMaster?: (row: T, rowIndex: number) => boolean;
41
+ /** Height of the detail rows: px, `"auto"` (measured) or a function per row. Default `"auto"`. */
42
+ detailHeight: DetailHeightType | ((row: T, rowIndex: number) => DetailHeightType);
43
+ /** Height of an `"auto"` detail row before its content was measured (px). Default 200. */
44
+ estimatedDetailHeight: number;
45
+ /**
46
+ * The column with the expand/collapse icon: column index or property. Default: the first data column (behind a row
47
+ * number and a row checkbox column – where a tree has its arrow).
48
+ */
49
+ toggleColumn?: number | string;
50
+ /**
51
+ * At most this many detail contents exist at the same time. Beyond it the detail row that was out of view the
52
+ * longest is destroyed and created again when it scrolls back into view (`context.recreated`). Default 50.
53
+ */
54
+ maxRenderedDetails: number;
55
+ /**
56
+ * The id of a master row – keeps detail rows open when the rows are replaced by new objects with the same ids
57
+ * (`setRows`). Default: `autoRestoreOptions.getRowId`, otherwise the row object itself.
58
+ */
59
+ getRowId?: (row: T) => string | number;
60
+ }
@@ -0,0 +1,223 @@
1
+ import { TableScope } from '../table-scope.js';
2
+ import { TableApi } from '../table-api.js';
3
+ import { TableModelIf } from '../data/tablemodel/table-model.if.js';
4
+ import { TableOptionsIf } from '../data/options/table-options.if.js';
5
+ import { EventListenerIf } from '../event-listener.if.js';
6
+ import { GeDetailEventSource } from '../data/common/event/ge-detail-event.js';
7
+ import { MasterDetailOptionsIf } from './master-detail-options.if.js';
8
+
9
+ /**
10
+ * The part of a nested table the master/detail service needs (a {@link TableScope}).
11
+ *
12
+ * @category Internals
13
+ * @since 1.1.97
14
+ */
15
+ export interface NestedTableScopeIf {
16
+ firstInit(): unknown;
17
+ getApi(): TableApi;
18
+ destroy(): void;
19
+ }
20
+ /**
21
+ * Creates the {@link TableScope} of a nested table (given by `TableScope` – no import cycle).
22
+ *
23
+ * @category Internals
24
+ * @since 1.1.97
25
+ */
26
+ export type NestedTableScopeFactory = (host: HTMLDivElement, tableModel: TableModelIf, tableOptions: TableOptionsIf | undefined, eventListener: EventListenerIf | undefined) => NestedTableScopeIf;
27
+ /**
28
+ * Master/detail of a table ({@link TableOptionsIf.masterDetail}): the expanded master rows, the detail layer and the
29
+ * life cycle of the detail contents. Created by {@link TableScope} (`tableScope.masterDetailService`); use the
30
+ * {@link TableApi} (`setDetailExpanded`, `toggleDetail` …).
31
+ *
32
+ * - **Layer**: one div `ge-table-detail-layer` in the host, over the body (full width, fixed columns included, without
33
+ * the scroll bars). It is never cleared by the virtual rendering and does not scroll horizontally.
34
+ * - **Positions**: the heights of the detail rows are extras of the body {@link RowLayout} (behind their master row);
35
+ * `RenderScope` reports the detail rows in view after every render pass ({@link layoutVisible}).
36
+ * - **Life cycle**: the content is created when the detail row becomes visible, hidden (not destroyed) when it scrolls
37
+ * out of view, destroyed when it is collapsed, beyond `maxRenderedDetails` or when the table is destroyed.
38
+ * - **Events**: mouse, keyboard and pointer events from a detail row are ignored by the outer table
39
+ * ({@link isInLayer}); the mouse wheel over a detail row scrolls the outer table when nothing in the detail scrolls.
40
+ *
41
+ * @category Internals
42
+ * @since 1.1.97
43
+ */
44
+ export declare class MasterDetailService {
45
+ private readonly scope;
46
+ private readonly createNestedScope;
47
+ /** The complete options (defaults + `tableOptions.masterDetail`). */
48
+ readonly options: MasterDetailOptionsIf;
49
+ private readonly active;
50
+ private layer?;
51
+ private readonly entries;
52
+ private byIndex;
53
+ private visibleNow;
54
+ private renderPass;
55
+ private renderedCount;
56
+ private toggleColumnIndex;
57
+ private resizeFrame;
58
+ private destroyed;
59
+ constructor(scope: TableScope, createNestedScope: NestedTableScopeFactory);
60
+ /** True if master/detail is configured (and the body is flat). */
61
+ isActive(): boolean;
62
+ /** True if at least one master row is expanded. */
63
+ hasExpanded(): boolean;
64
+ /** The detail layer (`undefined` without master/detail). */
65
+ getLayer(): HTMLDivElement | undefined;
66
+ /**
67
+ * True if an event comes from a detail row (or a nested table in it) – the outer table ignores it.
68
+ *
69
+ * @param {Event} evt - a DOM event while it is dispatched
70
+ * @returns {boolean} whether its path contains the detail layer
71
+ */
72
+ isInLayer(evt: Event): boolean;
73
+ /** The id of a master row: `options.getRowId`, `autoRestoreOptions.getRowId`, the row object or its index. */
74
+ keyOf(row: unknown, rowIndex: number): unknown;
75
+ /**
76
+ * True if the row at a display index can be expanded ({@link MasterDetailOptionsIf.isMaster}).
77
+ *
78
+ * @param {number} rowIndex - display index
79
+ * @returns {boolean} whether it is a master row
80
+ */
81
+ isMasterAt(rowIndex: number): boolean;
82
+ /**
83
+ * True if the master row at a display index is expanded.
84
+ *
85
+ * @param {number} rowIndex - display index
86
+ * @returns {boolean} whether its detail row is open
87
+ */
88
+ isExpandedAt(rowIndex: number): boolean;
89
+ /**
90
+ * True if a row (display index or row object) is expanded.
91
+ *
92
+ * @param {number | object} rowOrIndex - display index or row object
93
+ * @returns {boolean} whether its detail row is open
94
+ */
95
+ isExpanded(rowOrIndex: unknown): boolean;
96
+ /**
97
+ * Expands or collapses a master row. The table repaints and reports the change ({@link TableScope}).
98
+ *
99
+ * @param {number | object} rowOrIndex - display index or row object
100
+ * @param {boolean} expanded - the new state
101
+ * @param {GeDetailEventSource} [source] - what changed it (for the event)
102
+ * @returns {boolean} false if nothing changed (no master row, already in that state, no master/detail)
103
+ */
104
+ setExpanded(rowOrIndex: unknown, expanded: boolean, source?: GeDetailEventSource): boolean;
105
+ /**
106
+ * Expands all master rows that are shown (their contents are created only when they scroll into view).
107
+ * No event per row.
108
+ *
109
+ * @returns {number} the number of rows that were expanded
110
+ */
111
+ expandAll(): number;
112
+ /**
113
+ * Collapses all detail rows (their contents are destroyed). No event per row.
114
+ *
115
+ * @returns {number} the number of rows that were collapsed
116
+ */
117
+ collapseAll(): number;
118
+ /** The expanded master rows (row objects; for models without row objects: `undefined`). */
119
+ getExpandedRows(): unknown[];
120
+ /**
121
+ * Expands exactly these rows and collapses all others – the restore of the table state. Rows that are no master
122
+ * rows are skipped. No event per row.
123
+ *
124
+ * @param {unknown[]} rows - row objects of the body model
125
+ * @param {GeDetailEventSource} [source] - what changed it
126
+ * @returns {boolean} true if something changed
127
+ */
128
+ setExpandedRows(rows: readonly unknown[], source?: GeDetailEventSource): boolean;
129
+ /**
130
+ * Destroys the content of a detail row and creates it again (e.g. after the row's data changed).
131
+ *
132
+ * @param {number | object} rowOrIndex - display index or row object
133
+ * @returns {boolean} false if the row is not expanded
134
+ */
135
+ refresh(rowOrIndex: unknown): boolean;
136
+ /**
137
+ * The container of a detail row (the element `detailRenderer` renders into) – only while its content exists.
138
+ *
139
+ * @param {number | object} rowOrIndex - display index or row object
140
+ * @returns {HTMLDivElement | undefined} the container
141
+ */
142
+ getDetailElement(rowOrIndex: unknown): HTMLDivElement | undefined;
143
+ /**
144
+ * The display index of the master row a DOM node belongs to (a detail row or something in it).
145
+ *
146
+ * @param {Node} node - a node in the detail layer
147
+ * @returns {number} the display index, -1 if none
148
+ */
149
+ getMasterRowIndexOf(node: Node | null): number;
150
+ /**
151
+ * The display indices and heights of the expanded master rows that are shown – the extras of the body row layout.
152
+ * Called by `RenderScope` when the row layout is built (after filtering, sorting, editing, a detail toggle …): one
153
+ * pass over the shown rows. Removes expanded rows that no longer exist in the model (`getAllRows()`).
154
+ *
155
+ * @returns {{ rowIndices: number[], heights: number[] } | undefined} the extras, `undefined` without expanded rows
156
+ */
157
+ computeExtras(): {
158
+ rowIndices: number[];
159
+ heights: number[];
160
+ } | undefined;
161
+ /**
162
+ * Called at the start of the body render pass: the column of the expand icon.
163
+ */
164
+ startRenderPass(): void;
165
+ /**
166
+ * Shows the detail rows in view (called by `RenderScope` after the body rows were rendered): creates missing
167
+ * contents, sets their positions, hides the ones that left the view and destroys the oldest hidden contents beyond
168
+ * {@link MasterDetailOptionsIf.maxRenderedDetails}.
169
+ *
170
+ * @param {number[]} triples - `rowIndex, top, height` of each detail row in view; `top` relative to the body top
171
+ */
172
+ layoutVisible(triples: number[]): void;
173
+ /**
174
+ * Position and size of the layer: the visible body including the fixed columns, without the scroll bars.
175
+ *
176
+ * @param {number} left - px in the host
177
+ * @param {number} top - px in the host (below the header)
178
+ * @param {number} width - px
179
+ * @param {number} height - px
180
+ */
181
+ setBounds(left: number, top: number, width: number, height: number): void;
182
+ /**
183
+ * Decorates a body cell of the toggle column of a master row with the expand icon (`ge-table-detail-toggle`).
184
+ *
185
+ * @param {HTMLDivElement} cell - the rendered cell
186
+ * @param {number} rowIndex - display index
187
+ * @param {number} columnIndex - model column index
188
+ */
189
+ decorateCell(cell: HTMLDivElement, rowIndex: number, columnIndex: number): void;
190
+ /**
191
+ * Called when the table is destroyed: destroys all detail contents and removes the layer.
192
+ */
193
+ destroy(): void;
194
+ private getBody;
195
+ private getAllRows;
196
+ /** isMaster for a row that is not shown (filtered out) – the index is unknown (-1). */
197
+ private isMasterRow;
198
+ /** `[row, rowIndex]` of a display index or a row object (index -1 if the row is not shown). */
199
+ private resolve;
200
+ private heightOf;
201
+ private detailHeightOf;
202
+ private computeToggleColumnIndex;
203
+ private pruneRemovedRows;
204
+ private createLayer;
205
+ private createContent;
206
+ private createNestedTable;
207
+ private place;
208
+ private park;
209
+ /** The focus is inside a detail row that is hidden or removed: the master table (row) takes it. */
210
+ private releaseFocus;
211
+ private onResize;
212
+ private enforceLimit;
213
+ private destroyContent;
214
+ private clearAll;
215
+ /** The mouse wheel over a detail row: scrolls the outer table if nothing in the detail row takes the movement. */
216
+ private onWheel;
217
+ private canScrollInside;
218
+ /** Tab on a master row with an open detail row: the focus moves to the first focusable element in it. */
219
+ private onHostKeyDown;
220
+ /** The host got the focus back from a detail row (Shift+Tab): its master row gets the focus. */
221
+ private onHostFocus;
222
+ private findFocusable;
223
+ }