@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
@@ -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
  }
@@ -17,6 +17,8 @@
17
17
  export declare class CrudObjectEdit<T extends Record<string, any>> {
18
18
  private idKey;
19
19
  dialogTitle: string;
20
+ /** Writing direction of the dialog (`dir`) – the CRUD factory sets the direction of the table. @since 1.1.98 */
21
+ direction?: "ltr" | "rtl";
20
22
  private object;
21
23
  /** Property path of every input (index = `data-field-index`) – a key may contain a dot itself. */
22
24
  private fieldPaths;
@@ -5,6 +5,8 @@
5
5
  * @category CRUD
6
6
  */
7
7
  export declare class CrudObjectView<T extends Record<string, any>> {
8
+ /** Writing direction of the dialog (`dir`) – the CRUD factory sets the direction of the table. @since 1.1.98 */
9
+ direction?: "ltr" | "rtl";
8
10
  private object;
9
11
  constructor(obj: T);
10
12
  openDialog(): void;
@@ -59,6 +59,8 @@ export declare class CrudTableModelFactory implements ActionEventListenerIf {
59
59
  * While a request runs, the table shows its loading overlay ({@link TableApi.showLoadingOverlay}, since 1.1.92).
60
60
  * A failed request is logged with `console.error`; the table stays unchanged.
61
61
  */
62
+ /** The dialogs live outside the host: they get the writing direction of the table (since 1.1.98). */
63
+ private withDirection;
62
64
  onActionEvent(actionEvent: ActionEventIf): void;
63
65
  /** `createItem` without the id property (the server assigns it), then shows the new row. */
64
66
  private createItem;
@@ -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,5 @@
1
+ import { TableDirection } from '../../rtl/table-direction.type.js';
2
+ import { MasterDetailOptionsIf } from '../../master-detail/master-detail-options.if.js';
1
3
  import { DefaultRowHeightsIf } from './default-row-heights.if.js';
2
4
  import { TableOptionsIf } from './table-options.if.js';
3
5
  import { TreeOptions } from './tree-options.js';
@@ -85,5 +87,11 @@ export declare class TableOptions implements TableOptionsIf {
85
87
  overlayOptions?: Partial<OverlayOptionsIf>;
86
88
  rowDragOptions?: Partial<RowDragOptionsIf>;
87
89
  fillHandle?: boolean | Partial<FillHandleOptionsIf>;
90
+ masterDetail?: Partial<MasterDetailOptionsIf>;
88
91
  batchEdit?: boolean | Partial<BatchEditOptionsIf>;
92
+ /**
93
+ * Writing direction (`"ltr"` | `"rtl"` | `"auto"`). Not set = `"ltr"`; a nested table (master/detail) without its
94
+ * own value takes the direction of the outer table. @since 1.1.98
95
+ */
96
+ direction?: TableDirection;
89
97
  }
@@ -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';
@@ -16,6 +17,7 @@ import { HeaderGroupOptionsIf } from './header-group-options.if.js';
16
17
  import { AriaTextsIf } from '../../aria/aria-texts.if.js';
17
18
  import { TableStateIf } from '../../state/table-state.if.js';
18
19
  import { OverlayOptionsIf } from './overlay-options.if.js';
20
+ import { TableDirection } from '../../rtl/table-direction.type.js';
19
21
 
20
22
  /**
21
23
  * Represents the options for configuring a table.
@@ -361,6 +363,39 @@ export interface TableOptionsIf {
361
363
  * @since 1.1.96
362
364
  */
363
365
  batchEdit?: boolean | Partial<BatchEditOptionsIf>;
366
+ /**
367
+ * Master/detail: master rows get an expand icon; below an expanded row a detail row over the full width shows any
368
+ * content (a nested table, a form, a framework component – see {@link MasterDetailOptionsIf}). Only for flat body
369
+ * models. Fields you leave out keep their default ({@link MasterDetailOptions}); `detailRenderer` is required.
370
+ *
371
+ * @example
372
+ * const tableOptions: TableOptionsIf = {
373
+ * ...new TableOptions(),
374
+ * masterDetail: {
375
+ * detailHeight: "auto",
376
+ * detailRenderer: (container, row) => { container.textContent = `Notes: ${row.notes}`; }
377
+ * }
378
+ * };
379
+ *
380
+ * @since 1.1.97
381
+ */
382
+ masterDetail?: Partial<MasterDetailOptionsIf>;
383
+ /**
384
+ * Writing direction: `"ltr"` (default), `"rtl"` for Arabic, Hebrew, Persian, Urdu … (first column on the right,
385
+ * horizontal scrolling, fixed columns, tree indent, resize handles and arrow keys mirrored) or `"auto"` (the
386
+ * direction of the page: `dir="rtl"` on `<html>` or a parent element). Read once when the table is built – a new
387
+ * options object rebuilds the table in the wrappers.
388
+ *
389
+ * In right-to-left, the "left" and "right" in names of the API mean **start** and **end**:
390
+ * `fixedLeftColumnCount` fixes columns at the start (right), `fixedRightColumnCount` at the end (left),
391
+ * `ge-table-text-align-left` aligns to the start. The table sets `dir` on its host element (also `dir="ltr"`).
392
+ *
393
+ * @example
394
+ * const tableOptions: TableOptionsIf = { ...new TableOptions(), direction: "rtl" };
395
+ *
396
+ * @since 1.1.98
397
+ */
398
+ direction?: TableDirection;
364
399
  /**
365
400
  * The state the table starts with: column layout, sorting, tree, checked and selected rows … – e.g. loaded from
366
401
  * your server ({@link TableApi.getState}, {@link EventListenerIf.onStateChanged}). A state stored by
@@ -7,6 +7,7 @@ import { AriaIds } from './aria/aria-ids.js';
7
7
  import { AreaIdent } from './data/tablemodel/area-ident.type.js';
8
8
  import { SideIdent } from './data/side-ident.type.js';
9
9
  import { TableOverlay } from './overlay/table-overlay.js';
10
+ import { ResolvedTableDirection } from './rtl/table-direction.type.js';
10
11
 
11
12
  /**
12
13
  * Represents a store for HTML elements.
@@ -53,7 +54,43 @@ export declare class EleScope {
53
54
  readonly ariaIds: AriaIds;
54
55
  /** The overlay layer over the body ("Loading…", "No rows to show"). @since 1.1.92 */
55
56
  readonly tableOverlay: TableOverlay;
57
+ /**
58
+ * The resolved writing direction of the table ({@link TableOptionsIf.direction}; `"auto"` is decided when the table
59
+ * is built). @since 1.1.98
60
+ */
61
+ readonly direction: ResolvedTableDirection;
56
62
  constructor(hostElement: HTMLDivElement, tableModel: TableModelIf, dom: ConvenienceDomService, tableOptions: TableOptionsIf);
63
+ /**
64
+ * Whether the table runs from right to left ({@link TableOptionsIf.direction}).
65
+ *
66
+ * @returns {boolean} true for `"rtl"` (also a resolved `"auto"`)
67
+ * @since 1.1.98
68
+ */
69
+ isRtl(): boolean;
70
+ /**
71
+ * The horizontal scroll position of the body as distance from the **start** (≥ 0) – `scrollLeft`, in
72
+ * right-to-left `-scrollLeft`.
73
+ *
74
+ * @returns {number} px
75
+ * @since 1.1.98
76
+ */
77
+ getViewportScrollLeft(): number;
78
+ /**
79
+ * Sets the horizontal scroll position of the body as distance from the **start** (see {@link getViewportScrollLeft}).
80
+ *
81
+ * @param {number} x - px from the start
82
+ * @since 1.1.98
83
+ */
84
+ setViewportScrollLeft(x: number): void;
85
+ /**
86
+ * A client x coordinate (e.g. `PointerEvent.clientX`) as x in the host from the start of its padding box – the
87
+ * coordinates in which the table positions everything (right-to-left: from the right edge).
88
+ *
89
+ * @param {number} clientX - x in client (viewport) coordinates
90
+ * @returns {number} px from the start
91
+ * @since 1.1.98
92
+ */
93
+ clientXToHostX(clientX: number): number;
57
94
  /**
58
95
  * Shows the overlay that fits the current state (requested by the API, or "no rows" for an empty body).
59
96
  * Overwritten in {@link RenderScope}.
@@ -98,6 +135,12 @@ export declare class EleScope {
98
135
  * @return {void}
99
136
  */
100
137
  adjustContainersAndRows(): void;
138
+ /**
139
+ * Position and size of the detail layer (master/detail) in the host – overwritten in {@link RenderScope}.
140
+ *
141
+ * @since 1.1.97
142
+ */
143
+ protected adjustDetailLayerBounds(_left: number, _top: number, _width: number, _height: number): void;
101
144
  /**
102
145
  * Adjusts the position or appearance of elements after scrolling.
103
146
  * This method must be overwritten in child classes.
@@ -105,6 +148,14 @@ export declare class EleScope {
105
148
  * @return {void}
106
149
  */
107
150
  adjustAfterScrolling(): void;
151
+ /**
152
+ * The height of all body rows in px – the model's content height plus, in {@link RenderScope}, the heights of open
153
+ * detail rows (master/detail).
154
+ *
155
+ * @returns {number} px
156
+ * @since 1.1.97
157
+ */
158
+ protected getBodyContentHeight(): number;
108
159
  /**
109
160
  * Resets the size of the wrapper div based on the content dimensions.
110
161
  *
@@ -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
+ }