|
1 | | -import type { |
2 | | - ExplorerExpandedState, |
3 | | - ExplorerFileEntry, |
4 | | - ExplorerFlatNode, |
5 | | - ExplorerNode |
6 | | -} from './types'; |
7 | | -import { normalizeRelativePath } from './utils/path'; |
8 | | - |
9 | | -type MutableNode = ExplorerNode & { |
10 | | - childMap?: Map<string, MutableNode>; |
11 | | -}; |
12 | | - |
13 | | -function compareNodes(a: ExplorerNode, b: ExplorerNode): number { |
14 | | - if (a.isDir && !b.isDir) return -1; |
15 | | - if (!a.isDir && b.isDir) return 1; |
16 | | - return a.name.localeCompare(b.name, undefined, { |
17 | | - sensitivity: 'base', |
18 | | - numeric: true |
19 | | - }); |
20 | | -} |
21 | | - |
22 | | -function toExplorerNodes(map: Map<string, MutableNode>): ExplorerNode[] { |
23 | | - return Array.from(map.values()) |
24 | | - .map((node): ExplorerNode => { |
25 | | - const children = node.childMap ? toExplorerNodes(node.childMap) : undefined; |
26 | | - return { |
27 | | - name: node.name, |
28 | | - path: node.path, |
29 | | - isDir: node.isDir, |
30 | | - depth: node.depth, |
31 | | - children, |
32 | | - requestCount: node.requestCount |
33 | | - }; |
34 | | - }) |
35 | | - .sort(compareNodes); |
36 | | -} |
37 | | - |
38 | | -export function buildExplorerTree(files: ExplorerFileEntry[]): ExplorerNode[] { |
39 | | - const rootMap = new Map<string, MutableNode>(); |
40 | | - |
41 | | - for (const file of files) { |
42 | | - const normalizedPath = normalizeRelativePath(file.path); |
43 | | - if (!normalizedPath) continue; |
44 | | - |
45 | | - const parts = normalizedPath.split('/'); |
46 | | - let currentPath = ''; |
47 | | - let cursorMap = rootMap; |
48 | | - |
49 | | - for (let index = 0; index < parts.length; index += 1) { |
50 | | - const part = parts[index]; |
51 | | - if (!part) continue; |
52 | | - |
53 | | - currentPath = currentPath ? `${currentPath}/${part}` : part; |
54 | | - const isDir = index < parts.length - 1; |
55 | | - let node = cursorMap.get(part); |
56 | | - |
57 | | - if (!node) { |
58 | | - node = { |
59 | | - name: part, |
60 | | - path: currentPath, |
61 | | - isDir, |
62 | | - depth: index, |
63 | | - children: isDir ? [] : undefined, |
64 | | - requestCount: isDir ? undefined : file.requestCount, |
65 | | - childMap: isDir ? new Map<string, MutableNode>() : undefined |
66 | | - }; |
67 | | - cursorMap.set(part, node); |
68 | | - } else { |
69 | | - if (isDir && !node.isDir) { |
70 | | - node.isDir = true; |
71 | | - node.requestCount = undefined; |
72 | | - node.children = []; |
73 | | - node.childMap = new Map<string, MutableNode>(); |
74 | | - } |
75 | | - |
76 | | - if (!isDir) { |
77 | | - node.requestCount = file.requestCount; |
78 | | - } |
79 | | - } |
80 | | - |
81 | | - if (isDir) { |
82 | | - if (!node.childMap) { |
83 | | - node.childMap = new Map<string, MutableNode>(); |
84 | | - } |
85 | | - if (!node.children) { |
86 | | - node.children = []; |
87 | | - } |
88 | | - cursorMap = node.childMap; |
89 | | - } |
90 | | - } |
91 | | - } |
92 | | - |
93 | | - return toExplorerNodes(rootMap); |
94 | | -} |
95 | | - |
96 | | -export function flattenExplorerTree( |
97 | | - nodes: ExplorerNode[], |
98 | | - expandedDirs: ExplorerExpandedState |
99 | | -): ExplorerFlatNode[] { |
100 | | - const flattened: ExplorerFlatNode[] = []; |
101 | | - |
102 | | - const visit = (node: ExplorerNode) => { |
103 | | - const isExpanded = Boolean(expandedDirs[node.path]); |
104 | | - flattened.push({ |
105 | | - node, |
106 | | - isExpanded |
107 | | - }); |
108 | | - |
109 | | - if (!node.isDir || !isExpanded || !node.children) { |
110 | | - return; |
111 | | - } |
112 | | - |
113 | | - for (const child of node.children) { |
114 | | - visit(child); |
115 | | - } |
116 | | - }; |
117 | | - |
118 | | - for (const node of nodes) { |
119 | | - visit(node); |
120 | | - } |
121 | | - |
122 | | - return flattened; |
123 | | -} |
124 | | - |
125 | | -export function createInitialExpandedDirs(nodes: ExplorerNode[]): ExplorerExpandedState { |
126 | | - const initial: ExplorerExpandedState = {}; |
127 | | - |
128 | | - for (const node of nodes) { |
129 | | - if (node.isDir) { |
130 | | - initial[node.path] = true; |
131 | | - } |
132 | | - } |
133 | | - |
134 | | - return initial; |
135 | | -} |
136 | | - |
137 | | -export function findExplorerNode(nodes: ExplorerNode[], path: string): ExplorerNode | undefined { |
138 | | - for (const node of nodes) { |
139 | | - if (node.path === path) { |
140 | | - return node; |
141 | | - } |
142 | | - |
143 | | - if (node.children) { |
144 | | - const child = findExplorerNode(node.children, path); |
145 | | - if (child) { |
146 | | - return child; |
147 | | - } |
148 | | - } |
149 | | - } |
150 | | - |
151 | | - return undefined; |
152 | | -} |
153 | | - |
154 | | -export function hasExplorerPath(nodes: ExplorerNode[], path: string): boolean { |
155 | | - return Boolean(findExplorerNode(nodes, path)); |
156 | | -} |
157 | | - |
158 | | -export function pruneExpandedDirs( |
159 | | - expandedDirs: ExplorerExpandedState, |
160 | | - nodes: ExplorerNode[] |
161 | | -): ExplorerExpandedState { |
162 | | - const pruned: ExplorerExpandedState = {}; |
163 | | - |
164 | | - for (const [path, isExpanded] of Object.entries(expandedDirs)) { |
165 | | - const node = findExplorerNode(nodes, path); |
166 | | - if (node?.isDir) { |
167 | | - pruned[path] = Boolean(isExpanded); |
168 | | - } |
169 | | - } |
170 | | - |
171 | | - return pruned; |
172 | | -} |
| 1 | +export { |
| 2 | + buildExplorerTree, |
| 3 | + createInitialExpandedDirs, |
| 4 | + findExplorerNode, |
| 5 | + flattenExplorerTree, |
| 6 | + hasExplorerPath, |
| 7 | + pruneExpandedDirs |
| 8 | +} from '@t-req/ui/explorer'; |
0 commit comments