@zid-utils/tree-utils 0.0.6 → 0.0.8

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 (3) hide show
  1. package/README.md +458 -0
  2. package/dist/index.js +276 -244
  3. package/package.json +6 -3
package/README.md ADDED
@@ -0,0 +1,458 @@
1
+ # @zid-utils/tree-utils
2
+
3
+ > 树形数据结构处理工具库 (Tree data structure utility library)
4
+
5
+ ## 安装
6
+
7
+ ```bash
8
+ pnpm add @zid-utils/tree-utils
9
+ ```
10
+
11
+ ## 核心方法(来自 tree-lodash)
12
+
13
+ 本包使用 [tree-lodash](https://github.com/zhangshichun/tree-lodash) 作为核心实现,提供强大的树操作功能。
14
+
15
+ ### foreach - 遍历
16
+
17
+ 遍历树或森林的所有节点。
18
+
19
+ ```typescript
20
+ import { foreach } from '@zid-utils/tree-utils';
21
+
22
+ const tree = {
23
+ key: '1',
24
+ title: 'Root',
25
+ children: [
26
+ { key: '1-1', title: 'Child 1' },
27
+ { key: '1-2', title: 'Child 2' }
28
+ ]
29
+ };
30
+
31
+ foreach(tree, (node, meta) => {
32
+ console.log(`${' '.repeat(meta.depth)}${node.title}`);
33
+ }, { strategy: 'pre' });
34
+ ```
35
+
36
+ ### map - 映射
37
+
38
+ 遍历并映射每个节点,返回新的树结构。
39
+
40
+ ```typescript
41
+ import { map } from '@zid-utils/tree-utils';
42
+
43
+ const newTree = map(tree, (node) => ({
44
+ ...node,
45
+ title: `Mapped: ${node.title}`
46
+ }));
47
+ ```
48
+
49
+ ### filter - 过滤
50
+
51
+ 过滤树节点,保留符合条件的节点。
52
+
53
+ ```typescript
54
+ import { filter } from '@zid-utils/tree-utils';
55
+
56
+ const filteredTree = filter(tree, (node) => node.key.startsWith('1-'));
57
+ ```
58
+
59
+ ### find - 查找
60
+
61
+ 查找第一个匹配的节点。
62
+
63
+ ```typescript
64
+ import { find } from '@zid-utils/tree-utils';
65
+
66
+ const node = find(tree, (node) => node.key === '1-1');
67
+ ```
68
+
69
+ ### some - 存在检查
70
+
71
+ 检查是否存在匹配的节点。
72
+
73
+ ```typescript
74
+ import { some } from '@zid-utils/tree-utils';
75
+
76
+ const exists = some(tree, (node) => node.key === '1-1');
77
+ ```
78
+
79
+ ### toArray - 扁平化
80
+
81
+ 将树结构扁平化为数组。
82
+
83
+ ```typescript
84
+ import { toArray } from '@zid-utils/tree-utils';
85
+
86
+ const nodes = toArray(tree);
87
+ ```
88
+
89
+ ### fromArray - 构建树
90
+
91
+ 从扁平数组构建树结构。
92
+
93
+ ```typescript
94
+ import { fromArray } from '@zid-utils/tree-utils';
95
+
96
+ const array = [
97
+ { id: 1, pid: null, title: 'Root' },
98
+ { id: 2, pid: 1, title: 'Child' }
99
+ ];
100
+
101
+ const tree = fromArray(array, {
102
+ parentKey: 'pid',
103
+ itemKey: 'id',
104
+ childrenKey: 'children'
105
+ });
106
+ ```
107
+
108
+ ## 扩展方法
109
+
110
+ ### 节点查询
111
+
112
+ #### getLeafNodes
113
+
114
+ 获取所有叶子节点。
115
+
116
+ ```typescript
117
+ import { getLeafNodes } from '@zid-utils/tree-utils';
118
+
119
+ const leafNodes = getLeafNodes(tree, 'isLeaf');
120
+ ```
121
+
122
+ #### findNodeByKey
123
+
124
+ 根据 key 查找节点。
125
+
126
+ ```typescript
127
+ import { findNodeByKey } from '@zid-utils/tree-utils';
128
+
129
+ const node = findNodeByKey(tree, '1-1');
130
+ ```
131
+
132
+ #### findNodeById
133
+
134
+ 根据 id 查找节点。
135
+
136
+ ```typescript
137
+ import { findNodeById } from '@zid-utils/tree-utils';
138
+
139
+ const node = findNodeById(tree, 'node-id');
140
+ ```
141
+
142
+ #### findNodeByMatcher
143
+
144
+ 根据匹配器查找节点。
145
+
146
+ ```typescript
147
+ import { findNodeByMatcher } from '@zid-utils/tree-utils';
148
+
149
+ const node = findNodeByMatcher(tree, (n) => n.title === 'Target');
150
+ ```
151
+
152
+ #### nodeExistsInTree
153
+
154
+ 检查节点是否存在于树中。
155
+
156
+ ```typescript
157
+ import { nodeExistsInTree } from '@zid-utils/tree-utils';
158
+
159
+ const exists = nodeExistsInTree(tree, 'node-title');
160
+ ```
161
+
162
+ #### findParentOf
163
+
164
+ 查找节点的父节点。
165
+
166
+ ```typescript
167
+ import { findParentOf } from '@zid-utils/tree-utils';
168
+
169
+ const parent = findParentOf(tree, 'child-key');
170
+ ```
171
+
172
+ #### getNodePath
173
+
174
+ 获取节点路径。
175
+
176
+ ```typescript
177
+ import { getNodePath, type PathNode } from '@zid-utils/tree-utils';
178
+
179
+ const path = getNodePath(tree, '1-2', 'key', 'children');
180
+ // 返回 PathNode[] 包含路径信息
181
+ ```
182
+
183
+ #### getNodeDepth
184
+
185
+ 获取节点深度。
186
+
187
+ ```typescript
188
+ import { getNodeDepth } from '@zid-utils/tree-utils';
189
+
190
+ const depth = getNodeDepth(tree, '1-2-1');
191
+ // 返回节点所在层级(根节点为0)
192
+ ```
193
+
194
+ #### getNodeBreadcrumb
195
+
196
+ 获取面包屑路径(所有祖先节点)。
197
+
198
+ ```typescript
199
+ import { getNodeBreadcrumb } from '@zid-utils/tree-utils';
200
+
201
+ const breadcrumb = getNodeBreadcrumb(tree, '1-2-1');
202
+ // 返回从根到目标的所有节点
203
+ ```
204
+
205
+ #### getTreeStats
206
+
207
+ 获取树统计信息。
208
+
209
+ ```typescript
210
+ import { getTreeStats } from '@zid-utils/tree-utils';
211
+
212
+ const stats = getTreeStats(tree);
213
+ // { totalNodes: 10, maxDepth: 3, leafCount: 5 }
214
+ ```
215
+
216
+ ### 节点操作
217
+
218
+ #### updateKeys
219
+
220
+ 更新节点的 key。
221
+
222
+ ```typescript
223
+ import { updateKeys } from '@zid-utils/tree-utils';
224
+
225
+ updateKeys(sourceNode, targetNode);
226
+ ```
227
+
228
+ #### updateNodeTitleByKey
229
+
230
+ 根据 key 更新节点标题。
231
+
232
+ ```typescript
233
+ import { updateNodeTitleByKey } from '@zid-utils/tree-utils';
234
+
235
+ updateNodeTitleByKey(tree, '1-1', 'New Title');
236
+ ```
237
+
238
+ #### updateNodeByMatcher
239
+
240
+ 根据匹配器更新节点。
241
+
242
+ ```typescript
243
+ import { updateNodeByMatcher } from '@zid-utils/tree-utils';
244
+
245
+ const newTree = updateNodeByMatcher(
246
+ tree,
247
+ (node) => node.key === 'target',
248
+ (node) => ({ ...node, title: 'Updated' })
249
+ );
250
+ ```
251
+
252
+ #### moveNodeInTree
253
+
254
+ 在树中移动节点。
255
+
256
+ ```typescript
257
+ import { moveNodeInTree } from '@zid-utils/tree-utils';
258
+
259
+ moveNodeInTree(tree, 'source-key', 'target-key');
260
+ ```
261
+
262
+ #### cloneNode
263
+
264
+ 克隆节点。
265
+
266
+ ```typescript
267
+ import { cloneNode } from '@zid-utils/tree-utils';
268
+
269
+ cloneNode(sourceNode, targetNode);
270
+ ```
271
+
272
+ #### copyNode
273
+
274
+ 复制节点(带副本标识)。
275
+
276
+ ```typescript
277
+ import { copyNode } from '@zid-utils/tree-utils';
278
+
279
+ const newTree = copyNode(tree, 'source-key', 'target-key');
280
+ ```
281
+
282
+ #### deleteNode
283
+
284
+ 删除节点。
285
+
286
+ ```typescript
287
+ import { deleteNode } from '@zid-utils/tree-utils';
288
+
289
+ const newTree = deleteNode(tree, 'key-to-delete');
290
+ ```
291
+
292
+ #### addLeafProperties
293
+
294
+ 为节点添加叶子属性。
295
+
296
+ ```typescript
297
+ import { addLeafProperties } from '@zid-utils/tree-utils';
298
+
299
+ const newTree = addLeafProperties(tree);
300
+ // 自动设置 isLeaf 和 disabled 属性
301
+ ```
302
+
303
+ ### 树转换
304
+
305
+ #### transformTreeKeys
306
+
307
+ 转换树节点的键。
308
+
309
+ ```typescript
310
+ import { transformTreeKeys } from '@zid-utils/tree-utils';
311
+
312
+ const newTree = transformTreeKeys(tree, {
313
+ title: 'label',
314
+ key: 'id'
315
+ });
316
+ ```
317
+
318
+ #### transformTreeNodes
319
+
320
+ 转换树节点数据。
321
+
322
+ ```typescript
323
+ import { transformTreeNodes } from '@zid-utils/tree-utils';
324
+
325
+ const newTree = transformTreeNodes(tree, (node) => ({
326
+ ...node,
327
+ isActive: true
328
+ }));
329
+ ```
330
+
331
+ #### searchInTree
332
+
333
+ 在树中搜索节点。
334
+
335
+ ```typescript
336
+ import { searchInTree } from '@zid-utils/tree-utils';
337
+
338
+ const results = searchInTree(tree, 'search-term');
339
+ ```
340
+
341
+ ### 工具函数
342
+
343
+ #### findFirstLeaf
344
+
345
+ 查找第一个叶子节点。
346
+
347
+ ```typescript
348
+ import { findFirstLeaf } from '@zid-utils/tree-utils';
349
+
350
+ const leaf = findFirstLeaf(tree);
351
+ ```
352
+
353
+ #### traverseTreeValues
354
+
355
+ 遍历树中特定键的值。
356
+
357
+ ```typescript
358
+ import { traverseTreeValues } from '@zid-utils/tree-utils';
359
+
360
+ const values = traverseTreeValues(tree, 'title');
361
+ ```
362
+
363
+ #### filterCheckedLeafKeys
364
+
365
+ 过滤选中的叶子节点 key。
366
+
367
+ ```typescript
368
+ import { filterCheckedLeafKeys } from '@zid-utils/tree-utils';
369
+
370
+ const keys = filterCheckedLeafKeys(tree);
371
+ ```
372
+
373
+ #### collectLeafValuesByKey
374
+
375
+ 收集叶子节点的值。
376
+
377
+ ```typescript
378
+ import { collectLeafValuesByKey } from '@zid-utils/tree-utils';
379
+
380
+ const values = collectLeafValuesByKey(tree, 'parent-value');
381
+ ```
382
+
383
+ #### convertGroupsToTreeData
384
+
385
+ 将分组数据转换为树形结构。
386
+
387
+ ```typescript
388
+ import { convertGroupsToTreeData, type TreeGroup } from '@zid-utils/tree-utils';
389
+
390
+ const tree = convertGroupsToTreeData(items, 'category');
391
+ // 返回 TreeGroup[] 类型
392
+ ```
393
+
394
+ ## 类型定义
395
+
396
+ ### TreeNode
397
+
398
+ ```typescript
399
+ interface TreeNode {
400
+ key?: string | number;
401
+ title?: string | number;
402
+ children?: TreeNode[];
403
+ isLeaf?: boolean;
404
+ disabled?: boolean;
405
+ selectable?: boolean;
406
+ checked?: boolean;
407
+ [key: string]: any;
408
+ }
409
+ ```
410
+
411
+ ### PathNode
412
+
413
+ ```typescript
414
+ interface PathNode<T> {
415
+ node: T; // 节点数据
416
+ depth: number; // 深度(根节点为0)
417
+ index: number; // 在兄弟节点中的索引
418
+ parent: PathNode<T> | null; // 父节点
419
+ path: string; // 路径字符串,如 'root/child/grandchild'
420
+ }
421
+ ```
422
+
423
+ ### TreeGroup
424
+
425
+ ```typescript
426
+ interface TreeGroup {
427
+ label: string;
428
+ value: string | number;
429
+ children?: any[];
430
+ }
431
+ ```
432
+
433
+ ## 遍历策略
434
+
435
+ 所有核心方法都支持三种遍历策略:
436
+
437
+ - `pre` - 前序遍历(默认)
438
+ - `post` - 后序遍历
439
+ - `breadth` - 广度优先遍历
440
+
441
+ ```typescript
442
+ import { foreach } from '@zid-utils/tree-utils';
443
+
444
+ foreach(tree, callback, { strategy: 'breadth' });
445
+ ```
446
+
447
+ ## 相关库推荐
448
+
449
+ ### 文件系统场景
450
+ - [@httpx/treeu](https://github.com/belgattitude/httpx/tree/main/packages/treeu) - 轻量级 DFS 搜索和路径映射,适合深层嵌套树的性能优化
451
+
452
+ ### 其他选择
453
+ - [tree-model](https://github.com/joaonuno/tree-model) - 模型驱动的树操作
454
+ - [js-tree](https://github.com/guigrpa/js-tree) - React 集成友好的树组件
455
+
456
+ ## License
457
+
458
+ MIT
package/dist/index.js CHANGED
@@ -1,8 +1,16 @@
1
- // src/index.ts
2
- var defaultLogger = {
3
- warn: (msg) => console.warn("[tree-utils]", msg),
4
- error: (msg) => console.error("[tree-utils]", msg)
5
- };
1
+ // src/traverse/foreach.ts
2
+ import { default as default2 } from "tree-lodash";
3
+
4
+ // src/traverse/toArray.ts
5
+ import { default as default3 } from "tree-lodash";
6
+
7
+ // src/query/find.ts
8
+ import { default as default4 } from "tree-lodash";
9
+
10
+ // src/query/some.ts
11
+ import { default as default5 } from "tree-lodash";
12
+
13
+ // src/query/getLeafNodes.ts
6
14
  var getLeafNodes = (treeData, isLeafKey = "isLeaf") => {
7
15
  function collectLeafNodes(nodes) {
8
16
  const leafNodes = [];
@@ -17,24 +25,8 @@ var getLeafNodes = (treeData, isLeafKey = "isLeaf") => {
17
25
  }
18
26
  return collectLeafNodes(treeData);
19
27
  };
20
- var filterTreeNodes = (nodes, callback, isOnlyFilterChildren = true) => {
21
- const filterChildren = (children) => {
22
- if (children && children.length > 0) {
23
- return children.filter(callback).map((child) => {
24
- return {
25
- ...child,
26
- children: filterChildren(child.children)
27
- };
28
- });
29
- }
30
- return [];
31
- };
32
- const rootNodes = isOnlyFilterChildren ? nodes : nodes.filter(callback);
33
- return rootNodes.map((node) => ({
34
- ...node,
35
- children: filterChildren(node.children)
36
- }));
37
- };
28
+
29
+ // src/query/findNodeByKey.ts
38
30
  var findNodeByKey = (treeData, key) => {
39
31
  for (let i = 0; i < treeData.length; i++) {
40
32
  const node = treeData[i];
@@ -50,28 +42,127 @@ var findNodeByKey = (treeData, key) => {
50
42
  }
51
43
  return null;
52
44
  };
53
- var searchInTree = (tree, searchValue) => {
54
- function searchRecursive(node, searchText, result2) {
55
- const title = node.title?.toString().toLowerCase() || "";
56
- const text = searchText.toLowerCase();
57
- const arr = text.split("");
58
- if (node.isLeaf && arr.every((item) => title.includes(item))) {
59
- if (!result2.some((item) => item.key === node.key)) {
60
- result2.push(node);
61
- }
45
+
46
+ // src/query/findNodeById.ts
47
+ var findNodeById = (nodes, id) => {
48
+ for (const node of nodes) {
49
+ if (node.id === id) return node;
50
+ if (node.children && node.children.length) {
51
+ const found = findNodeById(node.children, id);
52
+ if (found) return found;
62
53
  }
63
- if (node.children) {
64
- for (const child of node.children) {
65
- searchRecursive(child, text, result2);
54
+ }
55
+ return null;
56
+ };
57
+
58
+ // src/query/findNodeByMatcher.ts
59
+ var findNodeByMatcher = (nodes, matcher, childrenKey = "children") => {
60
+ for (const node of nodes) {
61
+ if (matcher(node)) {
62
+ return node;
63
+ }
64
+ if (node[childrenKey]) {
65
+ const found = findNodeByMatcher(node[childrenKey], matcher, childrenKey);
66
+ if (found) {
67
+ return found;
66
68
  }
67
69
  }
68
70
  }
69
- const result = [];
70
- for (const rootNode of tree) {
71
- searchRecursive(rootNode, searchValue.toLowerCase(), result);
71
+ return null;
72
+ };
73
+
74
+ // src/query/nodeExistsInTree.ts
75
+ var nodeExistsInTree = (nodes, searchValue) => {
76
+ return nodes.some((node) => {
77
+ if (node.value === searchValue || node.title === searchValue) {
78
+ return true;
79
+ }
80
+ if (node.children && Array.isArray(node.children)) {
81
+ return nodeExistsInTree(node.children, searchValue);
82
+ }
83
+ return false;
84
+ });
85
+ };
86
+
87
+ // src/query/findParentOf.ts
88
+ var findParentOf = (nodes, targetKey) => {
89
+ for (const node of nodes) {
90
+ if (node.children?.some((child) => child.key === targetKey)) {
91
+ return node;
92
+ }
93
+ if (node.children) {
94
+ const found = findParentOf(node.children, targetKey);
95
+ if (found) return found;
96
+ }
72
97
  }
73
- return result;
98
+ return null;
99
+ };
100
+
101
+ // src/query/getNodePath.ts
102
+ var getNodePath = (treeData, targetKey, keyField = "key", childrenKey = "children") => {
103
+ const findPath = (nodes, target, currentPath) => {
104
+ for (let i = 0; i < nodes.length; i++) {
105
+ const node = nodes[i];
106
+ const newPathNode = {
107
+ node,
108
+ depth: currentPath.length,
109
+ index: i,
110
+ parent: currentPath.length > 0 ? currentPath[currentPath.length - 1] : null,
111
+ path: currentPath.length === 0 ? String(node[keyField]) : `${currentPath[currentPath.length - 1].path}/${String(node[keyField])}`
112
+ };
113
+ if (node[keyField] === target) {
114
+ return [...currentPath, newPathNode];
115
+ }
116
+ const children = node[childrenKey];
117
+ if (children && Array.isArray(children) && children.length > 0) {
118
+ const result = findPath(children, target, [
119
+ ...currentPath,
120
+ newPathNode
121
+ ]);
122
+ if (result) {
123
+ return result;
124
+ }
125
+ }
126
+ }
127
+ return null;
128
+ };
129
+ return findPath(treeData, targetKey, []);
130
+ };
131
+
132
+ // src/query/getNodeDepth.ts
133
+ var getNodeDepth = (treeData, targetKey, keyField = "key", childrenKey = "children") => {
134
+ const path = getNodePath(treeData, targetKey, keyField, childrenKey);
135
+ return path ? path.length - 1 : -1;
74
136
  };
137
+
138
+ // src/query/getNodeBreadcrumb.ts
139
+ var getNodeBreadcrumb = (treeData, targetKey, keyField = "key", childrenKey = "children") => {
140
+ const path = getNodePath(treeData, targetKey, keyField, childrenKey);
141
+ return path ? path.map((p) => p.node) : [];
142
+ };
143
+
144
+ // src/query/getTreeStats.ts
145
+ var getTreeStats = (treeData, childrenKey = "children") => {
146
+ let totalNodes = 0;
147
+ let maxDepth = 0;
148
+ let leafCount = 0;
149
+ const traverse = (nodes, depth) => {
150
+ for (const node of nodes) {
151
+ totalNodes++;
152
+ maxDepth = Math.max(maxDepth, depth);
153
+ const children = node[childrenKey];
154
+ if (children && Array.isArray(children) && children.length > 0) {
155
+ traverse(children, depth + 1);
156
+ } else {
157
+ leafCount++;
158
+ }
159
+ }
160
+ };
161
+ traverse(treeData, 0);
162
+ return { totalNodes, maxDepth, leafCount };
163
+ };
164
+
165
+ // src/modify/updateKeys.ts
75
166
  var updateKeys = (sourceNode, targetNode) => {
76
167
  sourceNode.key = `${targetNode.key}-${targetNode.children?.length}`;
77
168
  if (sourceNode.children) {
@@ -80,6 +171,49 @@ var updateKeys = (sourceNode, targetNode) => {
80
171
  });
81
172
  }
82
173
  };
174
+
175
+ // src/modify/updateNodeTitleByKey.ts
176
+ var updateNodeTitleByKey = (tree, targetKey, newTitle) => {
177
+ for (const node of tree) {
178
+ if (node.key === targetKey) {
179
+ node.title = newTitle;
180
+ return;
181
+ }
182
+ if (node.children) {
183
+ updateNodeTitleByKey(node.children, targetKey, newTitle);
184
+ }
185
+ }
186
+ };
187
+
188
+ // src/modify/updateNodeByMatcher.ts
189
+ var updateNodeByMatcher = (nodes, matcher, updater, childrenKey = "children") => {
190
+ return nodes.map((node) => {
191
+ if (matcher(node)) {
192
+ return updater(node);
193
+ }
194
+ if (node[childrenKey] && node[childrenKey].length > 0) {
195
+ const newChildren = updateNodeByMatcher(
196
+ node[childrenKey],
197
+ matcher,
198
+ updater,
199
+ childrenKey
200
+ );
201
+ if (newChildren !== node[childrenKey]) {
202
+ return {
203
+ ...node,
204
+ [childrenKey]: newChildren
205
+ };
206
+ }
207
+ }
208
+ return node;
209
+ });
210
+ };
211
+
212
+ // src/modify/moveNodeInTree.ts
213
+ var defaultLogger = {
214
+ warn: (msg) => console.warn("[tree-utils]", msg),
215
+ error: (msg) => console.error("[tree-utils]", msg)
216
+ };
83
217
  var moveNodeInTree = (treeData, sourceKey, targetKey, logger = defaultLogger) => {
84
218
  function findNode(nodes, key) {
85
219
  if (!nodes) return null;
@@ -133,6 +267,8 @@ var moveNodeInTree = (treeData, sourceKey, targetKey, logger = defaultLogger) =>
133
267
  targetNode.children = targetNode.children || [];
134
268
  targetNode.children.push(sourceNode);
135
269
  };
270
+
271
+ // src/modify/cloneNode.ts
136
272
  var cloneNode = (sourceNode, targetNode) => {
137
273
  const clonedNode = { ...sourceNode };
138
274
  if (targetNode.children) {
@@ -141,6 +277,8 @@ var cloneNode = (sourceNode, targetNode) => {
141
277
  targetNode.children = [clonedNode];
142
278
  }
143
279
  };
280
+
281
+ // src/modify/copyNode.ts
144
282
  var copyNode = (treeData, key, targetKey) => {
145
283
  const sourceNode = structuredClone(findNodeByKey(treeData, key));
146
284
  const targetNode = findNodeByKey(treeData, targetKey);
@@ -152,17 +290,8 @@ var copyNode = (treeData, key, targetKey) => {
152
290
  }
153
291
  return treeData;
154
292
  };
155
- var updateNodeTitleByKey = (tree, targetKey, newTitle) => {
156
- for (const node of tree) {
157
- if (node.key === targetKey) {
158
- node.title = newTitle;
159
- return;
160
- }
161
- if (node.children) {
162
- updateNodeTitleByKey(node.children, targetKey, newTitle);
163
- }
164
- }
165
- };
293
+
294
+ // src/modify/deleteNode.ts
166
295
  var deleteNode = (treeData, key) => {
167
296
  const delNode = (data) => {
168
297
  return data.filter((node) => {
@@ -177,19 +306,8 @@ var deleteNode = (treeData, key) => {
177
306
  };
178
307
  return delNode([...treeData]);
179
308
  };
180
- var flattenTree = (tree, filter = () => true) => {
181
- const result = [];
182
- const flatten = (node) => {
183
- if (filter(node)) {
184
- result.push(node);
185
- }
186
- if (node.children) {
187
- node.children.forEach(flatten);
188
- }
189
- };
190
- tree.forEach(flatten);
191
- return result;
192
- };
309
+
310
+ // src/modify/addLeafProperties.ts
193
311
  var addLeafProperties = (treeData) => {
194
312
  return treeData.map((node) => {
195
313
  const isLeaf = !node.children || node.children.length === 0;
@@ -201,66 +319,19 @@ var addLeafProperties = (treeData) => {
201
319
  };
202
320
  });
203
321
  };
204
- var findNodeById = (nodes, id) => {
205
- for (const node of nodes) {
206
- if (node.id === id) return node;
207
- if (node.children && node.children.length) {
208
- const found = findNodeById(node.children, id);
209
- if (found) return found;
210
- }
211
- }
212
- return null;
213
- };
214
- var filterCheckedLeafKeys = (treeData) => {
215
- const result = [];
216
- const traverse = (nodes) => {
217
- if (!nodes || nodes.length === 0) return;
218
- for (const node of nodes) {
219
- if (node.checked === false) continue;
220
- if (node.children && node.children.length > 0) {
221
- traverse(node.children);
222
- } else if (node.checked === true) {
223
- result.push(node.key);
224
- }
225
- }
226
- };
227
- traverse(treeData);
228
- return result;
229
- };
230
- var nodeExistsInTree = (nodes, searchValue) => {
231
- return nodes.some((node) => {
232
- if (node.value === searchValue || node.title === searchValue) {
233
- return true;
234
- }
235
- if (node.children && Array.isArray(node.children)) {
236
- return nodeExistsInTree(node.children, searchValue);
237
- }
238
- return false;
239
- });
240
- };
241
- var collectLeafValuesByKey = (nodes, targetValue, isLeafKey = "isLeaf") => {
242
- for (const node of nodes) {
243
- if (node.value === targetValue) {
244
- if (node.children && Array.isArray(node.children) && node.children.length > 0) {
245
- const leafNodes = getLeafNodes([node], isLeafKey);
246
- return leafNodes.map((leaf) => leaf.value);
247
- }
248
- return null;
249
- }
250
- if (node.children && Array.isArray(node.children)) {
251
- const result = collectLeafValuesByKey(
252
- node.children,
253
- targetValue,
254
- isLeafKey
255
- );
256
- if (result !== null) {
257
- return result;
258
- }
259
- }
260
- }
261
- return null;
322
+
323
+ // src/transform/map.ts
324
+ import { default as default6 } from "tree-lodash";
325
+
326
+ // src/transform/filter.ts
327
+ import { default as default7 } from "tree-lodash";
328
+
329
+ // src/transform/transformTreeKeys.ts
330
+ var defaultLogger2 = {
331
+ warn: (msg) => console.warn("[tree-utils]", msg),
332
+ error: (msg) => console.error("[tree-utils]", msg)
262
333
  };
263
- var transformTreeKeys = (treeData, keyMapping, childrenKey = "children", logger = defaultLogger) => {
334
+ var transformTreeKeys = (treeData, keyMapping, childrenKey = "children", logger = defaultLogger2) => {
264
335
  if (!Array.isArray(treeData)) {
265
336
  logger.warn?.("transformTreeKeys: treeData must be an array");
266
337
  return [];
@@ -288,7 +359,13 @@ var transformTreeKeys = (treeData, keyMapping, childrenKey = "children", logger
288
359
  };
289
360
  return treeData.map(transformNode);
290
361
  };
291
- var transformTreeNodes = (treeData, transformer, childrenKey = "children", logger = defaultLogger) => {
362
+
363
+ // src/transform/transformTreeNodes.ts
364
+ var defaultLogger3 = {
365
+ warn: (msg) => console.warn("[tree-utils]", msg),
366
+ error: (msg) => console.error("[tree-utils]", msg)
367
+ };
368
+ var transformTreeNodes = (treeData, transformer, childrenKey = "children", logger = defaultLogger3) => {
292
369
  if (!Array.isArray(treeData)) {
293
370
  logger.warn?.("transformTreeNodes: treeData must be an array");
294
371
  return [];
@@ -313,126 +390,35 @@ var transformTreeNodes = (treeData, transformer, childrenKey = "children", logge
313
390
  };
314
391
  return treeData.map(transformNode);
315
392
  };
316
- var findParentOf = (nodes, targetKey) => {
317
- for (const node of nodes) {
318
- if (node.children?.some((child) => child.key === targetKey)) {
319
- return node;
393
+
394
+ // src/transform/searchInTree.ts
395
+ var searchInTree = (tree, searchValue) => {
396
+ function searchRecursive(node, searchText, result2) {
397
+ const title = node.title?.toString().toLowerCase() || "";
398
+ const text = searchText.toLowerCase();
399
+ const arr = text.split("");
400
+ if (node.isLeaf && arr.every((item) => title.includes(item))) {
401
+ if (!result2.some((item) => item.key === node.key)) {
402
+ result2.push(node);
403
+ }
320
404
  }
321
405
  if (node.children) {
322
- const found = findParentOf(node.children, targetKey);
323
- if (found) return found;
324
- }
325
- }
326
- return null;
327
- };
328
- var findNodeByMatcher = (nodes, matcher, childrenKey = "children") => {
329
- for (const node of nodes) {
330
- if (matcher(node)) {
331
- return node;
332
- }
333
- if (node[childrenKey]) {
334
- const found = findNodeByMatcher(node[childrenKey], matcher, childrenKey);
335
- if (found) {
336
- return found;
406
+ for (const child of node.children) {
407
+ searchRecursive(child, text, result2);
337
408
  }
338
409
  }
339
410
  }
340
- return null;
341
- };
342
- var updateNodeByMatcher = (nodes, matcher, updater, childrenKey = "children") => {
343
- return nodes.map((node) => {
344
- if (matcher(node)) {
345
- return updater(node);
346
- }
347
- if (node[childrenKey] && node[childrenKey].length > 0) {
348
- const newChildren = updateNodeByMatcher(
349
- node[childrenKey],
350
- matcher,
351
- updater,
352
- childrenKey
353
- );
354
- if (newChildren !== node[childrenKey]) {
355
- return {
356
- ...node,
357
- [childrenKey]: newChildren
358
- };
359
- }
360
- }
361
- return node;
362
- });
363
- };
364
- var getNodePath = (treeData, targetKey, keyField = "key", childrenKey = "children") => {
365
- const findPath = (nodes, target, currentPath) => {
366
- for (let i = 0; i < nodes.length; i++) {
367
- const node = nodes[i];
368
- const newPathNode = {
369
- node,
370
- depth: currentPath.length,
371
- index: i,
372
- parent: currentPath.length > 0 ? currentPath[currentPath.length - 1] : null,
373
- path: currentPath.length === 0 ? String(node[keyField]) : `${currentPath[currentPath.length - 1].path}/${String(node[keyField])}`
374
- };
375
- if (node[keyField] === target) {
376
- return [...currentPath, newPathNode];
377
- }
378
- const children = node[childrenKey];
379
- if (children && Array.isArray(children) && children.length > 0) {
380
- const result = findPath(children, target, [
381
- ...currentPath,
382
- newPathNode
383
- ]);
384
- if (result) {
385
- return result;
386
- }
387
- }
388
- }
389
- return null;
390
- };
391
- return findPath(treeData, targetKey, []);
392
- };
393
- var getNodeDepth = (treeData, targetKey, keyField = "key", childrenKey = "children") => {
394
- const path = getNodePath(treeData, targetKey, keyField, childrenKey);
395
- return path ? path.length - 1 : -1;
396
- };
397
- var getNodeBreadcrumb = (treeData, targetKey, keyField = "key", childrenKey = "children") => {
398
- const path = getNodePath(treeData, targetKey, keyField, childrenKey);
399
- return path ? path.map((p) => p.node) : [];
400
- };
401
- var getTreeStats = (treeData, childrenKey = "children") => {
402
- let totalNodes = 0;
403
- let maxDepth = 0;
404
- let leafCount = 0;
405
- const traverse = (nodes, depth) => {
406
- for (const node of nodes) {
407
- totalNodes++;
408
- maxDepth = Math.max(maxDepth, depth);
409
- const children = node[childrenKey];
410
- if (children && Array.isArray(children) && children.length > 0) {
411
- traverse(children, depth + 1);
412
- } else {
413
- leafCount++;
414
- }
415
- }
416
- };
417
- traverse(treeData, 0);
418
- return { totalNodes, maxDepth, leafCount };
419
- };
420
- var mapTree = (treeData, mapper, childrenKey = "children") => {
421
- const traverse = (nodes, parent) => {
422
- return nodes.map((node) => {
423
- const mappedNode = mapper(node, parent);
424
- const children = node[childrenKey];
425
- if (children && Array.isArray(children) && children.length > 0) {
426
- return {
427
- ...mappedNode,
428
- [childrenKey]: traverse(children, node)
429
- };
430
- }
431
- return mappedNode;
432
- });
433
- };
434
- return traverse(treeData);
411
+ const result = [];
412
+ for (const rootNode of tree) {
413
+ searchRecursive(rootNode, searchValue.toLowerCase(), result);
414
+ }
415
+ return result;
435
416
  };
417
+
418
+ // src/build/fromArray.ts
419
+ import { default as default8 } from "tree-lodash";
420
+
421
+ // src/utils/findFirstLeaf.ts
436
422
  var findFirstLeaf = (treeData, childrenKey = "children") => {
437
423
  for (const node of treeData) {
438
424
  const children = node[childrenKey];
@@ -444,6 +430,8 @@ var findFirstLeaf = (treeData, childrenKey = "children") => {
444
430
  }
445
431
  return null;
446
432
  };
433
+
434
+ // src/utils/traverseTreeValues.ts
447
435
  var traverseTreeValues = (treeData, key, childrenKey = "children") => {
448
436
  const result = [];
449
437
  const traverse = (nodes) => {
@@ -460,6 +448,50 @@ var traverseTreeValues = (treeData, key, childrenKey = "children") => {
460
448
  traverse(treeData);
461
449
  return result;
462
450
  };
451
+
452
+ // src/utils/filterCheckedLeafKeys.ts
453
+ var filterCheckedLeafKeys = (treeData) => {
454
+ const result = [];
455
+ const traverse = (nodes) => {
456
+ if (!nodes || nodes.length === 0) return;
457
+ for (const node of nodes) {
458
+ if (node.checked === false) continue;
459
+ if (node.children && node.children.length > 0) {
460
+ traverse(node.children);
461
+ } else if (node.checked === true) {
462
+ result.push(node.key);
463
+ }
464
+ }
465
+ };
466
+ traverse(treeData);
467
+ return result;
468
+ };
469
+
470
+ // src/utils/collectLeafValuesByKey.ts
471
+ var collectLeafValuesByKey = (nodes, targetValue, isLeafKey = "isLeaf") => {
472
+ for (const node of nodes) {
473
+ if (node.value === targetValue) {
474
+ if (node.children && Array.isArray(node.children) && node.children.length > 0) {
475
+ const leafNodes = getLeafNodes([node], isLeafKey);
476
+ return leafNodes.map((leaf) => leaf.value);
477
+ }
478
+ return null;
479
+ }
480
+ if (node.children && Array.isArray(node.children)) {
481
+ const result = collectLeafValuesByKey(
482
+ node.children,
483
+ targetValue,
484
+ isLeafKey
485
+ );
486
+ if (result !== null) {
487
+ return result;
488
+ }
489
+ }
490
+ }
491
+ return null;
492
+ };
493
+
494
+ // src/utils/convertGroupsToTreeData.ts
463
495
  var convertGroupsToTreeData = (groups, groupKey = "group", _childrenKey = "children") => {
464
496
  const groupMap = /* @__PURE__ */ new Map();
465
497
  for (const item of groups) {
@@ -482,27 +514,27 @@ export {
482
514
  convertGroupsToTreeData,
483
515
  copyNode,
484
516
  deleteNode,
517
+ default7 as filter,
485
518
  filterCheckedLeafKeys,
486
- filterTreeNodes,
519
+ default4 as find,
487
520
  findFirstLeaf,
488
- findNodeById as findNode,
489
- collectLeafValuesByKey as findNodeAndCollectLeafValues,
490
521
  findNodeById,
491
522
  findNodeByKey,
492
523
  findNodeByMatcher,
493
- nodeExistsInTree as findNodeInTree,
494
524
  findParentOf,
495
- findParentOf as findTreeNode,
496
- flattenTree,
525
+ default2 as foreach,
526
+ default8 as fromArray,
497
527
  getLeafNodes,
498
528
  getNodeBreadcrumb,
499
529
  getNodeDepth,
500
530
  getNodePath,
501
531
  getTreeStats,
502
- mapTree,
532
+ default6 as map,
503
533
  moveNodeInTree,
504
534
  nodeExistsInTree,
505
535
  searchInTree,
536
+ default5 as some,
537
+ default3 as toArray,
506
538
  transformTreeKeys,
507
539
  transformTreeNodes,
508
540
  traverseTreeValues,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zid-utils/tree-utils",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
4
4
  "description": "Tree data structure utility functions",
5
5
  "repository": "github:huangzida/utils-packages",
6
6
  "type": "module",
@@ -19,8 +19,11 @@
19
19
  ],
20
20
  "devDependencies": {
21
21
  "tsup": "^8.0.2",
22
- "vitest": "^2.0.0",
23
- "typescript": "^5.4.5"
22
+ "typescript": "^5.4.5",
23
+ "vitest": "^2.0.0"
24
+ },
25
+ "dependencies": {
26
+ "tree-lodash": "^0.4.0"
24
27
  },
25
28
  "scripts": {
26
29
  "build": "tsup src/index.ts --format esm --out-dir dist --clean",