@guiexpert/table 1.1.96 → 1.1.98

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +7 -0
  2. package/css/main.css +192 -27
  3. package/css/table-classes.css +33 -9
  4. package/css/table-color-vars.css +12 -0
  5. package/css/table-fill-batch.css +9 -2
  6. package/css/table-master-detail.css +115 -0
  7. package/css/table-tree.css +23 -16
  8. package/index.cjs +50 -50
  9. package/index.d.ts +12 -0
  10. package/index.js +3996 -2631
  11. package/lib/table/aria/aria-ids.d.ts +7 -0
  12. package/lib/table/aria/aria-texts.d.ts +2 -0
  13. package/lib/table/aria/aria-texts.if.d.ts +14 -0
  14. package/lib/table/crud/crud-object-edit.d.ts +2 -0
  15. package/lib/table/crud/crud-object-view.d.ts +2 -0
  16. package/lib/table/crud/crud-table-model-factory.d.ts +2 -0
  17. package/lib/table/data/common/event/ge-detail-event.d.ts +40 -0
  18. package/lib/table/data/options/auto-restore-options.d.ts +2 -0
  19. package/lib/table/data/options/auto-restore-options.if.d.ts +6 -0
  20. package/lib/table/data/options/table-options.d.ts +8 -0
  21. package/lib/table/data/options/table-options.if.d.ts +35 -0
  22. package/lib/table/ele-scope.d.ts +51 -0
  23. package/lib/table/event-adapter.d.ts +3 -0
  24. package/lib/table/event-listener.if.d.ts +7 -0
  25. package/lib/table/master-detail/detail-renderer.if.d.ts +99 -0
  26. package/lib/table/master-detail/master-detail-options.d.ts +30 -0
  27. package/lib/table/master-detail/master-detail-options.if.d.ts +60 -0
  28. package/lib/table/master-detail/master-detail-service.d.ts +223 -0
  29. package/lib/table/render-scope.d.ts +73 -0
  30. package/lib/table/row-layout/row-layout.d.ts +120 -0
  31. package/lib/table/rtl/rtl-util.d.ts +126 -0
  32. package/lib/table/rtl/table-direction.type.d.ts +23 -0
  33. package/lib/table/service/convenience-dom.service.d.ts +49 -1
  34. package/lib/table/service/excel-export-options.if.d.ts +17 -0
  35. package/lib/table/service/excel-service.d.ts +8 -2
  36. package/lib/table/service/excel-service.if.d.ts +3 -1
  37. package/lib/table/state/table-state.if.d.ts +4 -1
  38. package/lib/table/state/table-state.service.d.ts +4 -0
  39. package/lib/table/table-api.d.ts +123 -2
  40. package/lib/table/table-scope.d.ts +69 -0
  41. package/package.json +1 -1
@@ -0,0 +1,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
+ }
@@ -26,6 +26,8 @@ import { AreaObjectMapType } from './data/common/area-map.type.js';
26
26
  import { TableCellUpdateEventIf } from './data/common/event/input/table-cell-update-event.if.js';
27
27
  import { AriaGridInfo } from './aria/aria-grid-info.js';
28
28
  import { AriaTextsIf } from './aria/aria-texts.if.js';
29
+ import { RowLayout } from './row-layout/row-layout.js';
30
+ import { MasterDetailService } from './master-detail/master-detail-service.js';
29
31
 
30
32
  /** @category Internals */
