@dbcdk/react-components 0.0.181 → 0.0.183

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 (56) hide show
  1. package/dist/client.cjs +21 -0
  2. package/dist/client.d.ts +3 -0
  3. package/dist/client.js +3 -0
  4. package/dist/components/clear-button/ClearButton.cjs +35 -12
  5. package/dist/components/clear-button/ClearButton.d.ts +17 -1
  6. package/dist/components/clear-button/ClearButton.js +35 -12
  7. package/dist/components/clear-button/ClearButton.module.css +12 -0
  8. package/dist/components/forms/multi-select/MultiSelect.cjs +2 -1
  9. package/dist/components/forms/multi-select/MultiSelect.js +2 -1
  10. package/dist/components/forms/select/Select.cjs +3 -1
  11. package/dist/components/forms/select/Select.js +3 -1
  12. package/dist/components/interval-select/IntervalSelect.cjs +2 -1
  13. package/dist/components/interval-select/IntervalSelect.js +2 -1
  14. package/dist/components/json-viewer/JsonViewer.cjs +3 -2
  15. package/dist/components/json-viewer/JsonViewer.d.ts +1 -1
  16. package/dist/components/json-viewer/JsonViewer.js +3 -2
  17. package/dist/components/json-viewer/JsonViewer.module.css +7 -0
  18. package/dist/components/json-viewer/types.d.ts +6 -0
  19. package/dist/components/line-viewer/LineViewer.cjs +174 -0
  20. package/dist/components/line-viewer/LineViewer.d.ts +20 -0
  21. package/dist/components/line-viewer/LineViewer.js +168 -0
  22. package/dist/components/line-viewer/LineViewer.module.css +112 -0
  23. package/dist/components/line-viewer/tokenizeLineFormat.cjs +52 -0
  24. package/dist/components/line-viewer/tokenizeLineFormat.d.ts +14 -0
  25. package/dist/components/line-viewer/tokenizeLineFormat.js +50 -0
  26. package/dist/components/tree-view/TreeNodeRow.cjs +99 -0
  27. package/dist/components/tree-view/TreeNodeRow.d.ts +18 -0
  28. package/dist/components/tree-view/TreeNodeRow.js +93 -0
  29. package/dist/components/tree-view/TreeView.cjs +93 -0
  30. package/dist/components/tree-view/TreeView.d.ts +20 -0
  31. package/dist/components/tree-view/TreeView.js +79 -0
  32. package/dist/components/tree-view/TreeView.module.css +141 -0
  33. package/dist/components/tree-view/types.cjs +2 -0
  34. package/dist/components/tree-view/types.d.ts +44 -0
  35. package/dist/components/tree-view/types.js +1 -0
  36. package/dist/components/tree-view/useTreeExpansion.cjs +47 -0
  37. package/dist/components/tree-view/useTreeExpansion.d.ts +17 -0
  38. package/dist/components/tree-view/useTreeExpansion.js +45 -0
  39. package/dist/components/tree-view/utils.cjs +32 -0
  40. package/dist/components/tree-view/utils.d.ts +16 -0
  41. package/dist/components/tree-view/utils.js +29 -0
  42. package/dist/components/xml-viewer/XmlViewer.cjs +175 -0
  43. package/dist/components/xml-viewer/XmlViewer.d.ts +20 -0
  44. package/dist/components/xml-viewer/XmlViewer.js +169 -0
  45. package/dist/components/xml-viewer/XmlViewer.module.css +109 -0
  46. package/dist/components/xml-viewer/tokenizeXml.cjs +50 -0
  47. package/dist/components/xml-viewer/tokenizeXml.d.ts +12 -0
  48. package/dist/components/xml-viewer/tokenizeXml.js +48 -0
  49. package/dist/index.cjs +7 -0
  50. package/dist/index.css +3 -0
  51. package/dist/index.d.ts +1 -0
  52. package/dist/index.js +1 -0
  53. package/dist/utils/date/formatDate.cjs +29 -17
  54. package/dist/utils/date/formatDate.d.ts +7 -0
  55. package/dist/utils/date/formatDate.js +29 -17
  56. package/package.json +1 -1
