@ticatec/uniface-element 0.3.15 → 0.3.16
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.
- package/dist/lib/TreeNode_README.md +449 -0
- package/dist/lib/TreeNode_README_CN.md +449 -0
- package/dist/lib/TreeNodes.d.ts +88 -21
- package/dist/lib/TreeNodes.js +164 -77
- package/dist/list-box/index.d.ts +2 -2
- package/dist/list-box/types.d.ts +11 -1
- package/dist/tree-view/README.md +12 -11
- package/dist/tree-view/README_CN.md +141 -1
- package/dist/tree-view/TreeNodeView.svelte +16 -4
- package/dist/tree-view/TreeView.svelte +24 -1
- package/dist/tree-view/TreeView.svelte.d.ts +1 -0
- package/dist/tree-view/Types.d.ts +8 -4
- package/dist/tree-view/index.d.ts +2 -2
- package/package.json +1 -1
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
# TreeNode - Tree Node Data Structure
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
`TreeNode` is the core data type used to represent nodes in a tree structure. It contains the node's data, child nodes, expansion state, and provides convenient methods to manipulate the node itself and its children.
|
|
6
|
+
|
|
7
|
+
Every node obtained from `TreeNodes` automatically has bound operation methods that can be called directly.
|
|
8
|
+
|
|
9
|
+
## Type Definition
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
interface TreeNode<T> {
|
|
13
|
+
/** The node's data object */
|
|
14
|
+
item: T;
|
|
15
|
+
|
|
16
|
+
/** Whether the node is expanded (showing children) */
|
|
17
|
+
expand?: boolean;
|
|
18
|
+
|
|
19
|
+
/** Array of child nodes */
|
|
20
|
+
children?: TreeNode<T>[];
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### TreeNodeWithMethods<T>
|
|
25
|
+
|
|
26
|
+
This is the actual node type in use, extending `TreeNode<T>` with operation methods:
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
interface TreeNodeWithMethods<T> extends TreeNode<T> {
|
|
30
|
+
/** Add a child node to the current node */
|
|
31
|
+
append: (childItem: T) => void;
|
|
32
|
+
|
|
33
|
+
/** Remove the current node (from its parent) */
|
|
34
|
+
remove: () => void;
|
|
35
|
+
|
|
36
|
+
/** Replace the current node's data */
|
|
37
|
+
replace: (newItem: T) => void;
|
|
38
|
+
|
|
39
|
+
/** Move the current node to a different parent */
|
|
40
|
+
moveTo: (newParentId: string) => void;
|
|
41
|
+
|
|
42
|
+
/** Remove a specific child node */
|
|
43
|
+
removeChild: (childId: any) => void;
|
|
44
|
+
|
|
45
|
+
/** Remove all child nodes */
|
|
46
|
+
removeChildren: () => void;
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Node Methods
|
|
51
|
+
|
|
52
|
+
### 1. append(childItem)
|
|
53
|
+
|
|
54
|
+
Add a child node to the current node.
|
|
55
|
+
|
|
56
|
+
**Parameter**:
|
|
57
|
+
- `childItem: T` - The child node's data object
|
|
58
|
+
|
|
59
|
+
**Example**:
|
|
60
|
+
```typescript
|
|
61
|
+
// Pass data object directly
|
|
62
|
+
parentNode.append({
|
|
63
|
+
id: 123,
|
|
64
|
+
name: "New Child",
|
|
65
|
+
parent: parentNode.item.id // Optional, will be set automatically
|
|
66
|
+
});
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**Notes**:
|
|
70
|
+
- **Auto-load lazy nodes**: When a node is selected (clicked or when `activeNode` is set externally), if it's a lazy node and not loaded (`children === null`), TreeView will automatically trigger `lazyLoader.load()` to load child nodes
|
|
71
|
+
- **Manually add child nodes**: You can manually call `append()` to add child nodes
|
|
72
|
+
- If the node is not yet loaded, it will automatically initialize the `children` array
|
|
73
|
+
- The node will transition from leaf to branch, UI will display the branch icon
|
|
74
|
+
- If a sort function is configured, nodes will be automatically sorted
|
|
75
|
+
- The parent's `expand` is automatically set to `true`
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
### 2. remove()
|
|
80
|
+
|
|
81
|
+
Remove the current node (from its parent).
|
|
82
|
+
|
|
83
|
+
**Parameter**: None
|
|
84
|
+
|
|
85
|
+
**Example**:
|
|
86
|
+
```typescript
|
|
87
|
+
// Remove the node
|
|
88
|
+
node.remove();
|
|
89
|
+
|
|
90
|
+
// Clear reference after removal
|
|
91
|
+
activeNode.remove();
|
|
92
|
+
activeNode = null;
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**Notes**:
|
|
96
|
+
- Removes from parent's `children` array
|
|
97
|
+
- Deletes from internal map
|
|
98
|
+
- If it's a root node, removes from root node list
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
### 3. replace(newItem)
|
|
103
|
+
|
|
104
|
+
Replace the current node's data.
|
|
105
|
+
|
|
106
|
+
**Parameter**:
|
|
107
|
+
- `newItem: T` - New data object
|
|
108
|
+
|
|
109
|
+
**Example**:
|
|
110
|
+
```typescript
|
|
111
|
+
// Update some fields
|
|
112
|
+
node.replace({
|
|
113
|
+
...node.item, // Keep other fields
|
|
114
|
+
name: "New Name", // Update name
|
|
115
|
+
status: "active" // Update status
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// Or complete replacement
|
|
119
|
+
node.replace({
|
|
120
|
+
id: node.item.id, // Must keep ID
|
|
121
|
+
name: "Completely New Data",
|
|
122
|
+
newField: "value"
|
|
123
|
+
});
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
**Notes**:
|
|
127
|
+
- Usually need to keep the `id` field unchanged
|
|
128
|
+
- If a sort function is configured and sort field changes, will automatically re-sort
|
|
129
|
+
- Automatically triggers view update
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
### 4. moveTo(newParentId)
|
|
134
|
+
|
|
135
|
+
Move the current node to a different parent.
|
|
136
|
+
|
|
137
|
+
**Parameter**:
|
|
138
|
+
- `newParentId: string | number` - New parent node's ID
|
|
139
|
+
|
|
140
|
+
**Example**:
|
|
141
|
+
```typescript
|
|
142
|
+
// Move to specified parent
|
|
143
|
+
node.moveTo("parent-123");
|
|
144
|
+
|
|
145
|
+
// Use another node's ID
|
|
146
|
+
node.moveTo(anotherNode.item.id);
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
**Notes**:
|
|
150
|
+
- Automatically updates the node's `parent` field
|
|
151
|
+
- Removes from current parent's `children`
|
|
152
|
+
- Adds to new parent's `children`
|
|
153
|
+
- **Lazy node handling**: If the new parent is lazy-loaded and not loaded (`children === null`), it will automatically initialize the `children` array
|
|
154
|
+
- The new parent will transition from leaf to branch, UI will display the branch icon
|
|
155
|
+
- When the user expands the node later, the lazyLoader will load child nodes that merge with manually added nodes
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
### 5. removeChild(childId)
|
|
160
|
+
|
|
161
|
+
Remove a specific child node.
|
|
162
|
+
|
|
163
|
+
**Parameter**:
|
|
164
|
+
- `childId: any` - ID of the child node to remove
|
|
165
|
+
|
|
166
|
+
**Example**:
|
|
167
|
+
```typescript
|
|
168
|
+
// Remove child with specific ID
|
|
169
|
+
parentNode.removeChild(123);
|
|
170
|
+
|
|
171
|
+
// Use variable
|
|
172
|
+
const childId = childNode.item.id;
|
|
173
|
+
parentNode.removeChild(childId);
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
**Notes**:
|
|
177
|
+
- Only removes direct children, not grandchildren recursively
|
|
178
|
+
- Silently ignores if child doesn't exist
|
|
179
|
+
- All descendants of the child node are also removed
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
### 6. removeChildren()
|
|
184
|
+
|
|
185
|
+
Remove all child nodes.
|
|
186
|
+
|
|
187
|
+
**Parameter**: None
|
|
188
|
+
|
|
189
|
+
**Example**:
|
|
190
|
+
```typescript
|
|
191
|
+
// Clear all children
|
|
192
|
+
parentNode.removeChildren();
|
|
193
|
+
|
|
194
|
+
// Remove and then add new children
|
|
195
|
+
parentNode.removeChildren();
|
|
196
|
+
parentNode.append(newChildData);
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
**Notes**:
|
|
200
|
+
- Removes all children and their descendants
|
|
201
|
+
- Node's `expand` state remains unchanged
|
|
202
|
+
- Internal map is updated accordingly
|
|
203
|
+
|
|
204
|
+
## Complete Usage Example
|
|
205
|
+
|
|
206
|
+
```svelte
|
|
207
|
+
<script>
|
|
208
|
+
import TreeView from "@ticatec/uniface-element/TreeView";
|
|
209
|
+
import TreeNodes, { type TreeNode } from "@ticatec/uniface-element/TreeNodes";
|
|
210
|
+
|
|
211
|
+
// Data type definition
|
|
212
|
+
interface MyData {
|
|
213
|
+
id: number;
|
|
214
|
+
name: string;
|
|
215
|
+
parent: number | null;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
let activeNode: TreeNodeWithMethods<MyData> | null = null;
|
|
219
|
+
|
|
220
|
+
// Create tree structure
|
|
221
|
+
const treeNodes = new TreeNodes<MyData>({
|
|
222
|
+
keyField: 'id',
|
|
223
|
+
textField: 'name',
|
|
224
|
+
parentKeyField: 'parent',
|
|
225
|
+
checkIsRoot: (item) => item.parent === null,
|
|
226
|
+
checkIsDirectory: (node) => node.children != null
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
// Initialize data
|
|
230
|
+
treeNodes.setData([
|
|
231
|
+
{ id: 1, name: "Root", parent: null },
|
|
232
|
+
{ id: 2, name: "Child 1", parent: 1 },
|
|
233
|
+
{ id: 3, name: "Child 2", parent: 1 }
|
|
234
|
+
]);
|
|
235
|
+
|
|
236
|
+
// Add child node
|
|
237
|
+
function addChild() {
|
|
238
|
+
if (!activeNode) return;
|
|
239
|
+
|
|
240
|
+
activeNode.append({
|
|
241
|
+
id: Date.now(),
|
|
242
|
+
name: "New Child",
|
|
243
|
+
parent: activeNode.item.id
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Delete current node
|
|
248
|
+
function deleteCurrentNode() {
|
|
249
|
+
if (!activeNode) return;
|
|
250
|
+
activeNode.remove();
|
|
251
|
+
activeNode = null;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Delete specific child
|
|
255
|
+
function deleteChild(childId: number) {
|
|
256
|
+
if (!activeNode) return;
|
|
257
|
+
activeNode.removeChild(childId);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Clear all children
|
|
261
|
+
function clearChildren() {
|
|
262
|
+
if (!activeNode) return;
|
|
263
|
+
activeNode.removeChildren();
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Rename node
|
|
267
|
+
function renameNode(newName: string) {
|
|
268
|
+
if (!activeNode) return;
|
|
269
|
+
activeNode.replace({
|
|
270
|
+
...activeNode.item,
|
|
271
|
+
name: newName
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Move node
|
|
276
|
+
function moveNodeTo(newParentId: number) {
|
|
277
|
+
if (!activeNode) return;
|
|
278
|
+
activeNode.moveTo(newParentId);
|
|
279
|
+
}
|
|
280
|
+
</script>
|
|
281
|
+
|
|
282
|
+
<div>
|
|
283
|
+
<TreeView
|
|
284
|
+
nodes={treeNodes.nodes}
|
|
285
|
+
version={treeNodes.version}
|
|
286
|
+
textField="name"
|
|
287
|
+
bind:activeNode
|
|
288
|
+
checkIsDirectory={(node) => node.children != null}
|
|
289
|
+
/>
|
|
290
|
+
|
|
291
|
+
{#if activeNode}
|
|
292
|
+
<div class="node-actions">
|
|
293
|
+
<h3>Selected: {activeNode.item.name}</h3>
|
|
294
|
+
<button on:click={addChild}>Add Child</button>
|
|
295
|
+
<button on:click={deleteCurrentNode}>Delete Node</button>
|
|
296
|
+
<button on:click={() => renameNode("New Name")}>Rename</button>
|
|
297
|
+
<button on:click={() => moveNodeTo(1)}>Move to Root</button>
|
|
298
|
+
<button on:click={clearChildren}>Clear Children</button>
|
|
299
|
+
</div>
|
|
300
|
+
{/if}
|
|
301
|
+
</div>
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
## Difference from TreeNodes Class Methods
|
|
305
|
+
|
|
306
|
+
`TreeNode` methods and `TreeNodes` class methods have clear responsibilities:
|
|
307
|
+
|
|
308
|
+
### TreeNode Node Methods (Recommended for Operating on Specific Nodes)
|
|
309
|
+
|
|
310
|
+
```typescript
|
|
311
|
+
// Operate directly on node - Recommended when you have a node instance
|
|
312
|
+
node.append(childItem); // Add child to this node
|
|
313
|
+
node.remove(); // Remove this node
|
|
314
|
+
node.replace(newItem); // Replace this node's data
|
|
315
|
+
node.moveTo(newParentId); // Move this node
|
|
316
|
+
node.removeChild(childId); // Remove specific child
|
|
317
|
+
node.removeChildren(); // Remove all children
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### TreeNodes Class Methods (For Batch Operations or Initialization)
|
|
321
|
+
|
|
322
|
+
```typescript
|
|
323
|
+
// Class-level operations - Suitable when you only have data
|
|
324
|
+
treeNodes.setData(data); // Initialize tree structure
|
|
325
|
+
treeNodes.append(item); // Add new node (auto-find parent)
|
|
326
|
+
treeNodes.nodes; // Get root node list
|
|
327
|
+
treeNodes.version; // Get version number
|
|
328
|
+
treeNodes.getHierarchyList(); // Get expanded node list
|
|
329
|
+
treeNodes.extractDirectories(item); // Extract directory structure
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
## Reactive Updates
|
|
333
|
+
|
|
334
|
+
All node method operations automatically trigger `version` updates, ensuring Svelte components can respond to data changes:
|
|
335
|
+
|
|
336
|
+
```svelte
|
|
337
|
+
<TreeView
|
|
338
|
+
nodes={treeNodes.nodes}
|
|
339
|
+
version={treeNodes.version} ← Auto-updated
|
|
340
|
+
...
|
|
341
|
+
/>
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
You don't need to manually trigger updates; node methods handle it automatically.
|
|
345
|
+
|
|
346
|
+
## FAQ
|
|
347
|
+
|
|
348
|
+
### Q: How to get a node instance?
|
|
349
|
+
|
|
350
|
+
```typescript
|
|
351
|
+
// Method 1: Through activeNode (selected node)
|
|
352
|
+
let activeNode: TreeNode | null = null;
|
|
353
|
+
<TreeView bind:activeNode />
|
|
354
|
+
|
|
355
|
+
// Method 2: Through nodeMap (need to keep TreeNodes reference)
|
|
356
|
+
const node = treeNodes.nodeMap.get(nodeId);
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
### Q: How to iterate over child nodes?
|
|
360
|
+
|
|
361
|
+
```typescript
|
|
362
|
+
if (node.children) {
|
|
363
|
+
node.children.forEach(child => {
|
|
364
|
+
console.log(child.item.name);
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
### Q: How to find a specific child node?
|
|
370
|
+
|
|
371
|
+
```typescript
|
|
372
|
+
function findChild(node: TreeNode, childId: number) {
|
|
373
|
+
if (!node.children) return null;
|
|
374
|
+
return node.children.find(child => child.item.id === childId) || null;
|
|
375
|
+
}
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
### Q: How do lazy-loaded nodes work?
|
|
379
|
+
|
|
380
|
+
For nodes using lazyLoader (`children === null`):
|
|
381
|
+
|
|
382
|
+
```typescript
|
|
383
|
+
// ✅ When a node is selected, TreeView automatically triggers loading
|
|
384
|
+
// When you click a node or set activeNode:
|
|
385
|
+
// - If it's a lazy node and not loaded
|
|
386
|
+
// - Automatically calls lazyLoader.load()
|
|
387
|
+
// - children is set, node transitions to branch
|
|
388
|
+
|
|
389
|
+
// Manually add child nodes
|
|
390
|
+
lazyNode.append(childItem); // Initialize children array and add child node
|
|
391
|
+
// Node transitions to branch, shows branch icon
|
|
392
|
+
|
|
393
|
+
lazyNode.moveTo(parentId); // Moving to this node will initialize children
|
|
394
|
+
|
|
395
|
+
// removeChild() and removeChildren() have no effect before loading
|
|
396
|
+
lazyNode.removeChild(id); // No effect when children is null
|
|
397
|
+
lazyNode.removeChildren(); // No effect when children is null
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
### Q: How to recursively operate on all descendants?
|
|
401
|
+
|
|
402
|
+
```typescript
|
|
403
|
+
function traverse(node: TreeNode, callback: (node: TreeNode) => void) {
|
|
404
|
+
callback(node);
|
|
405
|
+
if (node.children) {
|
|
406
|
+
node.children.forEach(child => traverse(child, callback));
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// Usage
|
|
411
|
+
traverse(rootNode, (node) => {
|
|
412
|
+
console.log(node.item.name);
|
|
413
|
+
node.expand = true; // Expand all nodes
|
|
414
|
+
});
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
## Best Practices
|
|
418
|
+
|
|
419
|
+
1. **Prefer node methods**: When you have a node instance, calling node methods directly is more intuitive
|
|
420
|
+
|
|
421
|
+
2. **Keep ID unchanged**: When using `replace()`, ensure the `id` field remains unchanged
|
|
422
|
+
|
|
423
|
+
3. **Check children existence**: Before operating on child nodes, check if `node.children` exists first
|
|
424
|
+
|
|
425
|
+
4. **Handle lazy nodes**: For lazy-loaded nodes, wait for loading to complete before operations
|
|
426
|
+
|
|
427
|
+
5. **Version management**: No need to manually manage versions, node methods update automatically
|
|
428
|
+
|
|
429
|
+
6. **Type safety**: Use TypeScript generics to ensure type safety
|
|
430
|
+
|
|
431
|
+
```typescript
|
|
432
|
+
// ✅ Recommended: Use generics
|
|
433
|
+
const treeNodes = new TreeNodes<MyData>({ ... });
|
|
434
|
+
|
|
435
|
+
// Node type is automatically inferred
|
|
436
|
+
node.append({ id: 1, name: "..." }); // Type checking
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
## Performance Considerations
|
|
440
|
+
|
|
441
|
+
- All node methods have O(1) or O(n) time complexity, where n is the number of child nodes
|
|
442
|
+
- Delete operations also remove from internal map to maintain consistency
|
|
443
|
+
- For large batch operations, consider using `setData()` to rebuild the tree structure
|
|
444
|
+
|
|
445
|
+
## Related Components
|
|
446
|
+
|
|
447
|
+
- `TreeNodes` - Tree management class
|
|
448
|
+
- `TreeView` - Tree view component
|
|
449
|
+
- `LazyLoader` - Lazy loading interface
|