@star-ng/ui 0.1.0 → 0.2.0

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.
@@ -1,37 +1,121 @@
1
1
  import * as i0 from "@angular/core";
2
2
  import { TemplateRef } from "@angular/core";
3
3
  import { StarOptionKey } from "@star-ng/ui/core";
4
- import { StarTreeNode } from "@star-ng/ui/tree";
4
+ import { StarTreeNode, TreeRow } from "@star-ng/ui/tree";
5
+ /** The rows in the DOM and the height of the two spacer rows that stand for the others. */
6
+ interface RowWindow {
7
+ /** Index of the first rendered row. */
8
+ readonly start: number;
9
+ /** Index after the last rendered row. */
10
+ readonly end: number;
11
+ /** Height in pixels of the rows before `start`. */
12
+ readonly before: number;
13
+ /** Height in pixels of the rows from `end` on. */
14
+ readonly after: number;
15
+ }
16
+ /** `aria-rowcount`: the visible rows and the header row. */
17
+ declare function ariaRowCount(rowCount: number): number;
5
18
  export interface StarTreeTableColumn {
6
19
  readonly field: string;
7
20
  readonly header: string;
8
21
  readonly width?: string;
9
22
  readonly align?: 'start' | 'center' | 'end';
10
23
  }
11
- interface TreeRow {
12
- readonly node: StarTreeNode<Record<string, unknown>>;
13
- readonly level: number;
14
- readonly setSize: number;
15
- readonly position: number;
24
+ type TreeTableNode = StarTreeNode<Record<string, unknown>>;
25
+ type TreeTableRow = TreeRow<TreeTableNode>;
26
+ /** One rendered row: the row and its `aria-rowindex` (null in the default rendering). */
27
+ interface TreeTableEntry {
28
+ readonly row: TreeTableRow;
29
+ readonly aria: number | null;
16
30
  }
17
31
  /**
18
32
  * Tree table (PrimeNG `p-treetable`): hierarchical rows with expand / collapse in a table,
19
- * `role="treegrid"`, Right / Left on a row expand / collapse, Up / Down move between rows.
20
- * Node `data` holds the column values; the first column shows the tree toggle.
33
+ * `role="treegrid"`, Right / Left on a row expand / collapse, Up / Down, Home and End move between
34
+ * rows. Node `data` holds the column values; the first column shows the tree toggle.
35
+ *
36
+ * Large tables: `virtualScroll` with `scrollHeight` and `virtualItemSize` renders only the rows in
37
+ * and near the viewport. It is still one `<table>`: a spacer row above and one below the rendered
38
+ * rows stand for the others, the header is sticky, and every row has the fixed height
39
+ * `virtualItemSize`. Keys, `aria-rowcount`, `aria-rowindex`, `aria-level`, `aria-setsize` and
40
+ * `aria-posinset` work over all open rows, also the ones that are not in the DOM.
21
41
  *
22
42
  * `<star-tree-table [nodes]="budget" [columns]="cols" [(expanded)]="open" ariaLabel="Budget" />`
23
43
  */
24
44
  export declare class StarTreeTable {
25
45
  protected readonly text: import("@star-ng/ui/core").StarUiText;
26
- readonly nodes: import("@angular/core").InputSignal<readonly StarTreeNode<Record<string, unknown>>[]>;
46
+ private readonly doc;
47
+ readonly nodes: import("@angular/core").InputSignal<readonly TreeTableNode[]>;
27
48
  readonly columns: import("@angular/core").InputSignal<readonly StarTreeTableColumn[]>;
28
49
  readonly expanded: import("@angular/core").ModelSignal<readonly string[]>;
29
50
  readonly ariaLabel: import("@angular/core").InputSignal<string | null>;
30
- protected readonly rows: import("@angular/core").Signal<TreeRow[]>;
51
+ /**
52
+ * Maximum height of the table (a CSS length, for example `20rem`). The table scrolls inside it
53
+ * and the header stays at the top.
54
+ */
55
+ readonly scrollHeight: import("@angular/core").InputSignal<string | null>;
56
+ /**
57
+ * Windowed rendering for large tables: only the rows in and near the viewport are in the DOM.
58
+ * Needs `scrollHeight`, and every row gets the fixed height `virtualItemSize` with its text on
59
+ * one line. Without `scrollHeight` the table renders all rows as usual.
60
+ */
61
+ readonly virtualScroll: import("@angular/core").InputSignalWithTransform<boolean, unknown>;
62
+ /**
63
+ * Row height in pixels in windowed rendering. The default is the natural height of a row of
64
+ * this table.
65
+ */
66
+ readonly virtualItemSize: import("@angular/core").InputSignalWithTransform<number, unknown>;
67
+ private readonly scroller;
68
+ private readonly header;
69
+ private readonly openKeys;
70
+ private readonly parents;
71
+ /** The rows whose parents are open, in order. The sibling numbers come from the data. */
72
+ protected readonly rows: import("@angular/core").Signal<TreeTableRow[]>;
73
+ private readonly rowIndex;
74
+ /**
75
+ * The active row (the one with `tabindex` 0). It stays a row after the rows change, and it is
76
+ * kept in state, not read from the DOM: in windowed rendering its element may not exist.
77
+ */
31
78
  protected readonly focusKey: import("@angular/core").WritableSignal<string | null>;
79
+ protected readonly windowed: import("@angular/core").Signal<boolean>;
80
+ private readonly scrollTop;
81
+ private readonly viewportHeight;
82
+ /** The rows that are in the DOM. Without windowed rendering: all of them. */
83
+ protected readonly window: import("@angular/core").Signal<RowWindow>;
84
+ protected readonly entries: import("@angular/core").Signal<TreeTableEntry[]>;
85
+ /** The active row is in the DOM. If it is not, the scroll area takes the Tab stop. */
86
+ protected readonly activeRendered: import("@angular/core").Signal<boolean>;
87
+ protected readonly ariaRowCount: typeof ariaRowCount;
88
+ /** Row to focus as soon as its element exists (it may need scrolling in first). */
89
+ private pendingFocus;
90
+ /** The focus waits on the scroll area because the focused row left the DOM. */
91
+ protected parked: boolean;
92
+ constructor();
32
93
  protected isOpen(key: string): boolean;
33
94
  toggle(key: string, open?: boolean): void;
34
- protected onKeydown(event: KeyboardEvent, row: TreeRow): void;
95
+ /** Reads the scroll position and the height of the scroll area (windowed rendering). */
96
+ protected syncScroll(): void;
97
+ protected onRowFocus(event: FocusEvent, key: string): void;
98
+ /**
99
+ * Moves the focus to a row. The row becomes the active row at once. In windowed rendering the
100
+ * row is scrolled into the window first, and the focus is applied after its element renders.
101
+ */
102
+ private focusRow;
103
+ /** Scrolls a row fully into view, below the sticky header, with the least movement. */
104
+ private revealRow;
105
+ private rowElement;
106
+ private applyPendingFocus;
107
+ /**
108
+ * The browser drops the focus to the page body when the focused element leaves the DOM. Before
109
+ * the focused row leaves the window, the focus moves to the scroll area; the keys still work
110
+ * there, and the focus returns to the row when it renders again.
111
+ */
112
+ private parkFocus;
113
+ private restoreParkedFocus;
114
+ /** Tab into the scroll area (no row has the Tab stop): the focus goes to the active row. */
115
+ protected onScrollerFocus(): void;
116
+ /** Keys on the scroll area itself (the focus is parked there): they act on the active row. */
117
+ protected onScrollerKeydown(event: KeyboardEvent): void;
118
+ protected onKeydown(event: KeyboardEvent, row: TreeTableRow): void;
35
119
  static ɵfac: i0.ɵɵFactoryDeclaration<StarTreeTable, never>;
36
120
  static ɵcmp: i0.ɵɵComponentDeclaration<StarTreeTable, "star-tree-table", never, {
37
121
  "nodes": {
@@ -54,6 +138,21 @@ export declare class StarTreeTable {
54
138
  "required": false;
55
139
  "isSignal": true;
56
140
  };
141
+ "scrollHeight": {
142
+ "alias": "scrollHeight";
143
+ "required": false;
144
+ "isSignal": true;
145
+ };
146
+ "virtualScroll": {
147
+ "alias": "virtualScroll";
148
+ "required": false;
149
+ "isSignal": true;
150
+ };
151
+ "virtualItemSize": {
152
+ "alias": "virtualItemSize";
153
+ "required": false;
154
+ "isSignal": true;
155
+ };
57
156
  }, {
58
157
  "expanded": "expandedChange";
59
158
  }, never, never, true, never>;
@@ -1,33 +1,7 @@
1
1
  import { FormValueControl } from "@angular/forms/signals";
2
- import { StarOptionKey } from "@star-ng/ui/core";
2
+ import { StarOptionKey, StarWindowRow } from "@star-ng/ui/core";
3
3
  import { StarOptionTemplate } from "@star-ng/ui/select";
4
4
  import * as i0 from "@angular/core";
5
- /**
6
- * Pure helpers of the virtual list in `star-listbox`: row model, index math, keyboard target and
7
- * ARIA values. They have no DOM access, so unit tests can check them without layout.
8
- */
9
- /** One row of the virtual list. Group headings and options have the same height. */
10
- type ListboxRow<T> = {
11
- readonly kind: 'group';
12
- readonly key: string;
13
- readonly label: string;
14
- readonly id: string;
15
- } | {
16
- readonly kind: 'option';
17
- readonly key: string;
18
- readonly option: T;
19
- /** Index in the visible options (keyboard order). */
20
- readonly index: number;
21
- /** Id of the heading row of the group, or null without groups. */
22
- readonly groupId: string | null;
23
- /** Index of the heading row of the group, or -1 without groups. */
24
- readonly groupRow: number;
25
- };
26
- /** The rows, and for each visible option the index of its row. */
27
- interface ListboxLayout<T> {
28
- readonly rows: readonly ListboxRow<T>[];
29
- readonly rowOfOption: readonly number[];
30
- }
31
5
  /**
32
6
  * Inline list choice (PrimeNG `p-listbox`): single or `multiple` (checkboxes), filter,
33
7
  * option template, WAI-ARIA listbox keyboard (Arrow, Home / End, Space / Enter, typeahead).
@@ -81,17 +55,17 @@ export declare class StarListbox<TOption = unknown, TValue = TOption> implements
81
55
  protected groupId(index: number): string | null;
82
56
  protected readonly itemSize: import("@angular/core").Signal<number>;
83
57
  /** Group headings and options in one list of rows of equal height. */
84
- protected readonly layout: import("@angular/core").Signal<ListboxLayout<TOption>>;
58
+ protected readonly layout: import("@angular/core").Signal<import("@star-ng/ui/core").StarWindowLayout<TOption>>;
85
59
  protected readonly viewportHeight: import("@angular/core").Signal<string>;
86
60
  /** Rows that are in the DOM now: `[start, end)`, reported by the CDK viewport. */
87
61
  private readonly renderedRange;
88
- protected readonly trackRow: (_index: number, row: ListboxRow<TOption>) => string;
62
+ protected readonly trackRow: (_index: number, row: StarWindowRow<TOption>) => string;
89
63
  protected positionOf(index: number): {
90
64
  setsize: number;
91
65
  posinset: number;
92
66
  };
93
67
  /** The heading of the group, only while its row is in the DOM (the id must exist). */
94
- protected describedBy(row: Extract<ListboxRow<TOption>, {
68
+ protected describedBy(row: Extract<StarWindowRow<TOption>, {
95
69
  kind: 'option';
96
70
  }>): string | null;
97
71
  /** `aria-activedescendant` only names an option that is in the DOM. */
@@ -193,5 +167,4 @@ export declare class StarListbox<TOption = unknown, TValue = TOption> implements
193
167
  "value": "valueChange";
194
168
  "touch": "touch";
195
169
  }, ["optionTemplate"], never, true, never>;
196
- }
197
- export {};
170
+ }
@@ -1,5 +1,5 @@
1
1
  import { FormValueControl } from "@angular/forms/signals";
2
- import { StarOptionKey } from "@star-ng/ui/core";
2
+ import { StarOptionKey, StarWindowRow } from "@star-ng/ui/core";
3
3
  import { StarMenuItem } from "@star-ng/ui/menu";
4
4
  import * as i0 from "@angular/core";
5
5
  import { ElementRef, TemplateRef } from "@angular/core";
@@ -185,19 +185,7 @@ export declare class StarOptionTemplate<T = unknown> {
185
185
  }, {}, never, never, true, never>;
186
186
  }
187
187
  /** One rendered line of the option list: a group heading or an option. */
188
- export type StarListRow<T> = {
189
- readonly kind: 'group';
190
- readonly key: string;
191
- readonly label: string;
192
- readonly id: string;
193
- } | {
194
- readonly kind: 'option';
195
- readonly key: string;
196
- readonly option: T;
197
- /** Index in the visible options (keyboard order). */
198
- readonly index: number;
199
- readonly groupId: string | null;
200
- };
188
+ export type StarListRow<T> = StarWindowRow<T>;
201
189
  /**
202
190
  * Shared state and keyboard model of the dropdown list controls (WAI-ARIA combobox +
203
191
  * listbox with `aria-activedescendant`). Subclasses add the selection model.
@@ -207,6 +195,8 @@ export declare abstract class StarOptionListBase<TOption, TValue> {
207
195
  private readonly directionSync;
208
196
  protected readonly text: import("@star-ng/ui/core").StarUiText;
209
197
  private readonly ids;
198
+ private readonly document;
199
+ private readonly injector;
210
200
  readonly options: import("@angular/core").InputSignal<readonly TOption[]>;
211
201
  readonly optionLabel: import("@angular/core").InputSignal<StarOptionKey<TOption> | null>;
212
202
  /** Value stored in the form. Omit to store the whole option. */
@@ -221,7 +211,10 @@ export declare abstract class StarOptionListBase<TOption, TValue> {
221
211
  readonly optionGroup: import("@angular/core").InputSignal<StarOptionKey<TOption> | null>;
222
212
  /**
223
213
  * Renders only the lines in view, for long option lists. Every line has the height
224
- * `virtualItemSize` and option text stays on one line.
214
+ * `virtualItemSize` and option text stays on one line. Keys and type-ahead work over all
215
+ * options, also those that are not in the DOM. Page up and Page down move by the lines in view.
216
+ * Each option has `aria-setsize` and `aria-posinset` for the whole list, and
217
+ * `aria-activedescendant` only names an option that is in the DOM.
225
218
  */
226
219
  readonly virtualScroll: import("@angular/core").InputSignalWithTransform<boolean, unknown>;
227
220
  /** Line height in pixels with `virtualScroll`. */
@@ -260,6 +253,8 @@ export declare abstract class StarOptionListBase<TOption, TValue> {
260
253
  protected readonly visibleOptions: import("@angular/core").Signal<readonly TOption[]>;
261
254
  /** With `optionGroup`: keeps each group together, in the order the groups first appear. */
262
255
  protected groupedOrder(options: readonly TOption[]): readonly TOption[];
256
+ /** Group headings and options in display order, and the row of each option. */
257
+ protected readonly layout: import("@angular/core").Signal<import("@star-ng/ui/core").StarWindowLayout<TOption>>;
263
258
  /** Group headings and options, in display order. */
264
259
  protected readonly rows: import("@angular/core").Signal<readonly StarListRow<TOption>[]>;
265
260
  private readonly listScrollTop;
@@ -273,7 +268,12 @@ export declare abstract class StarOptionListBase<TOption, TValue> {
273
268
  }>;
274
269
  protected readonly renderedRows: import("@angular/core").Signal<readonly StarListRow<TOption>[]>;
275
270
  protected onListScroll(event: Event): void;
271
+ /** `aria-activedescendant` only names an option that is in the DOM. */
276
272
  protected readonly activeId: import("@angular/core").Signal<string | null>;
273
+ /** The heading of the group, only while its row is in the DOM (the id must exist). */
274
+ protected describedBy(row: Extract<StarListRow<TOption>, {
275
+ kind: 'option';
276
+ }>): string | null;
277
277
  protected readonly emptyText: import("@angular/core").Signal<string>;
278
278
  private typeahead;
279
279
  private typeaheadTimer;
@@ -303,14 +303,32 @@ export declare abstract class StarOptionListBase<TOption, TValue> {
303
303
  protected onPanelMousedown(event: MouseEvent): void;
304
304
  protected onTriggerBlur(): void;
305
305
  protected onFilterInput(value: string): void;
306
+ /**
307
+ * Shows the list from its top. The window and the scroll position of the element change
308
+ * together: a new list that is still long would else keep the old position of the element
309
+ * while the window draws its first lines.
310
+ */
311
+ protected resetScroll(): void;
306
312
  protected onOptionClick(option: TOption, index: number): void;
307
313
  /** Keyboard model for both the trigger and the filter input. */
308
314
  protected onKeydown(event: KeyboardEvent): void;
309
315
  private chooseActive;
310
316
  private moveActive;
317
+ /** Page up / Page down: moves the active option by the lines in view, over all options. */
318
+ private movePage;
311
319
  protected firstEnabledFrom(start: number, step: 1 | -1): number;
312
320
  /** Type a letter to jump to the next option whose label starts with the typed text. */
313
321
  private runTypeahead;
322
+ /** The list element in the panel (the panel is outside the host, in the overlay). */
323
+ private listElement;
324
+ /**
325
+ * Moves the list element to the offset of the window. A list that has just opened may not be
326
+ * able to take it yet: it may be too short until its window is drawn, or not laid out until
327
+ * the popover shows. The browser then keeps the old offset and sends no scroll event, so the
328
+ * window would draw lines that the element does not show. Try again for some frames, until the
329
+ * element is there or the user scrolls.
330
+ */
331
+ private applyScroll;
314
332
  private scrollActiveIntoView;
315
333
  static ɵfac: i0.ɵɵFactoryDeclaration<StarOptionListBase<any, any>, never>;
316
334
  static ɵdir: i0.ɵɵDirectiveDeclaration<StarOptionListBase<any, any>, never, never, {
@@ -469,6 +487,11 @@ export declare class StarMultiSelect<TOption = unknown, TValue = TOption> extend
469
487
  private readonly selectableVisible;
470
488
  /** `true`, `false` or `'mixed'` for the "select all" box (over visible, enabled options). */
471
489
  protected readonly allState: import("@angular/core").Signal<boolean | "mixed">;
490
+ /**
491
+ * The chosen values in a `Set`, for the default `compareWith`. A lookup then costs one step and
492
+ * not one scan of the value: with 10,000 chosen options a scan per option took seconds.
493
+ */
494
+ private readonly chosenSet;
472
495
  protected isSelected(option: TOption): boolean;
473
496
  protected choose(option: TOption): void;
474
497
  protected initialActiveIndex(): number;
@@ -812,18 +812,74 @@ export interface StarPrintFitMeasure {
812
812
  /** Minimum width of each column at the last step that keeps words whole. */
813
813
  readonly columns: readonly number[];
814
814
  }
815
+ /** A style sheet of the print document that can be changed and removed again. */
816
+ export interface StarPrintStyle {
817
+ /** Replaces the rules of the sheet. */
818
+ set(css: string): void;
819
+ /** Takes the sheet out of the document. */
820
+ remove(): void;
821
+ }
822
+ /**
823
+ * Adds a style sheet to `doc`. The sheet is a constructed style sheet (`new CSSStyleSheet()`,
824
+ * created in the realm of the document, then `adoptedStyleSheets`). A policy whose `style-src`
825
+ * has no `'unsafe-inline'` blocks an inline `style` element, but it does not block a constructed
826
+ * sheet. An environment without constructed style sheets (or one where they fail) gets a
827
+ * `style` element instead: that is the only fallback, and a strict policy blocks it.
828
+ */
829
+ export declare function addPrintStyle(doc: Document, css?: string): StarPrintStyle;
830
+ /**
831
+ * Adds the base style of the print document (see `buildPrintDocument`) to `doc`. The document
832
+ * has no `style` element and no `style` attribute, so a Content-Security-Policy without
833
+ * `'unsafe-inline'` in `style-src` does not unstyle it. Call it when the frame has loaded,
834
+ * before `fitPrintDocument` measures the table.
835
+ */
836
+ export declare function stylePrintDocument(doc: Document): void;
815
837
  /**
816
838
  * Measures the minimum width of the print table at each fit step, in the layout of `doc`, and
817
- * adds the style sheet of `printFitStyle` to it. Call it when the frame has loaded, before the
818
- * print dialog opens. The widths are read in the real fonts of the document, so the text of any
819
- * font, wide or narrow, gets the steps it needs.
839
+ * adds the style sheet of `printFitStyle` to it (a constructed style sheet, see
840
+ * `addPrintStyle`). Call it when the frame has loaded and has its base style
841
+ * (`stylePrintDocument`), before the print dialog opens. The widths are read in the real fonts
842
+ * of the document, so the text of any font, wide or narrow, gets the steps it needs.
820
843
  */
821
844
  export declare function fitPrintDocument(doc: Document): StarPrintFitMeasure;
845
+ /** Alignment of a print column. `start` and `end` follow the direction of the document. */
846
+ export type StarPrintAlign = 'start' | 'center' | 'end';
847
+ /**
848
+ * Alignment of a print column. The column definition decides: an explicit `align` wins. Without
849
+ * it a column in which every value is a number (and there is at least one) is `end`, and any
850
+ * other column is `start`. The type of the printed text does not matter: a number that a
851
+ * `format` function turned into a string still sits in a number column. `values` are the cell
852
+ * values before formatting.
853
+ */
854
+ export declare function printColumnAlign(align: StarPrintAlign | undefined, values: readonly unknown[]): StarPrintAlign;
855
+ /** Language and direction of the print document. */
856
+ export interface StarPrintContext {
857
+ readonly lang?: string;
858
+ readonly dir?: 'ltr' | 'rtl';
859
+ }
860
+ /**
861
+ * `lang` and `dir` for the print document: the nearest ancestor of `host` (the element itself
862
+ * first) that has the attribute with a usable value, else the `<html>` element of `doc`. An
863
+ * empty `lang` and a `dir` other than `ltr` or `rtl` (such as `auto`) are skipped.
864
+ */
865
+ export declare function printContext(host: Element | null, doc: Document): StarPrintContext;
866
+ /** Options of `buildPrintDocument`. */
867
+ export interface StarPrintOptions extends StarPrintContext {
868
+ /** Alignment of each column, in column order (see `printColumnAlign`). */
869
+ readonly align?: readonly StarPrintAlign[];
870
+ }
871
+ /** Base style of the print document. It is added with `stylePrintDocument`, not inline. */
872
+ export declare const PRINT_STYLE = "body{font:12px/1.4 system-ui,sans-serif;color:#000;margin:16px}\nh1{font-size:16px;margin:0 0 12px;overflow-wrap:anywhere}\n.fit{container-type:inline-size}\ntable{width:100%;border-collapse:collapse}\nth,td{padding:4px 8px;border:1px solid #999;text-align:start;vertical-align:top;overflow-wrap:break-word}\nth{background:#eee}\nth.c,td.c{text-align:center}\nth.e,td.e{text-align:end}\ntd.s{white-space:nowrap}\nthead{display:table-header-group}\ntr{break-inside:avoid}";
822
873
  /**
823
- * HTML document with one table, for the browser print dialog ("Save as PDF"). The table is
824
- * built to print at the normal size. When the paper is too narrow for it, call
825
- * `fitPrintDocument` on the loaded document: the table then wraps, shrinks or breaks words
826
- * (see `PRINT_FIT_STEPS`) so that no column is cut off.
874
+ * HTML document with one table, for the browser print dialog ("Save as PDF"). The markup has
875
+ * no `style` element and no `style` attribute: the style is added to the loaded document by
876
+ * `stylePrintDocument`, as a constructed style sheet, so that it works under a policy without
877
+ * `'unsafe-inline'` in `style-src`. The table is built to print at the normal size. When the
878
+ * paper is too narrow for it, call `fitPrintDocument` on the loaded document: the table then
879
+ * wraps, shrinks or breaks words (see `PRINT_FIT_STEPS`) so that no column is cut off.
880
+ *
881
+ * `options.align` sets the alignment of each column (cells and header). `options.lang` and
882
+ * `options.dir` go on the `html` element.
827
883
  */
828
- export declare function buildPrintDocument(title: string, headers: readonly string[], rows: readonly (readonly StarExportCell[])[]): string;
884
+ export declare function buildPrintDocument(title: string, headers: readonly string[], rows: readonly (readonly StarExportCell[])[], options?: StarPrintOptions): string;
829
885
  export {};
@@ -1,6 +1,11 @@
1
1
  import { ConnectedPosition } from "@angular/cdk/overlay";
2
2
  import { FormValueControl } from "@angular/forms/signals";
3
3
  import * as i0 from "@angular/core";
4
+ /** The part of a tree node that the helpers read. `StarTreeNode` fits it. */
5
+ interface BranchLike<N> {
6
+ readonly key: string;
7
+ readonly children?: readonly N[];
8
+ }
4
9
  /** One visible row of the flattened tree. */
5
10
  interface TreeRow<N> {
6
11
  readonly node: N;
@@ -12,6 +17,34 @@ interface TreeRow<N> {
12
17
  readonly position: number;
13
18
  readonly parentKey: string | null;
14
19
  }
20
+ /**
21
+ * Flattens the nodes whose parents are open into rows. The sibling numbers come from the data,
22
+ * so they stay right for rows that are not in the DOM. `openAll` shows every branch open (filter).
23
+ */
24
+ export declare function flattenVisible<N extends BranchLike<N>>(nodes: readonly N[], open: ReadonlySet<string>, openAll?: boolean): TreeRow<N>[];
25
+ /** Row index of each key. */
26
+ export declare function rowIndexOf<N extends BranchLike<N>>(rows: readonly TreeRow<N>[]): Map<string, number>;
27
+ /** Parent key of every node (null at the top). */
28
+ export declare function parentsOf<N extends BranchLike<N>>(nodes: readonly N[]): Map<string, string | null>;
29
+ /**
30
+ * The key that keeps the focus after the rows changed: the previous key if it is still a row,
31
+ * else its nearest ancestor that is a row, else the first row.
32
+ */
33
+ export declare function stillFocusable(previous: string | null | undefined, rowIndex: ReadonlyMap<string, number>, parents: ReadonlyMap<string, string | null>, firstKey: string | null): string | null;
34
+ /**
35
+ * The row that a navigation key moves to, or null when the key does not move (or there is no
36
+ * target). Arrow right on a closed branch and arrow left on an open branch toggle it: they have
37
+ * no target.
38
+ */
39
+ export declare function navigationTarget<N extends BranchLike<N>>(rows: readonly TreeRow<N>[], index: number, key: string, state: {
40
+ hasChildren: boolean;
41
+ expanded: boolean;
42
+ }): string | null;
43
+ /**
44
+ * Scroll offset that brings a fixed-size row fully into view with the least movement, or null
45
+ * when the row is already fully visible.
46
+ */
47
+ export declare function scrollOffsetToReveal(index: number, itemSize: number, scrollOffset: number, viewportSize: number): number | null;
15
48
  /** Tree data (PrimeNG `TreeNode`). `key` must be unique in the tree. */
16
49
  export interface StarTreeNode<T = unknown> {
17
50
  readonly key: string;
@@ -307,4 +340,4 @@ export declare class StarTreeSelect implements FormValueControl<readonly string[
307
340
  "touch": "touch";
308
341
  }, never, never, true, never>;
309
342
  }
310
- export {};
343
+ export type { BranchLike, TreeRow };