@neovici/cosmoz-treenode-navigator 8.5.0 → 9.0.1

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 (53) hide show
  1. package/NOTICE +6 -0
  2. package/README.md +143 -48
  3. package/dist/cosmoz-treenode-button-view.js +12 -1
  4. package/dist/cosmoz-treenode-button-view.js.map +1 -1
  5. package/dist/cosmoz-treenode-button-view.styles.d.ts.map +1 -1
  6. package/dist/cosmoz-treenode-button-view.styles.js +34 -15
  7. package/dist/cosmoz-treenode-button-view.styles.js.map +1 -1
  8. package/dist/cosmoz-treenode-navigator.d.ts +1 -1
  9. package/dist/cosmoz-treenode-navigator.d.ts.map +1 -1
  10. package/dist/cosmoz-treenode-navigator.js +128 -267
  11. package/dist/cosmoz-treenode-navigator.js.map +1 -1
  12. package/dist/cosmoz-treenode-navigator.styles.d.ts.map +1 -1
  13. package/dist/cosmoz-treenode-navigator.styles.js +59 -76
  14. package/dist/cosmoz-treenode-navigator.styles.js.map +1 -1
  15. package/dist/hooks/useNodes.d.ts.map +1 -1
  16. package/dist/hooks/useNodes.js +2 -1
  17. package/dist/hooks/useNodes.js.map +1 -1
  18. package/dist/hooks/useSourceTree.d.ts +33 -0
  19. package/dist/hooks/useSourceTree.d.ts.map +1 -0
  20. package/dist/hooks/useSourceTree.js +102 -0
  21. package/dist/hooks/useSourceTree.js.map +1 -0
  22. package/dist/tree-view/flatten.d.ts +29 -0
  23. package/dist/tree-view/flatten.d.ts.map +1 -0
  24. package/dist/tree-view/flatten.js +88 -0
  25. package/dist/tree-view/flatten.js.map +1 -0
  26. package/dist/tree-view/render.d.ts +22 -0
  27. package/dist/tree-view/render.d.ts.map +1 -0
  28. package/dist/tree-view/render.js +85 -0
  29. package/dist/tree-view/render.js.map +1 -0
  30. package/dist/tree-view/styles.d.ts +3 -0
  31. package/dist/tree-view/styles.d.ts.map +1 -0
  32. package/dist/tree-view/styles.js +134 -0
  33. package/dist/tree-view/styles.js.map +1 -0
  34. package/dist/tree-view/tree-view.d.ts +25 -0
  35. package/dist/tree-view/tree-view.d.ts.map +1 -0
  36. package/dist/tree-view/tree-view.js +27 -0
  37. package/dist/tree-view/tree-view.js.map +1 -0
  38. package/dist/tree-view/types.d.ts +59 -0
  39. package/dist/tree-view/types.d.ts.map +1 -0
  40. package/dist/tree-view/types.js +2 -0
  41. package/dist/tree-view/types.js.map +1 -0
  42. package/dist/tree-view/use-tree-view.d.ts +18 -0
  43. package/dist/tree-view/use-tree-view.d.ts.map +1 -0
  44. package/dist/tree-view/use-tree-view.js +175 -0
  45. package/dist/tree-view/use-tree-view.js.map +1 -0
  46. package/dist/util/source-tree.d.ts +26 -0
  47. package/dist/util/source-tree.d.ts.map +1 -0
  48. package/dist/util/source-tree.js +91 -0
  49. package/dist/util/source-tree.js.map +1 -0
  50. package/dist/util/source.d.ts.map +1 -1
  51. package/dist/util/source.js +4 -1
  52. package/dist/util/source.js.map +1 -1
  53. package/package.json +9 -8