@@ -0,0 +1,93 @@
1
+ 'use client';
2
+ 'use strict';
3
+
4
+ var jsxRuntime = require('react/jsx-runtime');
5
+ var react = require('react');
6
+ var TreeNodeRow = require('./TreeNodeRow');
7
+ var styles = require('./TreeView.module.css');
8
+ var useTreeExpansion = require('./useTreeExpansion');
9
+ var utils = require('./utils');
10
+
11
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
12
+
13
+ var styles__default = /*#__PURE__*/_interopDefault(styles);
14
+
15
+ function TreeView({
16
+ data,
17
+ loadChildren,
18
+ defaultExpandedIds,
19
+ expandedIds,
20
+ onExpandedIdsChange,
21
+ selectedId,
22
+ onSelectNode,
23
+ renderLabel,
24
+ emptyMessage = "Intet at vise.",
25
+ loadErrorMessage = () => "Der skete en fejl under indl\xE6sning.",
26
+ className,
27
+ ...rest
28
+ }) {
29
+ const { isExpanded, toggle, expand } = useTreeExpansion.useTreeExpansion(
30
+ defaultExpandedIds,
31
+ expandedIds,
32
+ onExpandedIdsChange
33
+ );
34
+ const [resolvedChildren, setResolvedChildren] = react.useState({});
35
+ const [loadingIds, setLoadingIds] = react.useState(/* @__PURE__ */ new Set());
36
+ const [errorIds, setErrorIds] = react.useState({});
37
+ const requestLoad = react.useCallback(
38
+ (node) => {
39
+ if (!loadChildren || loadingIds.has(node.id)) return;
40
+ setLoadingIds((prev) => new Set(prev).add(node.id));
41
+ setErrorIds((prev) => {
42
+ if (!(node.id in prev)) return prev;
43
+ const next = { ...prev };
44
+ delete next[node.id];
45
+ return next;
46
+ });
47
+ loadChildren(node).then((children) => {
48
+ setResolvedChildren((prev) => ({ ...prev, [node.id]: children }));
49
+ expand(node.id);
50
+ }).catch((err) => {
51
+ setErrorIds((prev) => ({ ...prev, [node.id]: loadErrorMessage(err, node) }));
52
+ }).finally(() => {
53
+ setLoadingIds((prev) => {
54
+ const next = new Set(prev);
55
+ next.delete(node.id);
56
+ return next;
57
+ });
58
+ });
59
+ },
60
+ [loadChildren, loadingIds, expand, loadErrorMessage]
61
+ );
62
+ if (data.length === 0) {
63
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: [styles__default.default.emptyState, className].filter(Boolean).join(" "), ...rest, children: emptyMessage });
64
+ }
65
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "tree", className: [styles__default.default.tree, className].filter(Boolean).join(" "), ...rest, children: data.map((node) => /* @__PURE__ */ jsxRuntime.jsx(
66
+ TreeNodeRow.TreeNodeRow,
67
+ {
68
+ node,
69
+ depth: 0,
70
+ isExpanded,
71
+ toggle,
72
+ resolvedChildren,
73
+ loadingIds,
74
+ errorIds,
75
+ requestLoad,
76
+ loadChildren,
77
+ selectedId,
78
+ onSelectNode,
79
+ renderLabel
80
+ },
81
+ node.id
82
+ )) });
83
+ }
84
+
85
+ Object.defineProperty(exports, "collectExpandableIds", {
86
+ enumerable: true,
87
+ get: function () { return utils.collectExpandableIds; }
88
+ });
89
+ Object.defineProperty(exports, "findAncestorIds", {
90
+ enumerable: true,
91
+ get: function () { return utils.findAncestorIds; }
92
+ });
93
+ exports.TreeView = TreeView;
@@ -0,0 +1,20 @@
1
+ import type { JSX } from 'react';
2
+ import type { TreeViewProps } from './types';
3
+ export type { LoadTreeChildren, TreeNode, TreeViewProps } from './types';
4
+ export { collectExpandableIds, findAncestorIds } from './utils';
5
+ /**
6
+ * A single component for both shapes of tree data:
7
+ * - Eager: every node's full subtree is already in `data` via `children`.
8
+ * No `loadChildren` needed - toggling a node only ever flips its
9
+ * expanded/collapsed state.
10
+ * - Lazy: `data` only has to contain what you already know; pass
11
+ * `loadChildren` and a node whose `children` is undefined gets fetched
12
+ * the first time it's expanded (see TreeNode's docs on `hasChildren` for
13
+ * how a not-yet-fetched node still shows a toggle).
14
+ *
15
+ * Expand/collapse state follows the same model as the Sidebar: a single
16
+ * `Set<string>` of expanded ids is the source of truth (see
17
+ * useTreeExpansion), and it can be left uncontrolled or owned by the
18
+ * caller via `expandedIds`/`onExpandedIdsChange`.
19
+ */
20
+ export declare function TreeView<T = unknown>({ data, loadChildren, defaultExpandedIds, expandedIds, onExpandedIdsChange, selectedId, onSelectNode, renderLabel, emptyMessage, loadErrorMessage, className, ...rest }: TreeViewProps<T>): JSX.Element;
@@ -0,0 +1,79 @@
1
+ 'use client';
2
+ import { jsx } from 'react/jsx-runtime';
3
+ import { useState, useCallback } from 'react';
4
+ import { TreeNodeRow } from './TreeNodeRow';
5
+ import styles from './TreeView.module.css';
6
+ import { useTreeExpansion } from './useTreeExpansion';
7
+ export { collectExpandableIds, findAncestorIds } from './utils';
8
+
9
+ function TreeView({
10
+ data,
11
+ loadChildren,
12
+ defaultExpandedIds,
13
+ expandedIds,
14
+ onExpandedIdsChange,
15
+ selectedId,
16
+ onSelectNode,
17
+ renderLabel,
18
+ emptyMessage = "Intet at vise.",
19
+ loadErrorMessage = () => "Der skete en fejl under indl\xE6sning.",
20
+ className,
21
+ ...rest
22
+ }) {
23
+ const { isExpanded, toggle, expand } = useTreeExpansion(
24
+ defaultExpandedIds,
25
+ expandedIds,
26
+ onExpandedIdsChange
27
+ );
28
+ const [resolvedChildren, setResolvedChildren] = useState({});
29
+ const [loadingIds, setLoadingIds] = useState(/* @__PURE__ */ new Set());
30
+ const [errorIds, setErrorIds] = useState({});
31
+ const requestLoad = useCallback(
32
+ (node) => {
33
+ if (!loadChildren || loadingIds.has(node.id)) return;
34
+ setLoadingIds((prev) => new Set(prev).add(node.id));
35
+ setErrorIds((prev) => {
36
+ if (!(node.id in prev)) return prev;
37
+ const next = { ...prev };
38
+ delete next[node.id];
39
+ return next;
40
+ });
41
+ loadChildren(node).then((children) => {
42
+ setResolvedChildren((prev) => ({ ...prev, [node.id]: children }));
43
+ expand(node.id);
44
+ }).catch((err) => {
45
+ setErrorIds((prev) => ({ ...prev, [node.id]: loadErrorMessage(err, node) }));
46
+ }).finally(() => {
47
+ setLoadingIds((prev) => {
48
+ const next = new Set(prev);
49
+ next.delete(node.id);
50
+ return next;
51
+ });
52
+ });
53
+ },
54
+ [loadChildren, loadingIds, expand, loadErrorMessage]
55
+ );
56
+ if (data.length === 0) {
57
+ return /* @__PURE__ */ jsx("div", { className: [styles.emptyState, className].filter(Boolean).join(" "), ...rest, children: emptyMessage });
58
+ }
59
+ return /* @__PURE__ */ jsx("div", { role: "tree", className: [styles.tree, className].filter(Boolean).join(" "), ...rest, children: data.map((node) => /* @__PURE__ */ jsx(
60
+ TreeNodeRow,
61
+ {
62
+ node,
63
+ depth: 0,
64
+ isExpanded,
65
+ toggle,
66
+ resolvedChildren,
67
+ loadingIds,
68
+ errorIds,
69
+ requestLoad,
70
+ loadChildren,
71
+ selectedId,
72
+ onSelectNode,
73
+ renderLabel
74
+ },
75
+ node.id
76
+ )) });
77
+ }
78
+
79
+ export { TreeView };
@@ -0,0 +1,141 @@
1
+ .tree {
2
+ display: flex;
3
+ flex-direction: column;
4
+ gap: 1px;
5
+ font-family: inherit;
6
+ font-size: var(--font-size-sm);
7
+ color: var(--color-fg-default);
8
+ }
9
+
10
+ .children {
11
+ display: flex;
12
+ flex-direction: column;
13
+ gap: 1px;
14
+ }
15
+
16
+ .row {
17
+ display: flex;
18
+ align-items: center;
19
+ min-inline-size: 0;
20
+ padding-left: calc(var(--tree-depth) * var(--spacing-lg));
21
+ border-radius: var(--border-radius-default);
22
+ }
23
+
24
+ .row[data-selected='true'] {
25
+ background: color-mix(in oklab, var(--color-brand) 16%, transparent);
26
+ }
27
+
28
+ .toggle,
29
+ .labelButton {
30
+ display: inline-flex;
31
+ align-items: center;
32
+ gap: var(--spacing-xs);
33
+ min-inline-size: 0;
34
+ padding-block: var(--spacing-2xs);
35
+ background: transparent;
36
+ border: 0;
37
+ border-radius: var(--border-radius-default);
38
+ color: inherit;
39
+ font: inherit;
40
+ text-align: left;
41
+ cursor: pointer;
42
+ }
43
+
44
+ .toggle {
45
+ flex: 0 0 auto;
46
+ padding-inline: var(--spacing-2xs);
47
+ color: var(--color-fg-subtle);
48
+ }
49
+
50
+ .toggle:disabled {
51
+ cursor: default;
52
+ }
53
+
54
+ .toggle svg {
55
+ inline-size: var(--icon-size-sm);
56
+ block-size: var(--icon-size-sm);
57
+ }
58
+
59
+ .leafSpacer {
60
+ display: inline-block;
61
+ inline-size: var(--icon-size-sm);
62
+ block-size: var(--icon-size-sm);
63
+ }
64
+
65
+ .toggleUnresolved {
66
+ display: inline-flex;
67
+ opacity: 0.45;
68
+ }
69
+
70
+ .spinner {
71
+ display: inline-flex;
72
+ }
73
+
74
+ .spinner svg {
75
+ inline-size: var(--icon-size-sm);
76
+ block-size: var(--icon-size-sm);
77
+ }
78
+
79
+ .labelButton {
80
+ flex: 1 1 auto;
81
+ min-inline-size: 0;
82
+ padding-inline: var(--spacing-xs);
83
+ }
84
+
85
+ .row:hover .toggle:not(:disabled),
86
+ .row:hover .labelButton,
87
+ .toggle:focus-visible,
88
+ .labelButton:focus-visible {
89
+ background: var(--color-bg-hover-subtle);
90
+ }
91
+
92
+ .toggle:focus-visible,
93
+ .labelButton:focus-visible {
94
+ outline: none;
95
+ box-shadow: var(--focus-ring);
96
+ }
97
+
98
+ .icon {
99
+ display: inline-flex;
100
+ flex: 0 0 auto;
101
+ color: var(--color-fg-subtle);
102
+ }
103
+
104
+ .icon svg {
105
+ inline-size: var(--icon-size-sm);
106
+ block-size: var(--icon-size-sm);
107
+ }
108
+
109
+ .label {
110
+ min-inline-size: 0;
111
+ overflow: hidden;
112
+ text-overflow: ellipsis;
113
+ white-space: nowrap;
114
+ }
115
+
116
+ .errorRow {
117
+ display: flex;
118
+ align-items: center;
119
+ gap: var(--spacing-xs);
120
+ padding-left: calc((var(--tree-depth) * var(--spacing-lg)) + var(--spacing-lg));
121
+ padding-block: var(--spacing-2xs);
122
+ font-size: var(--font-size-xs);
123
+ color: var(--color-status-error-fg);
124
+ }
125
+
126
+ .retryButton {
127
+ padding: 0;
128
+ background: transparent;
129
+ border: 0;
130
+ color: var(--color-link);
131
+ font: inherit;
132
+ font-size: inherit;
133
+ text-decoration: underline;
134
+ cursor: pointer;
135
+ }
136
+
137
+ .emptyState {
138
+ padding: var(--spacing-md);
139
+ color: var(--color-fg-muted);
140
+ text-align: center;
141
+ }
@@ -0,0 +1,2 @@
1
+ 'use strict';
2
+
@@ -0,0 +1,44 @@
1
+ import type { ReactNode } from 'react';
2
+ export type TreeNode<T = unknown> = {
3
+ id: string;
4
+ label: ReactNode;
5
+ icon?: ReactNode;
6
+ data?: T;
7
+ /**
8
+ * Present (even as an empty array) means this node's children are
9
+ * already known - eager mode relies entirely on this and never calls
10
+ * `loadChildren`. Leave it undefined for a node whose children haven't
11
+ * been fetched yet (lazy mode).
12
+ */
13
+ children?: TreeNode<T>[];
14
+ /**
15
+ * Only consulted while `children` is undefined and `loadChildren` is
16
+ * given: says whether this node is expected to have children at all.
17
+ * Defaults to true (assume expandable) so a node the caller hasn't
18
+ * inspected yet still shows a toggle; set it to false for a node you
19
+ * already know is a leaf, to skip showing a toggle for it entirely.
20
+ */
21
+ hasChildren?: boolean;
22
+ };
23
+ /** Fetches one node's children. Called once per node, the first time it's expanded (and again on retry after a failed load). Presence of this prop on TreeView is what switches it into lazy mode. */
24
+ export type LoadTreeChildren<T = unknown> = (node: TreeNode<T>) => Promise<TreeNode<T>[]>;
25
+ export type TreeViewProps<T = unknown> = {
26
+ /** Top-level nodes. In eager mode every node's full subtree must already be present via `children`. In lazy mode only the nodes you already know about need to be here - deeper levels arrive via `loadChildren`. */
27
+ data: TreeNode<T>[];
28
+ /** Omit for eager mode. Provide for lazy mode. */
29
+ loadChildren?: LoadTreeChildren<T>;
30
+ /** Uncontrolled initial expanded ids. Ignored if `expandedIds` is given. */
31
+ defaultExpandedIds?: string[];
32
+ /** Controlled expanded ids - pass together with `onExpandedIdsChange` to own the expand/collapse state yourself (e.g. to sync it with a URL). */
33
+ expandedIds?: string[];
34
+ onExpandedIdsChange?: (ids: string[]) => void;
35
+ selectedId?: string;
36
+ onSelectNode?: (node: TreeNode<T>) => void;
37
+ /** Customize how a node's row content renders; defaults to `node.icon` + `node.label`. */
38
+ renderLabel?: (node: TreeNode<T>) => ReactNode;
39
+ emptyMessage?: ReactNode;
40
+ /** Formats the error shown under a node whose `loadChildren` call rejected. */
41
+ loadErrorMessage?: (error: unknown, node: TreeNode<T>) => ReactNode;
42
+ className?: string;
43
+ 'data-testid'?: string;
44
+ };
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,47 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+
5
+ function useTreeExpansion(defaultExpandedIds = [], controlledIds, onChange) {
6
+ const [uncontrolledIds, setUncontrolledIds] = react.useState(
7
+ () => new Set(defaultExpandedIds)
8
+ );
9
+ const isControlled = controlledIds !== void 0;
10
+ const expandedIds = react.useMemo(
11
+ () => isControlled ? new Set(controlledIds) : uncontrolledIds,
12
+ [isControlled, controlledIds, uncontrolledIds]
13
+ );
14
+ const commit = react.useCallback(
15
+ (next) => {
16
+ if (!isControlled) setUncontrolledIds(next);
17
+ onChange == null ? void 0 : onChange(Array.from(next));
18
+ },
19
+ [isControlled, onChange]
20
+ );
21
+ const expand = react.useCallback(
22
+ (id) => {
23
+ if (expandedIds.has(id)) return;
24
+ commit(new Set(expandedIds).add(id));
25
+ },
26
+ [expandedIds, commit]
27
+ );
28
+ const collapse = react.useCallback(
29
+ (id) => {
30
+ if (!expandedIds.has(id)) return;
31
+ const next = new Set(expandedIds);
32
+ next.delete(id);
33
+ commit(next);
34
+ },
35
+ [expandedIds, commit]
36
+ );
37
+ const toggle = react.useCallback(
38
+ (id) => expandedIds.has(id) ? collapse(id) : expand(id),
39
+ [expandedIds, expand, collapse]
40
+ );
41
+ const expandAll = react.useCallback((ids) => commit(new Set(ids)), [commit]);
42
+ const collapseAll = react.useCallback(() => commit(/* @__PURE__ */ new Set()), [commit]);
43
+ const isExpanded = react.useCallback((id) => expandedIds.has(id), [expandedIds]);
44
+ return { expandedIds, expand, collapse, toggle, expandAll, collapseAll, isExpanded };
45
+ }
46
+
47
+ exports.useTreeExpansion = useTreeExpansion;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * expandedIds is the single source of truth for "open nodes", same model
3
+ * as the Sidebar's expandedItems: a plain Set of ids, with expand/collapse
4
+ * no-ops guarded so an already-open (or already-closed) node doesn't
5
+ * trigger a re-render. Supports both controlled (`controlledIds` +
6
+ * `onChange`, mirroring the Sidebar's activeLink-driven expansion) and
7
+ * uncontrolled usage.
8
+ */
9
+ export declare function useTreeExpansion(defaultExpandedIds?: string[], controlledIds?: string[], onChange?: (ids: string[]) => void): {
10
+ expandedIds: Set<string>;
11
+ expand: (id: string) => void;
12
+ collapse: (id: string) => void;
13
+ toggle: (id: string) => void;
14
+ expandAll: (ids: string[]) => void;
15
+ collapseAll: () => void;
16
+ isExpanded: (id: string) => boolean;
17
+ };
@@ -0,0 +1,45 @@
1
+ import { useState, useMemo, useCallback } from 'react';
2
+
3
+ function useTreeExpansion(defaultExpandedIds = [], controlledIds, onChange) {
4
+ const [uncontrolledIds, setUncontrolledIds] = useState(
5
+ () => new Set(defaultExpandedIds)
6
+ );
7
+ const isControlled = controlledIds !== void 0;
8
+ const expandedIds = useMemo(
9
+ () => isControlled ? new Set(controlledIds) : uncontrolledIds,
10
+ [isControlled, controlledIds, uncontrolledIds]
11
+ );
12
+ const commit = useCallback(
13
+ (next) => {
14
+ if (!isControlled) setUncontrolledIds(next);
15
+ onChange == null ? void 0 : onChange(Array.from(next));
16
+ },
17
+ [isControlled, onChange]
18
+ );
19
+ const expand = useCallback(
20
+ (id) => {
21
+ if (expandedIds.has(id)) return;
22
+ commit(new Set(expandedIds).add(id));
23
+ },
24
+ [expandedIds, commit]
25
+ );
26
+ const collapse = useCallback(
27
+ (id) => {
28
+ if (!expandedIds.has(id)) return;
29
+ const next = new Set(expandedIds);
30
+ next.delete(id);
31
+ commit(next);
32
+ },
33
+ [expandedIds, commit]
34
+ );
35
+ const toggle = useCallback(
36
+ (id) => expandedIds.has(id) ? collapse(id) : expand(id),
37
+ [expandedIds, expand, collapse]
38
+ );
39
+ const expandAll = useCallback((ids) => commit(new Set(ids)), [commit]);
40
+ const collapseAll = useCallback(() => commit(/* @__PURE__ */ new Set()), [commit]);
41
+ const isExpanded = useCallback((id) => expandedIds.has(id), [expandedIds]);
42
+ return { expandedIds, expand, collapse, toggle, expandAll, collapseAll, isExpanded };
43
+ }
44
+
45
+ export { useTreeExpansion };
@@ -0,0 +1,32 @@
1
+ 'use strict';
2
+
3
+ function collectExpandableIds(nodes) {
4
+ const ids = [];
5
+ const walk = (list) => {
6
+ for (const node of list) {
7
+ if (node.children && node.children.length > 0) {
8
+ ids.push(node.id);
9
+ walk(node.children);
10
+ }
11
+ }
12
+ };
13
+ walk(nodes);
14
+ return ids;
15
+ }
16
+ function findAncestorIds(nodes, targetId) {
17
+ var _a;
18
+ const dfs = (list, trail) => {
19
+ for (const node of list) {
20
+ if (node.id === targetId) return trail;
21
+ if (node.children) {
22
+ const found = dfs(node.children, [...trail, node.id]);
23
+ if (found) return found;
24
+ }
25
+ }
26
+ return null;
27
+ };
28
+ return (_a = dfs(nodes, [])) != null ? _a : [];
29
+ }
30
+
31
+ exports.collectExpandableIds = collectExpandableIds;
32
+ exports.findAncestorIds = findAncestorIds;
@@ -0,0 +1,16 @@
1
+ import type { TreeNode } from './types';
2
+ /**
3
+ * All node ids that already have at least one known child - the eager
4
+ * "expand all" target set. A node whose children haven't been resolved yet
5
+ * (lazy mode, before its first load) is skipped: expanding it needs a
6
+ * fetch, not a state flip, so it isn't safe to include here.
7
+ */
8
+ export declare function collectExpandableIds<T>(nodes: TreeNode<T>[]): string[];
9
+ /**
10
+ * DFS chain of ancestor ids leading to `targetId` within an already-known
11
+ * tree - pass the result to `defaultExpandedIds`/`expandedIds` to reveal a
12
+ * specific node (mirrors the Sidebar's `findExpandableParentChain`). Only
13
+ * searches subtrees already present via `children`, so in lazy mode this
14
+ * can only find a target inside branches already loaded.
15
+ */
16
+ export declare function findAncestorIds<T>(nodes: TreeNode<T>[], targetId: string): string[];
@@ -0,0 +1,29 @@
1
+ function collectExpandableIds(nodes) {
2
+ const ids = [];
3
+ const walk = (list) => {
4
+ for (const node of list) {
5
+ if (node.children && node.children.length > 0) {
6
+ ids.push(node.id);
7
+ walk(node.children);
8
+ }
9
+ }
10
+ };
11
+ walk(nodes);
12
+ return ids;
13
+ }
14
+ function findAncestorIds(nodes, targetId) {
15
+ var _a;
16
+ const dfs = (list, trail) => {
17
+ for (const node of list) {
18
+ if (node.id === targetId) return trail;
19
+ if (node.children) {
20
+ const found = dfs(node.children, [...trail, node.id]);
21
+ if (found) return found;
22
+ }
23
+ }
24
+ return null;
25
+ };
26
+ return (_a = dfs(nodes, [])) != null ? _a : [];
27
+ }
28
+
29
+ export { collectExpandableIds, findAncestorIds };