31
33
  export declare class RenderScope extends EleScope {
@@ -103,6 +105,16 @@ export declare class RenderScope extends EleScope {
103
105
  * @since 1.1.96
104
106
  */
105
107
  fillHandleService?: FillHandleService;
108
+ /**
109
+ * Master/detail of this table (detail rows below master rows) – created by {@link TableScope} with
110
+ * `tableOptions.masterDetail`.
111
+ * @since 1.1.97
112
+ */
113
+ masterDetailService?: MasterDetailService;
114
+ /** Positions of the body rows (prefix sums of the heights + detail rows), see {@link getBodyRowLayout}. */
115
+ private readonly bodyRowLayout;
116
+ private bodyRowLayoutDirty;
117
+ private bodyRowLayoutModel?;
106
118
  constructor(hostElement: HTMLDivElement, tableModel: TableModelIf, dom: ConvenienceDomService, tableOptions: TableOptionsIf);
107
119
  /**
108
120
  * The DOM service of this table (all DOM access of the table goes through it).
@@ -155,6 +167,52 @@ export declare class RenderScope extends EleScope {
155
167
  * @since 1.1.96
156
168
  */
157
169
  getBodyCellAtHostPoint(x: number, y: number): [number, number];
170
+ /**
171
+ * The positions of the body rows: prefix sums of the row heights (O(log n) lookups also for variable heights) plus
172
+ * the heights of open detail rows. Built lazily – again after {@link invalidateRowLayout}, a new body model or
173
+ * another row count.
174
+ *
175
+ * @returns {RowLayout} the layout of the body rows
176
+ * @since 1.1.97
177
+ */
178
+ getBodyRowLayout(): RowLayout;
179
+ /**
180
+ * Marks the positions of the body rows as outdated (rows filtered, sorted, edited, added or removed, a detail row
181
+ * opened or closed) – they are computed again on the next access.
182
+ *
183
+ * @since 1.1.97
184
+ */
185
+ invalidateRowLayout(): void;
186
+ /**
187
+ * Sets the height of the detail row behind one body row in the current row layout without building it again
188
+ * (a detail row was expanded or collapsed). Does nothing if the layout is outdated anyway.
189
+ *
190
+ * @param {number} rowIndex - display index of the master row
191
+ * @param {number} height - the detail height, 0 = none
192
+ * @returns {boolean} true if the layout was updated, false if it is outdated (it is built again on the next access)
193
+ * @since 1.1.97
194
+ */
195
+ setBodyRowExtra(rowIndex: number, height: number): boolean;
196
+ /** True if open detail rows take space between the body rows. */
197
+ protected hasBodyRowExtras(): boolean;
198
+ /**
199
+ * The top of a body row in content pixels: {@link AreaModelIf.getYPosByRowIndex} plus the heights of the open detail
200
+ * rows above it (master/detail).
201
+ *
202
+ * @param {number} rowIndex - display index
203
+ * @returns {number} px from the top of the first body row
204
+ * @since 1.1.97
205
+ */
206
+ getBodyRowTop(rowIndex: number): number;
207
+ /**
208
+ * The body row at a content position (a position in an open detail row belongs to its master row). Above the rows:
209
+ * 0, below: the last row, without rows: -1.
210
+ *
211
+ * @param {number} contentY - px from the top of the first body row
212
+ * @returns {number} display index
213
+ * @since 1.1.97
214
+ */
215
+ getBodyRowIndexAtY(contentY: number): number;
158
216
  /**
159
217
  * Scroll bar pixels per content pixel: 1, below 1 for a capped scroll height ({@link isScrollHeightCapped}).
160
218
  *
@@ -338,6 +396,15 @@ export declare class RenderScope extends EleScope {
338
396
  * scaled and scrollTop is no longer a content pixel position.
339
397
  */
340
398
  isScrollHeightCapped(): boolean;
399
+ /**
400
+ * The model's content height plus the heights of open detail rows (master/detail).
401
+ *
402
+ * @returns {number} px
403
+ * @since 1.1.97
404
+ */
405
+ /** The detail layer (master/detail) covers the visible body. @since 1.1.97 */
406
+ protected adjustDetailLayerBounds(left: number, top: number, width: number, height: number): void;
407
+ protected getBodyContentHeight(): number;
341
408
  /** Scrollable range of the content in content pixels (content height minus visible body height). */
342
409
  protected getContentScrollRangeY(): number;
343
410
  /** Scrollable range of the scroll viewport in scroll bar pixels. */
@@ -550,6 +617,12 @@ export declare class RenderScope extends EleScope {
550
617
  *
551
618
  * @returns {void}
552
619
  */
620
+ /**
621
+ * Hides the hover row and column – e.g. while the pointer is over a detail row (master/detail).
622
+ *
623
+ * @since 1.1.97
624
+ */
625
+ hideHover(): void;
553
626
  protected hideHoverRow(): void;
554
627
  /**
555
628
  * Adjusts the position and size of the hover column based on the mouse move event.
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Vertical positions of the body rows: the row heights as prefix sums plus optional extra heights behind single rows
3
+ * (the detail rows of master/detail). DOM-free; the table keeps one instance for its body
4
+ * ({@link RenderScope.getBodyRowTop}, {@link RenderScope.getBodyRowIndexAtY}).
5
+ *
6
+ * - Constant row height: positions are computed (`i * h`), no array.
7
+ * - Variable row heights: a `Float64Array` of `rowCount + 1` prefix sums, built lazily on the first access after
8
+ * {@link setRows} – then {@link getTop} is O(1) and {@link findIndexAt} O(log n) (before 1.1.97 the table summed the
9
+ * heights of all rows above the viewport in every scroll frame).
10
+ * - Extras: sorted row indices with their extra heights and the prefix sums of the extras (O(log k) per lookup,
11
+ * k = number of extras). Changing one extra height ({@link setExtraHeight}) costs O(k), not O(n).
12
+ *
13
+ * The slot of a row is the row plus its extra height below it: `getTop(i + 1) = getTop(i) + getRowHeight(i) +
14
+ * getExtraHeight(i)`.
15
+ *
16
+ * @example
17
+ * const layout = new RowLayout();
18
+ * layout.setRows(1000, i => i % 2 ? 30 : 50); // variable heights
19
+ * layout.setExtras([12, 40], [180, 240]); // details behind rows 12 and 40
20
+ * const top = layout.getTop(41); // top of row 41 below both details
21
+ * const row = layout.findIndexAt(5000); // the row whose slot contains y = 5000
22
+ *
23
+ * @category Internals
24
+ * @since 1.1.97
25
+ */
26
+ export declare class RowLayout {
27
+ private rowCount;
28
+ private constantHeight?;
29
+ private heightOf;
30
+ private prefix?;
31
+ private extraIndices;
32
+ private extraHeights;
33
+ private extraPrefix;
34
+ /**
35
+ * Sets the rows. Prefix sums of variable heights are built lazily on the first access.
36
+ * Extras behind rows that no longer exist are dropped.
37
+ *
38
+ * @param {number} rowCount - number of rows
39
+ * @param {(rowIndex: number) => number} heightOf - height of a row (called only for variable heights)
40
+ * @param {number} [constantHeight] - the height of every row, if they are all equal (`AreaModelIf.getConstantRowHeight`)
41
+ */
42
+ setRows(rowCount: number, heightOf: (rowIndex: number) => number, constantHeight?: number): void;
43
+ /**
44
+ * Sets the extra heights (e.g. detail rows) behind rows. Unsorted input is fine; heights of the same row add up;
45
+ * indices outside the rows and heights ≤ 0 are ignored. Replaces all previous extras.
46
+ *
47
+ * @param {ArrayLike<number>} rowIndices - the rows with an extra height below them
48
+ * @param {ArrayLike<number>} heights - the extra heights (same order as `rowIndices`)
49
+ */
50
+ setExtras(rowIndices: ArrayLike<number>, heights: ArrayLike<number>): void;
51
+ /**
52
+ * Changes the extra height behind one row that already has one (see {@link setExtras}).
53
+ *
54
+ * @param {number} rowIndex - the row
55
+ * @param {number} height - the new extra height (> 0)
56
+ * @returns {boolean} false if the row has no extra or the height did not change
57
+ */
58
+ setExtraHeight(rowIndex: number, height: number): boolean;
59
+ /**
60
+ * Sets, changes or (height ≤ 0) removes the extra height behind one row – O(k), the other extras stay.
61
+ *
62
+ * @param {number} rowIndex - the row (ignored outside the rows)
63
+ * @param {number} height - the extra height, ≤ 0 removes it
64
+ * @returns {boolean} true if something changed
65
+ */
66
+ setExtra(rowIndex: number, height: number): boolean;
67
+ /** The number of rows. */
68
+ getRowCount(): number;
69
+ /** The number of rows with an extra height. */
70
+ getExtraCount(): number;
71
+ /** True after the prefix sums of variable heights were built (never for constant heights). */
72
+ hasPrefixSums(): boolean;
73
+ /**
74
+ * The height of a row (without its extra).
75
+ *
76
+ * @param {number} rowIndex - the row
77
+ * @returns {number} the height in px
78
+ */
79
+ getRowHeight(rowIndex: number): number;
80
+ /**
81
+ * The extra height below a row (0 = none).
82
+ *
83
+ * @param {number} rowIndex - the row
84
+ * @returns {number} the extra height in px
85
+ */
86
+ getExtraHeight(rowIndex: number): number;
87
+ /**
88
+ * The sum of the extra heights of the rows above a row.
89
+ *
90
+ * @param {number} rowIndex - the row (`rowCount` = all extras)
91
+ * @returns {number} px
92
+ */
93
+ getExtraBefore(rowIndex: number): number;
94
+ /** The sum of all extra heights. */
95
+ getExtraTotal(): number;
96
+ /**
97
+ * The top of a row: heights and extras of all rows above it. `getTop(rowCount)` = total height. The index is
98
+ * clamped to `0 … rowCount`.
99
+ *
100
+ * @param {number} rowIndex - the row
101
+ * @returns {number} px from the top of the first row
102
+ */
103
+ getTop(rowIndex: number): number;
104
+ /** The height of all rows and extras. */
105
+ getTotalHeight(): number;
106
+ /**
107
+ * The row whose slot (row + extra) contains `y`; rows of height 0 are skipped. Above the first row: 0, below the
108
+ * last: the last row, without rows: -1. O(1) for constant heights without extras, otherwise O(log n).
109
+ *
110
+ * @param {number} y - px from the top of the first row
111
+ * @returns {number} the row index
112
+ */
113
+ findIndexAt(y: number): number;
114
+ private getBaseTop;
115
+ private ensurePrefix;
116
+ private rebuildExtraPrefix;
117
+ /** The number of extras behind rows with an index < rowIndex (lower bound). */
118
+ private countExtrasBelow;
119
+ private findExtra;
120
+ }
@@ -0,0 +1,126 @@
1
+ import { ResolvedTableDirection, TableDirection } from './table-direction.type.js';
2
+
3
+ /**
4
+ * Decides the direction of a table: `"rtl"` and `"ltr"` as given, `"auto"` from the computed `direction` of the host
5
+ * (inherited from `<html dir="rtl">` or any parent) – for a host that is not in the document yet from the nearest
6
+ * `dir` attribute of the host or a parent. Missing (`undefined`) = `"ltr"`.
7
+ *
8
+ * Must be called before the table sets the `dir` attribute of the host.
9
+ *
10
+ * @param {TableDirection | undefined} direction - the option ({@link TableOptionsIf.direction})
11
+ * @param {HTMLElement} [host] - the host element of the table (needed for `"auto"`)
12
+ * @returns {ResolvedTableDirection} `"ltr"` or `"rtl"`
13
+ *
14
+ * @example
15
+ * resolveTableDirection("auto", document.getElementById("table")!); // "rtl" on a page with <html dir="rtl">
16
+ *
17
+ * @since 1.1.98
18
+ * @category Utilities
19
+ */
20
+ export declare function resolveTableDirection(direction: TableDirection | undefined, host?: HTMLElement): ResolvedTableDirection;
21
+ /**
22
+ * The horizontal scroll position of an element as distance from the **start** (≥ 0): in left-to-right `scrollLeft`,
23
+ * in right-to-left `-scrollLeft` (browsers report 0 at the right start and negative values when scrolled to the left,
24
+ * CSSOM View).
25
+ *
26
+ * @param {{ scrollLeft: number }} element - a scroll container
27
+ * @param {boolean} rtl - the container is right-to-left
28
+ * @returns {number} px from the start
29
+ *
30
+ * @example
31
+ * const fromStart = getLogicalScrollLeft(tableApi.getTableScope().scrollViewport, tableApi.getDirection() === "rtl");
32
+ *
33
+ * @since 1.1.98
34
+ * @category Utilities
35
+ */
36
+ export declare function getLogicalScrollLeft(element: {
37
+ scrollLeft: number;
38
+ }, rtl: boolean): number;
39
+ /**
40
+ * Sets the horizontal scroll position of an element as distance from the **start** (see {@link getLogicalScrollLeft}).
41
+ *
42
+ * @param {{ scrollLeft: number }} element - a scroll container
43
+ * @param {number} x - px from the start
44
+ * @param {boolean} rtl - the container is right-to-left
45
+ *
46
+ * @example
47
+ * setLogicalScrollLeft(tableApi.getTableScope().scrollViewport, 0, true); // back to the (right) start
48
+ *
49
+ * @since 1.1.98
50
+ * @category Utilities
51
+ */
52
+ export declare function setLogicalScrollLeft(element: {
53
+ scrollLeft: number;
54
+ }, x: number, rtl: boolean): void;
55
+ /**
56
+ * The horizontal scroll position for `element.scrollTo(…)` – physical, i.e. negative in right-to-left.
57
+ *
58
+ * @param {number} x - px from the start
59
+ * @param {boolean} rtl - right-to-left
60
+ * @returns {number} the value for `scrollLeft` / `scrollTo()`
61
+ *
62
+ * @example
63
+ * tableApi.getTableScope().scrollViewport.scrollTo(toPhysicalScrollLeft(200, true), 0); // 200 px from the right start
64
+ *
65
+ * @since 1.1.98
66
+ * @category Utilities
67
+ */
68
+ export declare function toPhysicalScrollLeft(x: number, rtl: boolean): number;
69
+ /**
70
+ * Whether a scroll container can still scroll horizontally by `dx` (physical: `dx > 0` = to the right) – for
71
+ * left-to-right and right-to-left containers alike.
72
+ *
73
+ * @param {{ scrollLeft: number, scrollWidth: number, clientWidth: number }} element - a scroll container
74
+ * @param {number} dx - physical delta (e.g. `WheelEvent.deltaX`)
75
+ * @param {boolean} rtl - the container is right-to-left
76
+ * @returns {boolean} true if a scroll by `dx` would move the content
77
+ *
78
+ * @example
79
+ * host.addEventListener("wheel", e => {
80
+ * const takesIt = canScrollHorizontally(host, e.deltaX, getComputedStyle(host).direction === "rtl");
81
+ * console.info(takesIt ? "the element scrolls itself" : "pass it on");
82
+ * });
83
+ *
84
+ * @since 1.1.98
85
+ * @category Utilities
86
+ */
87
+ export declare function canScrollHorizontally(element: {
88
+ scrollLeft: number;
89
+ scrollWidth: number;
90
+ clientWidth: number;
91
+ }, dx: number, rtl: boolean): boolean;
92
+ /**
93
+ * A client x coordinate (e.g. `PointerEvent.clientX`) as position in a box measured from the box's **start**:
94
+ * left-to-right from its left edge, right-to-left from its right edge.
95
+ *
96
+ * @param {number} clientX - x in client (viewport) coordinates
97
+ * @param {number} boxLeft - the left edge of the box in client coordinates
98
+ * @param {number} boxWidth - the width of the box
99
+ * @param {boolean} rtl - right-to-left
100
+ * @returns {number} px from the start of the box
101
+ *
102
+ * @example
103
+ * host.addEventListener("pointerdown", e => {
104
+ * const r = host.getBoundingClientRect();
105
+ * console.info(toLogicalX(e.clientX, r.left + host.clientLeft, host.clientWidth, true)); // px from the right edge
106
+ * });
107
+ *
108
+ * @since 1.1.98
109
+ * @category Utilities
110
+ */
111
+ export declare function toLogicalX(clientX: number, boxLeft: number, boxWidth: number, rtl: boolean): number;
112
+ /**
113
+ * Swaps the arrow keys "left" and "right" in a harmonized shortcut (e.g. `"cmd left"` → `"cmd right"`) – the
114
+ * keyboard handling of a right-to-left table, so that → moves backwards in reading direction.
115
+ *
116
+ * @param {string} shortcut - a harmonized shortcut (`harmonizeShortcut`)
117
+ * @returns {string} the shortcut with swapped arrow keys
118
+ *
119
+ * @example
120
+ * mirrorArrowKeys("left"); // "right"
121
+ * mirrorArrowKeys("cmd right"); // "cmd left"
122
+ *
123
+ * @since 1.1.98
124
+ * @category Keyboard
125
+ */
126
+ export declare function mirrorArrowKeys(shortcut: string): string;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * The writing direction of a table ({@link TableOptionsIf.direction}):
3
+ *
4
+ * - `"ltr"` (default): left to right – the first column on the left.
5
+ * - `"rtl"`: right to left (Arabic, Hebrew, Persian, Urdu …) – the first column on the right, horizontal scrolling,
6
+ * fixed columns, tree indent, resize handles and arrow keys mirrored.
7
+ * - `"auto"`: the direction of the page where the table is created (`dir="rtl"` on `<html>`, a parent element or the
8
+ * host itself → `"rtl"`).
9
+ *
10
+ * @example
11
+ * const tableOptions: TableOptionsIf = { ...new TableOptions(), direction: "rtl" };
12
+ *
13
+ * @since 1.1.98
14
+ * @category Options
15
+ */
16
+ export type TableDirection = "ltr" | "rtl" | "auto";
17
+ /**
18
+ * A resolved writing direction – what {@link TableApi.getDirection} returns (`"auto"` is already decided).
19
+ *
20
+ * @since 1.1.98
21
+ * @category Options
22
+ */
23
+ export type ResolvedTableDirection = "ltr" | "rtl";
@@ -10,6 +10,7 @@ import { SideIdent } from '../data/side-ident.type.js';
10
10
  import { SortState } from '../data/common/sort-state.type.js';
11
11
  import { SortedOptionsIf } from '../data/options/sorted-options.if.js';
12
12
  import { HtmlStyle } from './data/html-style.type.js';
13
+ import { ResolvedTableDirection } from '../rtl/table-direction.type.js';
13
14
 
14
15
  /** @category DOM */
15
16
  export interface AddColumnDivPara {
@@ -37,7 +38,54 @@ export declare class ConvenienceDomService {
37
38
  readonly domService: DomServiceIf;
38
39
  /** Width of the focus frame of a cell in px (WCAG 2.4.7: visible focus, not only a color). @since 1.1.91 */
39
40
  static focusBorderWidthInPx: number;
41
+ /** Right-to-left: `left` is written as `right` and vice versa (see {@link setDirection}). */
42
+ private rtl;
43
+ /** The physical CSS property of the inline start (`left`, right-to-left `right`). */
44
+ private startKey;
45
+ /** The physical CSS property of the inline end (`right`, right-to-left `left`). */
46
+ private endKey;
40
47
  constructor(domService: DomServiceIf);
48
+ /**
49
+ * Sets the writing direction of the table. All positions of the table are computed from the **start** of a row
50
+ * (x = 0 is the first column); in right-to-left this service writes them as `right` instead of `left` – in
51
+ * {@link setStyle}, {@link applyStyle}, {@link applyStyleInPx} and all `add…` methods – so that the whole table is
52
+ * mirrored. Called once by the table before anything is positioned.
53
+ *
54
+ * @param {ResolvedTableDirection} direction - `"ltr"` or `"rtl"`
55
+ *
56
+ * @example
57
+ * const dom = new ConvenienceDomService(new SimpleDomService());
58
+ * dom.setDirection("rtl");
59
+ * const div = document.createElement("div");
60
+ * dom.setStyle(div, "left", "20px"); // div.style.right = "20px"
61
+ *
62
+ * @since 1.1.98
63
+ */
64
+ setDirection(direction: ResolvedTableDirection): void;
65
+ /**
66
+ * The writing direction set with {@link setDirection} (default `"ltr"`).
67
+ *
68
+ * @returns {ResolvedTableDirection} `"ltr"` or `"rtl"`
69
+ * @since 1.1.98
70
+ */
71
+ getDirection(): ResolvedTableDirection;
72
+ /**
73
+ * The physical CSS property for a logical position key: in right-to-left `left` ↔ `right`, everything else as is.
74
+ *
75
+ * @param {string} key - a CSS property
76
+ * @returns {string} the property to write
77
+ * @since 1.1.98
78
+ */
79
+ toPhysicalStyleKey(key: string): string;
80
+ /**
81
+ * The position of an element from the inline start in px, as written by this service (`style.left`, right-to-left
82
+ * `style.right`); 0 if it has none.
83
+ *
84
+ * @param {HTMLElement} ele - an element positioned by this service
85
+ * @returns {number} px
86
+ * @since 1.1.98
87
+ */
88
+ getInlineStart(ele: HTMLElement): number;
41
89
  setStyle(el: any, style: string, value: any): any;
42
90
  applyStyle(ele: HTMLDivElement, style: HtmlStyle): HTMLDivElement;
43
91
  applyDisplayNoneStyle(ele: HTMLDivElement): HTMLDivElement;
@@ -46,7 +94,7 @@ export declare class ConvenienceDomService {
46
94
  applyStylePosistionRelative(ele: HTMLDivElement): HTMLDivElement;
47
95
  applyStylePosistionAbsolute(ele: HTMLDivElement): HTMLDivElement;
48
96
  applyStyleFullSize(ele: HTMLDivElement): HTMLDivElement;
49
- applyStyleOverflowAuto(overflowX: "scroll" | "auto" | undefined, overflowY: "scroll" | "auto" | undefined, ele: HTMLDivElement): HTMLDivElement;
97
+ applyStyleOverflowAuto(overflowX: "auto" | "scroll" | undefined, overflowY: "auto" | "scroll" | undefined, ele: HTMLDivElement): HTMLDivElement;
50
98
  applyStyleNoPadding(ele: HTMLDivElement): HTMLDivElement;
51
99
  appendRelativeChildDiv(parent: HTMLDivElement): DivScope;
52
100
  appendText(parent: HTMLDivElement, text: string): HTMLElement;