@@ -0,0 +1,134 @@
1
+ import { css } from '@pionjs/pion';
2
+ // Rows follow the Untitled UI tree view and the main menu's gray scale:
3
+ // rest, hover and selected are three steps of one gray, and indent guides
4
+ // run through the full row height so they read as continuous lines.
5
+ export default css `
6
+ :host {
7
+ display: block;
8
+ overflow: hidden;
9
+ font-family: var(--cz-font-body, inherit);
10
+ --row-height: 32px;
11
+ --indent: 20px;
12
+ }
13
+
14
+ :host([size='md']) {
15
+ --row-height: 40px;
16
+ }
17
+
18
+ .tree {
19
+ height: 100%;
20
+ overflow: auto;
21
+ outline: none;
22
+ }
23
+
24
+ .row {
25
+ display: flex;
26
+ align-items: stretch;
27
+ width: 100%;
28
+ height: var(--row-height);
29
+ box-sizing: border-box;
30
+ cursor: pointer;
31
+ user-select: none;
32
+ outline: none;
33
+ color: var(--cz-color-text-secondary);
34
+ font-size: var(--cz-text-sm);
35
+ line-height: var(--cz-text-sm-line-height);
36
+ font-weight: var(--cz-font-weight-medium);
37
+ }
38
+
39
+ :host([size='md']) .row {
40
+ font-size: var(--cz-text-base);
41
+ line-height: var(--cz-text-base-line-height);
42
+ }
43
+
44
+ .guide {
45
+ flex: 0 0 var(--indent);
46
+ position: relative;
47
+ }
48
+
49
+ .guide::before {
50
+ content: '';
51
+ position: absolute;
52
+ inset-block: 0;
53
+ inset-inline-start: calc(var(--indent) / 2 + 4px);
54
+ border-inline-start: 1px solid var(--cz-color-border-secondary);
55
+ }
56
+
57
+ .content {
58
+ flex: 1;
59
+ min-width: 0;
60
+ display: flex;
61
+ align-items: center;
62
+ gap: calc(var(--cz-spacing) * 1);
63
+ margin-block: 1px;
64
+ padding-inline: calc(var(--cz-spacing) * 1) calc(var(--cz-spacing) * 2);
65
+ border-radius: var(--cz-radius-sm);
66
+ transition:
67
+ background 0.15s,
68
+ color 0.15s;
69
+ }
70
+
71
+ .row:hover .content {
72
+ background: var(--cz-color-bg-primary-hover);
73
+ color: var(--cz-color-text-secondary-hover);
74
+ }
75
+
76
+ .row[aria-selected='true'] .content {
77
+ background: var(--cz-color-bg-tertiary);
78
+ color: var(--cz-color-text-primary);
79
+ font-weight: var(--cz-font-weight-semibold);
80
+ }
81
+
82
+ .row:focus-visible .content {
83
+ box-shadow: var(--cz-focus-ring);
84
+ }
85
+
86
+ .toggle {
87
+ flex: 0 0 var(--indent);
88
+ height: var(--indent);
89
+ display: inline-flex;
90
+ align-items: center;
91
+ justify-content: center;
92
+ border-radius: var(--cz-radius-xs);
93
+ color: var(--cz-color-fg-quaternary);
94
+ }
95
+
96
+ .toggle svg {
97
+ transition: rotate 0.15s ease-in-out;
98
+ }
99
+
100
+ .row[aria-expanded='true'] .toggle svg {
101
+ rotate: 90deg;
102
+ }
103
+
104
+ .row[aria-expanded] .toggle:hover {
105
+ color: var(--cz-color-text-secondary);
106
+ background: var(--cz-color-bg-tertiary);
107
+ }
108
+
109
+ .icon {
110
+ display: inline-flex;
111
+ flex: none;
112
+ color: var(--cz-color-fg-quaternary);
113
+ }
114
+
115
+ .row[aria-selected='true'] .icon {
116
+ color: var(--cz-color-text-primary);
117
+ }
118
+
119
+ .label {
120
+ flex: 1;
121
+ min-width: 0;
122
+ overflow: hidden;
123
+ text-overflow: ellipsis;
124
+ white-space: nowrap;
125
+ }
126
+
127
+ @media (prefers-reduced-motion: reduce) {
128
+ .content,
129
+ .toggle svg {
130
+ transition: none;
131
+ }
132
+ }
133
+ `;
134
+ //# sourceMappingURL=styles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styles.js","sourceRoot":"","sources":["../../src/tree-view/styles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAEnC,wEAAwE;AACxE,0EAA0E;AAC1E,oEAAoE;AACpE,eAAe,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgIjB,CAAC"}
@@ -0,0 +1,25 @@
1
+ import type { Props, TreeViewApi } from './types';
2
+ /**
3
+ * A tree of nested rows, after the Untitled UI tree view.
4
+ *
5
+ * Clicking a row selects it; the chevron expands it. Double-click or Enter
6
+ * activates the row (`activate` event). Only expanded branches are
7
+ * flattened and the rows are virtualized, so very large trees stay fast.
8
+ *
9
+ * @fires selected-changed - `detail.value` is the selected id
10
+ * @fires expanded-changed - `detail.value` is the array of expanded ids
11
+ * @fires activate - `detail` is `{ id, node }`
12
+ * @csspart tree - the scrolling container (`role="tree"`)
13
+ * @csspart row - a row (`role="treeitem"`)
14
+ * @csspart content - the highlighted part of a row
15
+ * @csspart toggle - the chevron
16
+ * @csspart label - the row text
17
+ */
18
+ declare const TreenodeTreeView: <T>(host: HTMLElement & Props<T>) => import("lit-html").TemplateResult<1>;
19
+ declare global {
20
+ interface HTMLElementTagNameMap {
21
+ 'cosmoz-treenode-tree-view': HTMLElement & Props & TreeViewApi;
22
+ }
23
+ }
24
+ export { TreenodeTreeView };
25
+ //# sourceMappingURL=tree-view.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tree-view.d.ts","sourceRoot":"","sources":["../../src/tree-view/tree-view.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAGlD;;;;;;;;;;;;;;;GAeG;AACH,QAAA,MAAM,gBAAgB,GAAI,CAAC,EAAE,MAAM,WAAW,GAAG,KAAK,CAAC,CAAC,CAAC,yCACnB,CAAC;AAUvC,OAAO,CAAC,MAAM,CAAC;IACd,UAAU,qBAAqB;QAC9B,2BAA2B,EAAE,WAAW,GAAG,KAAK,GAAG,WAAW,CAAC;KAC/D;CACD;AAED,OAAO,EAAE,gBAAgB,EAAE,CAAC"}
@@ -0,0 +1,27 @@
1
+ import { component } from '@pionjs/pion';
2
+ import { render } from './render';
3
+ import styles from './styles';
4
+ import { useTreeView } from './use-tree-view';
5
+ /**
6
+ * A tree of nested rows, after the Untitled UI tree view.
7
+ *
8
+ * Clicking a row selects it; the chevron expands it. Double-click or Enter
9
+ * activates the row (`activate` event). Only expanded branches are
10
+ * flattened and the rows are virtualized, so very large trees stay fast.
11
+ *
12
+ * @fires selected-changed - `detail.value` is the selected id
13
+ * @fires expanded-changed - `detail.value` is the array of expanded ids
14
+ * @fires activate - `detail` is `{ id, node }`
15
+ * @csspart tree - the scrolling container (`role="tree"`)
16
+ * @csspart row - a row (`role="treeitem"`)
17
+ * @csspart content - the highlighted part of a row
18
+ * @csspart toggle - the chevron
19
+ * @csspart label - the row text
20
+ */
21
+ const TreenodeTreeView = (host) => render(host, useTreeView(host, host));
22
+ customElements.define('cosmoz-treenode-tree-view', component(TreenodeTreeView, {
23
+ styleSheets: [styles],
24
+ observedAttributes: ['size', 'label', 'selected'],
25
+ }));
26
+ export { TreenodeTreeView };
27
+ //# sourceMappingURL=tree-view.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tree-view.js","sourceRoot":"","sources":["../../src/tree-view/tree-view.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,MAAM,MAAM,UAAU,CAAC;AAE9B,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAE9C;;;;;;;;;;;;;;;GAeG;AACH,MAAM,gBAAgB,GAAG,CAAI,IAA4B,EAAE,EAAE,CAC5D,MAAM,CAAC,IAAI,EAAE,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;AAEvC,cAAc,CAAC,MAAM,CACpB,2BAA2B,EAC3B,SAAS,CAAC,gBAAgB,EAAE;IAC3B,WAAW,EAAE,CAAC,MAAM,CAAC;IACrB,kBAAkB,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,UAAU,CAAC;CACjD,CAAC,CACF,CAAC;AAQF,OAAO,EAAE,gBAAgB,EAAE,CAAC"}
@@ -0,0 +1,59 @@
1
+ import type { TemplateResult } from 'lit-html';
2
+ export type TreeViewSize = 'sm' | 'md';
3
+ /**
4
+ * How the tree reads a node. Each accessor has a default, so plain
5
+ * `{ id, label, children }` data works without any configuration.
6
+ */
7
+ export interface TreeViewAccessors<T> {
8
+ /** Stable, unique id of a node. Defaults to `node.id`. */
9
+ getId?: (node: T) => string;
10
+ /** Visible text of a node. Defaults to `node.label ?? node.name`. */
11
+ getLabel?: (node: T) => string;
12
+ /** Child nodes, or nothing for a leaf. Defaults to `node.children`. */
13
+ getChildren?: (node: T) => readonly T[] | null | undefined;
14
+ /** Whether a node can expand, including children not loaded yet. */
15
+ hasChildren?: (node: T) => boolean;
16
+ }
17
+ export interface TreeViewRowState {
18
+ expanded: boolean;
19
+ selected: boolean;
20
+ hasChildren: boolean;
21
+ level: number;
22
+ }
23
+ export interface Props<T = unknown> extends TreeViewAccessors<T> {
24
+ /** Root nodes. */
25
+ items?: readonly T[];
26
+ /** Id of the selected node. Two-way: emits `selected-changed`. */
27
+ selected?: string;
28
+ /** Ids of expanded nodes. Two-way: emits `expanded-changed`. */
29
+ expanded?: readonly string[];
30
+ size?: TreeViewSize;
31
+ /** Accessible name of the tree. */
32
+ label?: string;
33
+ /** Optional icon before the label. */
34
+ renderIcon?: (node: T, state: TreeViewRowState) => TemplateResult | unknown;
35
+ }
36
+ /** One visible row of the flattened tree. */
37
+ export interface Row<T> {
38
+ node: T;
39
+ id: string;
40
+ /** 1-based depth, as in `aria-level`. */
41
+ level: number;
42
+ /** 1-based position among its siblings, as in `aria-posinset`. */
43
+ posinset: number;
44
+ /** Number of siblings including itself, as in `aria-setsize`. */
45
+ setsize: number;
46
+ parentId?: string;
47
+ hasChildren: boolean;
48
+ expanded: boolean;
49
+ }
50
+ /** Methods on the internal tree view element. */
51
+ export interface TreeViewApi {
52
+ /** Moves focus to the active row (the tree's tab stop) and scrolls it into view. */
53
+ focusActiveItem(): void;
54
+ }
55
+ export interface ActivateEventDetail<T = unknown> {
56
+ id: string;
57
+ node: T;
58
+ }
59
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/tree-view/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE/C,MAAM,MAAM,YAAY,GAAG,IAAI,GAAG,IAAI,CAAC;AAEvC;;;GAGG;AACH,MAAM,WAAW,iBAAiB,CAAC,CAAC;IACnC,0DAA0D;IAC1D,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,CAAC;IAC5B,qEAAqE;IACrE,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,CAAC;IAC/B,uEAAuE;IACvE,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,SAAS,CAAC,EAAE,GAAG,IAAI,GAAG,SAAS,CAAC;IAC3D,oEAAoE;IACpE,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC;CACnC;AAED,MAAM,WAAW,gBAAgB;IAChC,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE,OAAO,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,KAAK,CAAC,CAAC,GAAG,OAAO,CAAE,SAAQ,iBAAiB,CAAC,CAAC,CAAC;IAC/D,kBAAkB;IAClB,KAAK,CAAC,EAAE,SAAS,CAAC,EAAE,CAAC;IACrB,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gEAAgE;IAChE,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7B,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,mCAAmC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,gBAAgB,KAAK,cAAc,GAAG,OAAO,CAAC;CAC5E;AAED,6CAA6C;AAC7C,MAAM,WAAW,GAAG,CAAC,CAAC;IACrB,IAAI,EAAE,CAAC,CAAC;IACR,EAAE,EAAE,MAAM,CAAC;IACX,yCAAyC;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,kEAAkE;IAClE,QAAQ,EAAE,MAAM,CAAC;IACjB,iEAAiE;IACjE,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,OAAO,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;CAClB;AAED,iDAAiD;AACjD,MAAM,WAAW,WAAW;IAC3B,oFAAoF;IACpF,eAAe,IAAI,IAAI,CAAC;CACxB;AAED,MAAM,WAAW,mBAAmB,CAAC,CAAC,GAAG,OAAO;IAC/C,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,CAAC,CAAC;CACR"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/tree-view/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,18 @@
1
+ import type { RangeChangedEvent } from '@lit-labs/virtualizer/events.js';
2
+ import { type VirtualizerHostElement } from '@lit-labs/virtualizer/virtualize.js';
3
+ import type { Props, Row } from './types';
4
+ export declare const useTreeView: <T>(host: HTMLElement, props: Props<T>) => {
5
+ rows: Row<T>[];
6
+ selected: string | undefined;
7
+ tabStopId: string;
8
+ scrollerRef: import("@pionjs/pion").Ref<VirtualizerHostElement | undefined>;
9
+ toggle: (id: string, open?: boolean) => void;
10
+ select: (row: Row<T>) => void;
11
+ activate: (row: Row<T>) => void;
12
+ onKeyDown: (e: KeyboardEvent) => void;
13
+ treeTabIndex: number;
14
+ focusActiveItem: () => void;
15
+ onRowFocus: (row: Row<T>) => void;
16
+ onRangeChanged: ({ first, last }: RangeChangedEvent) => void;
17
+ };
18
+ //# sourceMappingURL=use-tree-view.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-tree-view.d.ts","sourceRoot":"","sources":["../../src/tree-view/use-tree-view.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACzE,OAAO,EAEN,KAAK,sBAAsB,EAC3B,MAAM,qCAAqC,CAAC;AAW7C,OAAO,KAAK,EAAuB,KAAK,EAAE,GAAG,EAAE,MAAM,SAAS,CAAC;AAgJ/D,eAAO,MAAM,WAAW,GAAI,CAAC,EAAE,MAAM,WAAW,EAAE,OAAO,KAAK,CAAC,CAAC,CAAC;;;;;iBAO5C,MAAM,SAAS,OAAO;kBAkBnC,GAAG,CAAC,CAAC,CAAC;oBASN,GAAG,CAAC,CAAC,CAAC;mBAuBS,aAAa;;;sBAyBhB,GAAG,CAAC,CAAC,CAAC;sCACU,iBAAiB;CAGpD,CAAC"}
@@ -0,0 +1,175 @@
1
+ import { virtualizerRef, } from '@lit-labs/virtualizer/virtualize.js';
2
+ import { useImperativeApi } from '@neovici/cosmoz-utils/hooks/use-imperative-api';
3
+ import { useCallback, useEffect, useMemo, useProperty, useRef, useState, } from '@pionjs/pion';
4
+ import { flatten, keyAction, toggleId } from './flatten';
5
+ const NO_IDS = [];
6
+ const rowSelector = (id) => `[role='treeitem'][data-id='${CSS.escape(id)}']`;
7
+ /**
8
+ * Brings the row at `index` into view and, once the virtualizer has
9
+ * rendered it, gives it focus. Rows outside the viewport do not exist in
10
+ * the DOM, so the focus has to wait for the scroll.
11
+ */
12
+ const revealRow = (scroller, index, id, { focus = true, block = 'nearest' } = {}) => {
13
+ if (!scroller)
14
+ return;
15
+ scroller[virtualizerRef]?.element(index)?.scrollIntoView({ block });
16
+ if (!focus)
17
+ return;
18
+ let frames = 0;
19
+ const tryFocus = () => {
20
+ const el = scroller.querySelector(rowSelector(id));
21
+ if (el) {
22
+ el.focus({ preventScroll: true });
23
+ return;
24
+ }
25
+ if (++frames < 10)
26
+ requestAnimationFrame(tryFocus);
27
+ };
28
+ tryFocus();
29
+ };
30
+ /**
31
+ * Resolves once the virtualizer has laid out its rows. On first render it
32
+ * has no layout yet, and scrolling to an index then would throw. The
33
+ * timeout covers an idle virtualizer, whose promise only settles after the
34
+ * next layout pass.
35
+ */
36
+ const whenLaidOut = async (scroller) => {
37
+ await new Promise(requestAnimationFrame);
38
+ const virtualizer = scroller?.[virtualizerRef];
39
+ if (!virtualizer)
40
+ return;
41
+ await Promise.race([
42
+ virtualizer.layoutComplete.catch(() => undefined),
43
+ new Promise((resolve) => setTimeout(resolve, 500)),
44
+ ]);
45
+ };
46
+ /** The row holding the tab stop: the active one, else the selected, else the first. */
47
+ const pickTabStop = (rows, indexOf, ...candidates) => candidates.find((id) => id != null && indexOf.has(id)) ?? rows[0]?.id;
48
+ const useRows = ({ items, getId, getChildren, hasChildren }) => {
49
+ const [expandedProp, setExpanded] = useProperty('expanded', () => []);
50
+ const expanded = expandedProp ?? NO_IDS;
51
+ const expandedSet = useMemo(() => new Set(expanded), [expanded]);
52
+ const rows = useMemo(() => flatten(items, expandedSet, { getId, getChildren, hasChildren }), [items, expandedSet, getId, getChildren, hasChildren]);
53
+ const indexOf = useMemo(() => new Map(rows.map((row, i) => [row.id, i])), [rows]);
54
+ const toggle = useCallback((id, open) => setExpanded((current) => toggleId(current ?? NO_IDS, id, open)), []);
55
+ return { rows, indexOf, toggle };
56
+ };
57
+ /**
58
+ * Keep an external selection visible as loaded siblings change its row index.
59
+ * Browsing another branch suppresses automatic scrolling. Clicking a row
60
+ * records its position without scrolling or moving focus.
61
+ */
62
+ const useRevealSelection = (selected, activeId, indexOf, scrollerRef) => {
63
+ const revealed = useRef(undefined);
64
+ useEffect(() => {
65
+ if (selected == null) {
66
+ revealed.current = undefined;
67
+ return;
68
+ }
69
+ const index = indexOf.get(selected);
70
+ if (index == null ||
71
+ activeId !== selected ||
72
+ (revealed.current?.id === selected && revealed.current.index === index)) {
73
+ return;
74
+ }
75
+ const controller = new AbortController();
76
+ whenLaidOut(scrollerRef.current).then(() => {
77
+ if (controller.signal.aborted)
78
+ return;
79
+ revealRow(scrollerRef.current, index, selected, {
80
+ focus: false,
81
+ block: 'center',
82
+ });
83
+ revealed.current = { id: selected, index };
84
+ });
85
+ return () => controller.abort();
86
+ }, [selected, activeId, indexOf]);
87
+ return revealed;
88
+ };
89
+ const handleKey = (e, ctx) => {
90
+ if (e.defaultPrevented || e.altKey || e.ctrlKey || e.metaKey)
91
+ return;
92
+ const row = ctx.rows[ctx.index];
93
+ if (row && (e.key === 'Enter' || e.key === ' ')) {
94
+ e.preventDefault();
95
+ (e.key === 'Enter' ? ctx.activate : ctx.select)(row);
96
+ return;
97
+ }
98
+ const action = keyAction(ctx.rows, ctx.index, e.key);
99
+ if (!action)
100
+ return;
101
+ e.preventDefault();
102
+ if (action.type === 'focus')
103
+ ctx.focus(action.index);
104
+ else
105
+ ctx.toggle(action.id, action.type === 'expand');
106
+ };
107
+ export const useTreeView = (host, props) => {
108
+ const [selected, setSelected] = useProperty('selected');
109
+ const [activeId, setActiveId] = useState(undefined);
110
+ const [range, setRange] = useState({ first: -1, last: -1 });
111
+ useEffect(() => setActiveId(selected), [selected]);
112
+ const scrollerRef = useRef(undefined);
113
+ const { rows, indexOf, toggle: toggleExpanded } = useRows(props);
114
+ const toggle = (id, open) => {
115
+ setActiveId(id);
116
+ toggleExpanded(id, open);
117
+ };
118
+ const revealed = useRevealSelection(selected, activeId, indexOf, scrollerRef);
119
+ const tabStopId = pickTabStop(rows, indexOf, activeId, selected);
120
+ const tabStopIndex = indexOf.get(tabStopId) ?? -1;
121
+ const treeTabIndex = rows.length && (tabStopIndex < range.first || tabStopIndex > range.last)
122
+ ? 0
123
+ : -1;
124
+ const focusActiveItem = () => {
125
+ if (tabStopIndex >= 0) {
126
+ revealRow(scrollerRef.current, tabStopIndex, tabStopId);
127
+ }
128
+ };
129
+ const select = useCallback((row) => {
130
+ revealed.current = { id: row.id, index: indexOf.get(row.id) ?? -1 };
131
+ setActiveId(row.id);
132
+ setSelected(row.id);
133
+ }, [indexOf]);
134
+ const activate = useCallback((row) => {
135
+ select(row);
136
+ host.dispatchEvent(new CustomEvent('activate', {
137
+ detail: { id: row.id, node: row.node },
138
+ bubbles: true,
139
+ composed: true,
140
+ }));
141
+ }, [select]);
142
+ // `focusActiveItem()` moves focus to the tab-stop row, as tabbing into
143
+ // the tree would. Native `focus()` is left alone: frameworks and test
144
+ // tools call it on the host and must not have focus redirected.
145
+ useImperativeApi({
146
+ focusActiveItem,
147
+ }, [indexOf, tabStopId]);
148
+ const onKeyDown = (e) => handleKey(e, {
149
+ rows,
150
+ index: indexOf.get(tabStopId) ?? -1,
151
+ select,
152
+ activate,
153
+ toggle,
154
+ focus: (index) => {
155
+ const target = rows[index];
156
+ setActiveId(target.id);
157
+ revealRow(scrollerRef.current, index, target.id);
158
+ },
159
+ });
160
+ return {
161
+ rows,
162
+ selected,
163
+ tabStopId,
164
+ scrollerRef,
165
+ toggle,
166
+ select,
167
+ activate,
168
+ onKeyDown,
169
+ treeTabIndex,
170
+ focusActiveItem,
171
+ onRowFocus: (row) => setActiveId(row.id),
172
+ onRangeChanged: ({ first, last }) => setRange({ first, last }),
173
+ };
174
+ };
175
+ //# sourceMappingURL=use-tree-view.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-tree-view.js","sourceRoot":"","sources":["../../src/tree-view/use-tree-view.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import type { Node } from '@neovici/cosmoz-tree';
2
+ import type { NodeSource } from './source';
3
+ export declare const forest: (source: NodeSource, nodes: readonly Node[]) => {
4
+ roots: Node[];
5
+ getChildren: (node: Node) => Node[];
6
+ expanded: string[];
7
+ };
8
+ /** Loaded levels and the ancestor chains connecting them. */
9
+ export declare const sourceTree: (source: NodeSource) => {
10
+ subscribe: (listener: () => void) => () => void;
11
+ roots: () => Node[];
12
+ getChildren: (node: Node) => Node[];
13
+ hasChildren: (node: Node) => boolean;
14
+ pending: Map<string, Promise<void>>;
15
+ errors: Set<string>;
16
+ loadPath: (path: string) => Promise<Node[]>;
17
+ loadLevel: (path: string) => Promise<void>;
18
+ };
19
+ /** Ancestors are inserted in match order, preserving the source's ranking. */
20
+ export declare const searchTree: (source: NodeSource, query: string) => Promise<{
21
+ matches: number;
22
+ roots: Node[];
23
+ getChildren: (node: Node) => Node[];
24
+ expanded: string[];
25
+ }>;
26
+ //# sourceMappingURL=source-tree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"source-tree.d.ts","sourceRoot":"","sources":["../../src/util/source-tree.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAE3C,eAAO,MAAM,MAAM,GAAI,QAAQ,UAAU,EAAE,OAAO,SAAS,IAAI,EAAE;;wBAgB3C,IAAI;;CAGzB,CAAC;AAEF,6DAA6D;AAC7D,eAAO,MAAM,UAAU,GAAI,QAAQ,UAAU;0BAarB,MAAM,IAAI;;wBAOZ,IAAI;wBAEJ,IAAI;;;qBAMD,MAAM;sBAMX,MAAM;CAyBzB,CAAC;AAEF,8EAA8E;AAC9E,eAAO,MAAM,UAAU,GAAU,QAAQ,UAAU,EAAE,OAAO,MAAM;;;wBApE5C,IAAI;;EAgFzB,CAAC"}
@@ -0,0 +1,91 @@
1
+ export const forest = (source, nodes) => {
2
+ const byPath = new Map(nodes.map((node) => [node.pathLocator, node]));
3
+ const children = new Map();
4
+ for (const node of byPath.values()) {
5
+ const parent = source.parentOf(node);
6
+ if (parent === node.pathLocator)
7
+ continue;
8
+ const siblings = children.get(parent) ?? [];
9
+ siblings.push(node);
10
+ children.set(parent, siblings);
11
+ }
12
+ return {
13
+ roots: [...byPath.values()].filter((node) => source.parentOf(node) === node.pathLocator ||
14
+ !byPath.has(source.parentOf(node))),
15
+ getChildren: (node) => children.get(node.pathLocator) ?? [],
16
+ expanded: [...children.keys()].filter((path) => byPath.has(path)),
17
+ };
18
+ };
19
+ /** Loaded levels and the ancestor chains connecting them. */
20
+ export const sourceTree = (source) => {
21
+ const nodes = new Map();
22
+ let hierarchy = forest(source, []);
23
+ const levels = new Map();
24
+ const pending = new Map();
25
+ const errors = new Set();
26
+ const listeners = new Set();
27
+ const notify = () => listeners.forEach((listener) => listener());
28
+ const absorb = (items) => {
29
+ for (const node of items)
30
+ nodes.set(node.pathLocator, node);
31
+ hierarchy = forest(source, [...nodes.values()]);
32
+ };
33
+ return {
34
+ subscribe: (listener) => {
35
+ listeners.add(listener);
36
+ return () => {
37
+ listeners.delete(listener);
38
+ };
39
+ },
40
+ roots: () => hierarchy.roots,
41
+ getChildren: (node) => levels.get(node.pathLocator) ?? hierarchy.getChildren(node),
42
+ hasChildren: (node) => levels.has(node.pathLocator)
43
+ ? levels.get(node.pathLocator).length > 0
44
+ : source.hasChildren(node) !== false,
45
+ pending,
46
+ errors,
47
+ loadPath: async (path) => {
48
+ const items = await source.getPath(path);
49
+ absorb(items);
50
+ notify();
51
+ return items;
52
+ },
53
+ loadLevel: (path) => {
54
+ if (levels.has(path))
55
+ return Promise.resolve();
56
+ const current = pending.get(path);
57
+ if (current)
58
+ return current;
59
+ errors.delete(path);
60
+ const request = Promise.resolve()
61
+ .then(() => source.getLevel(path))
62
+ .then((items) => {
63
+ levels.set(path, items);
64
+ absorb(items);
65
+ }, () => {
66
+ errors.add(path);
67
+ })
68
+ .finally(() => {
69
+ pending.delete(path);
70
+ notify();
71
+ });
72
+ pending.set(path, request);
73
+ notify();
74
+ return request;
75
+ },
76
+ };
77
+ };
78
+ /** Ancestors are inserted in match order, preserving the source's ranking. */
79
+ export const searchTree = async (source, query) => {
80
+ const matches = await source.search(query, '');
81
+ const paths = await Promise.all(matches.map(async (node) => {
82
+ try {
83
+ return [...(await source.getPath(node.pathLocator)), node];
84
+ }
85
+ catch {
86
+ return [node];
87
+ }
88
+ }));
89
+ return { ...forest(source, paths.flat()), matches: matches.length };
90
+ };
91
+ //# sourceMappingURL=source-tree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"source-tree.js","sourceRoot":"","sources":["../../src/util/source-tree.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"source.d.ts","sourceRoot":"","sources":["../../src/util/source.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAGvD;;;;;;;GAOG;AACH,MAAM,WAAW,UAAU;IAC1B,oEAAoE;IACpE,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IAExD,mDAAmD;IACnD,OAAO,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IAEvD;;;OAGG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IAE/D,gFAAgF;IAChF,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,GAAG,SAAS,CAAC;IAE7C,KAAK,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAAC;IAE1B,2EAA2E;IAC3E,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAEnD,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAAC;IAE7B,yDAAyD;IACzD,YAAY,EAAE,OAAO,CAAC;CACtB;AAED;;;GAGG;AACH,eAAO,MAAM,UAAU,GAAI,MAAM,IAAI,KAAG,UAiBtC,CAAC"}
1
+ {"version":3,"file":"source.d.ts","sourceRoot":"","sources":["../../src/util/source.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAGvD;;;;;;;GAOG;AACH,MAAM,WAAW,UAAU;IAC1B,oEAAoE;IACpE,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IAExD,mDAAmD;IACnD,OAAO,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IAEvD;;;OAGG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IAE/D,gFAAgF;IAChF,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,GAAG,SAAS,CAAC;IAE7C,KAAK,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAAC;IAE1B,2EAA2E;IAC3E,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAEnD,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAAC;IAE7B,yDAAyD;IACzD,YAAY,EAAE,OAAO,CAAC;CACtB;AAED;;;GAGG;AACH,eAAO,MAAM,UAAU,GAAI,MAAM,IAAI,KAAG,UAoBtC,CAAC"}
@@ -4,7 +4,10 @@ import { computeDataPlane, getParentPath, getTreePathParts } from './helpers';
4
4
  * took a source at all.
5
5
  */
6
6
  export const treeSource = (tree) => ({
7
- getLevel: (pathLocator) => computeDataPlane(tree, '', pathLocator),
7
+ getLevel: (pathLocator) => {
8
+ const nodes = computeDataPlane(tree, '', pathLocator);
9
+ return Array.isArray(nodes) ? nodes : [];
10
+ },
8
11
  getPath: (pathLocator) => getTreePathParts(pathLocator, tree),
9
12
  search: (query, scope) => computeDataPlane(tree, query, scope),
10
13
  hasChildren: (node) => tree?.hasChildren(node),
@@ -1 +1 @@
1
- {"version":3,"file":"source.js","sourceRoot":"","sources":["../../src/util/source.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAqC9E;;;GAGG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,IAAU,EAAc,EAAE,CAAC,CAAC;IACtD,QAAQ,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,gBAAgB,CAAC,IAAI,EAAE,EAAE,EAAE,WAAW,CAAC;IAElE,OAAO,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,gBAAgB,CAAC,WAAW,EAAE,IAAI,CAAC;IAE7D,MAAM,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,gBAAgB,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC;IAE9D,WAAW,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,WAAW,CAAC,IAAI,CAAC;IAE9C,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAE,IAAI,CAAC,IAAI,CAAC,cAAc,CAAY,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,EAAE;IAE1E,SAAS,EAAE,CAAC,WAAW,EAAE,EAAE,CAC1B,IAAI,EAAE,aAAa,CAAC,WAAW,EAAE,IAAI,CAAC,cAAc,CAAC;IAEtD,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAE3D,YAAY,EAAE,IAAI;CAClB,CAAC,CAAC"}
1
+ {"version":3,"file":"source.js","sourceRoot":"","sources":["../../src/util/source.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAqC9E;;;GAGG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,IAAU,EAAc,EAAE,CAAC,CAAC;IACtD,QAAQ,EAAE,CAAC,WAAW,EAAE,EAAE;QACzB,MAAM,KAAK,GAAG,gBAAgB,CAAC,IAAI,EAAE,EAAE,EAAE,WAAW,CAAC,CAAC;QACtD,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;IAC1C,CAAC;IAED,OAAO,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,gBAAgB,CAAC,WAAW,EAAE,IAAI,CAAC;IAE7D,MAAM,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,gBAAgB,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC;IAE9D,WAAW,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,WAAW,CAAC,IAAI,CAAC;IAE9C,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAE,IAAI,CAAC,IAAI,CAAC,cAAc,CAAY,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,EAAE;IAE1E,SAAS,EAAE,CAAC,WAAW,EAAE,EAAE,CAC1B,IAAI,EAAE,aAAa,CAAC,WAAW,EAAE,IAAI,CAAC,cAAc,CAAC;IAEtD,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAE3D,YAAY,EAAE,IAAI;CAClB,CAAC,CAAC"}