@ticatec/uniface-element 0.3.15 → 0.3.17
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 +577 -0
- package/dist/lib/TreeNode_README_CN.md +577 -0
- package/dist/lib/TreeNodes.d.ts +366 -45
- package/dist/lib/TreeNodes.js +394 -137
- package/dist/lib/index.d.ts +3 -4
- package/dist/lib/index.js +1 -2
- package/dist/list-box/index.d.ts +2 -2
- package/dist/list-box/types.d.ts +11 -1
- package/dist/nav-menu/Menus.d.ts +2 -2
- package/dist/nav-menu/Menus.js +6 -4
- package/dist/ticatec-uniface-web.css +29 -5
- package/dist/tree-view/README.md +143 -17
- package/dist/tree-view/README_CN.md +141 -1
- package/dist/tree-view/TreeNodeView.svelte +23 -9
- package/dist/tree-view/TreeNodeView.svelte.d.ts +5 -5
- package/dist/tree-view/TreeView.svelte +45 -5
- package/dist/tree-view/TreeView.svelte.d.ts +4 -2
- package/dist/tree-view/Types.d.ts +12 -8
- package/dist/tree-view/index.d.ts +3 -2
- package/package.json +1 -1
|
@@ -0,0 +1,577 @@
|
|
|
1
|
+
# TreeNode - Tree Node Data Structure
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
`TreeNode` is a class that represents nodes in a tree structure. It encapsulates the node's data, child nodes, expansion state, and provides convenient methods to manipulate the node itself and its children.
|
|
6
|
+
|
|
7
|
+
Each node created from `TreeNodes` or `CommonTreeNodes` is an instance of the `TreeNode` class with built-in methods.
|
|
8
|
+
|
|
9
|
+
## TreeNode Class
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
class TreeNode<T> {
|
|
13
|
+
/** The node's data object (readonly) */
|
|
14
|
+
public readonly item: T;
|
|
15
|
+
|
|
16
|
+
/** Whether the node is expanded (showing children) */
|
|
17
|
+
public expand: boolean;
|
|
18
|
+
|
|
19
|
+
/** Whether the node is currently loading (for lazy loading) */
|
|
20
|
+
public loading: boolean;
|
|
21
|
+
|
|
22
|
+
/** Parent node (null for root nodes) */
|
|
23
|
+
public parent: TreeNode<T> | null;
|
|
24
|
+
|
|
25
|
+
/** Child nodes (readonly, use methods to modify) */
|
|
26
|
+
get children(): ReadonlyArray<TreeNode<T>>;
|
|
27
|
+
|
|
28
|
+
/** Add a child node to the current node */
|
|
29
|
+
append(childItem: T): void;
|
|
30
|
+
|
|
31
|
+
/** Remove the current node (from its parent) */
|
|
32
|
+
detach(): void;
|
|
33
|
+
|
|
34
|
+
/** Move the current node to a different parent */
|
|
35
|
+
moveTo(newParent: TreeNode<T>): void;
|
|
36
|
+
|
|
37
|
+
/** Replace the current node's data */
|
|
38
|
+
replace(newItem: T): void;
|
|
39
|
+
|
|
40
|
+
/** Remove a specific child node by ID */
|
|
41
|
+
removeChild(childId: any): void;
|
|
42
|
+
|
|
43
|
+
/** Remove all child nodes */
|
|
44
|
+
removeChildren(): void;
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## NodeViewOptions Class
|
|
49
|
+
|
|
50
|
+
The `NodeViewOptions` class handles view configuration for tree nodes, separating display concerns from data:
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
class NodeViewOptions<T> {
|
|
54
|
+
/** Field name for unique identifier */
|
|
55
|
+
keyField: keyof T;
|
|
56
|
+
|
|
57
|
+
/** Field name or function for display text */
|
|
58
|
+
textField: keyof T | ((data: T) => string);
|
|
59
|
+
|
|
60
|
+
/** Optional: Field name or function for icon */
|
|
61
|
+
iconField?: keyof T | ((data: T) => string);
|
|
62
|
+
|
|
63
|
+
/** Optional: Field name or function for CSS class */
|
|
64
|
+
cssClassField?: keyof T | ((data: T) => string);
|
|
65
|
+
|
|
66
|
+
/** Optional: Field name or function for inline styles */
|
|
67
|
+
styleField?: keyof T | ((data: T) => Record<string, string>);
|
|
68
|
+
|
|
69
|
+
/** Get display text from data */
|
|
70
|
+
getText(data: T): string;
|
|
71
|
+
|
|
72
|
+
/** Get icon from data */
|
|
73
|
+
getIcon(data: T): string | undefined;
|
|
74
|
+
|
|
75
|
+
/** Get CSS class from data */
|
|
76
|
+
getCssClass(data: T): string | undefined;
|
|
77
|
+
|
|
78
|
+
/** Get styles from data */
|
|
79
|
+
getStyle(data: T): Record<string, string> | undefined;
|
|
80
|
+
|
|
81
|
+
/** Get unique key from data */
|
|
82
|
+
getKey(data: T): any;
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## TreeNode Methods
|
|
87
|
+
|
|
88
|
+
### 1. append(childItem)
|
|
89
|
+
|
|
90
|
+
Add a child node to the current node.
|
|
91
|
+
|
|
92
|
+
**Parameter**:
|
|
93
|
+
- `childItem: T` - The child node's data object
|
|
94
|
+
|
|
95
|
+
**Example**:
|
|
96
|
+
```typescript
|
|
97
|
+
// Pass data object directly
|
|
98
|
+
parentNode.append({
|
|
99
|
+
id: 123,
|
|
100
|
+
name: "New Child",
|
|
101
|
+
parentId: parentNode.item.id // Will be set automatically
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**Notes**:
|
|
106
|
+
- Automatically initializes children array if needed
|
|
107
|
+
- If a sort function is configured, nodes will be automatically sorted
|
|
108
|
+
- The parent's `expand` is automatically set to `true`
|
|
109
|
+
- Triggers version update for reactive updates
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
### 2. detach()
|
|
114
|
+
|
|
115
|
+
Remove the current node from its parent.
|
|
116
|
+
|
|
117
|
+
**Parameter**: None
|
|
118
|
+
|
|
119
|
+
**Example**:
|
|
120
|
+
```typescript
|
|
121
|
+
// Remove the node from its parent
|
|
122
|
+
node.detach();
|
|
123
|
+
|
|
124
|
+
// Clear reference after removal
|
|
125
|
+
activeNode.detach();
|
|
126
|
+
activeNode = null;
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**Notes**:
|
|
130
|
+
- Removes from parent's `children` array
|
|
131
|
+
- Deletes from internal map
|
|
132
|
+
- If it's a root node, removes from root node list
|
|
133
|
+
- Triggers version update for reactive updates
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
### 3. moveTo(newParent)
|
|
138
|
+
|
|
139
|
+
Move the current node to a different parent node.
|
|
140
|
+
|
|
141
|
+
**Parameter**:
|
|
142
|
+
- `newParent: TreeNode<T>` - The new parent node (not an ID)
|
|
143
|
+
|
|
144
|
+
**Example**:
|
|
145
|
+
```typescript
|
|
146
|
+
// Move to a different parent node
|
|
147
|
+
node.moveTo(newParentNode);
|
|
148
|
+
|
|
149
|
+
// Get parent node first
|
|
150
|
+
const parentNode = treeNodes.nodeMap.get(parentId);
|
|
151
|
+
if (parentNode) {
|
|
152
|
+
node.moveTo(parentNode);
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
**Notes**:
|
|
157
|
+
- Takes a TreeNode instance, not an ID
|
|
158
|
+
- Automatically updates the node's `parentKeyField` in data
|
|
159
|
+
- Removes from current parent's `children`
|
|
160
|
+
- Adds to new parent's `children`
|
|
161
|
+
- If a sort function is configured, will automatically re-sort
|
|
162
|
+
- Triggers version update for reactive updates
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
### 4. replace(newItem)
|
|
167
|
+
|
|
168
|
+
Replace the current node's data.
|
|
169
|
+
|
|
170
|
+
**Parameter**:
|
|
171
|
+
- `newItem: T` - New data object
|
|
172
|
+
|
|
173
|
+
**Example**:
|
|
174
|
+
```typescript
|
|
175
|
+
// Update some fields
|
|
176
|
+
node.replace({
|
|
177
|
+
...node.item, // Keep other fields
|
|
178
|
+
name: "New Name", // Update name
|
|
179
|
+
status: "active" // Update status
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// Or complete replacement
|
|
183
|
+
node.replace({
|
|
184
|
+
id: node.item.id, // Must keep ID
|
|
185
|
+
name: "Completely New Data",
|
|
186
|
+
newField: "value"
|
|
187
|
+
});
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
**Notes**:
|
|
191
|
+
- Usually need to keep the `id` field unchanged
|
|
192
|
+
- If a sort function is configured and sort field changes, will automatically re-sort
|
|
193
|
+
- Automatically triggers view update
|
|
194
|
+
- Preserves children, expand state, and loading state
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
### 5. removeChild(childId)
|
|
199
|
+
|
|
200
|
+
Remove a specific child node.
|
|
201
|
+
|
|
202
|
+
**Parameter**:
|
|
203
|
+
- `childId: any` - ID of the child node to remove
|
|
204
|
+
|
|
205
|
+
**Example**:
|
|
206
|
+
```typescript
|
|
207
|
+
// Remove child with specific ID
|
|
208
|
+
parentNode.removeChild(123);
|
|
209
|
+
|
|
210
|
+
// Use variable
|
|
211
|
+
const childId = childNode.item.id;
|
|
212
|
+
parentNode.removeChild(childId);
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
**Notes**:
|
|
216
|
+
- Only removes direct children, not grandchildren recursively
|
|
217
|
+
- Silently ignores if child doesn't exist
|
|
218
|
+
- All descendants of the child node are also removed
|
|
219
|
+
- Triggers version update for reactive updates
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
### 6. removeChildren()
|
|
224
|
+
|
|
225
|
+
Remove all child nodes.
|
|
226
|
+
|
|
227
|
+
**Parameter**: None
|
|
228
|
+
|
|
229
|
+
**Example**:
|
|
230
|
+
```typescript
|
|
231
|
+
// Clear all children
|
|
232
|
+
parentNode.removeChildren();
|
|
233
|
+
|
|
234
|
+
// Remove and then add new children
|
|
235
|
+
parentNode.removeChildren();
|
|
236
|
+
parentNode.append(newChildData);
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
**Notes**:
|
|
240
|
+
- Removes all children and their descendants
|
|
241
|
+
- Node's `expand` state remains unchanged
|
|
242
|
+
- Internal map is updated accordingly
|
|
243
|
+
- Triggers version update for reactive updates
|
|
244
|
+
|
|
245
|
+
## CommonTreeNodes Options
|
|
246
|
+
|
|
247
|
+
When creating a tree, you can configure it with these options:
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
interface TreeNodeOptions<T> {
|
|
251
|
+
/** Field name for unique identifier (default: 'id') */
|
|
252
|
+
keyField?: keyof T;
|
|
253
|
+
|
|
254
|
+
/** Field name or function for display text (default: 'text') */
|
|
255
|
+
textField?: keyof T | ((data: T) => string);
|
|
256
|
+
|
|
257
|
+
/** Field name for parent reference (default: 'parentId') */
|
|
258
|
+
parentKeyField?: keyof T;
|
|
259
|
+
|
|
260
|
+
/** Function to check if data is a root node */
|
|
261
|
+
checkIsRoot: (data: T) => boolean;
|
|
262
|
+
|
|
263
|
+
/** Optional: Function to check if node is a branch (has children) */
|
|
264
|
+
checkIsDirectory?: (node: TreeNode<T>) => boolean;
|
|
265
|
+
|
|
266
|
+
/** Optional: Comparison function for sorting */
|
|
267
|
+
compareFun?: (o1: T, o2: T) => number | undefined;
|
|
268
|
+
|
|
269
|
+
/** Optional: Expand depth (default: 1) */
|
|
270
|
+
expendDepth?: number;
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
## Complete Usage Example
|
|
275
|
+
|
|
276
|
+
```svelte
|
|
277
|
+
<script>
|
|
278
|
+
import TreeView from "@ticatec/uniface-element/TreeView";
|
|
279
|
+
import { CommonTreeNodes, type TreeNode } from "@ticatec/uniface-element/lib/TreeNodes";
|
|
280
|
+
|
|
281
|
+
// Data type definition
|
|
282
|
+
interface MyData {
|
|
283
|
+
id: number;
|
|
284
|
+
name: string;
|
|
285
|
+
parentId: number | null;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
let activeNode: TreeNode<MyData> | null = null;
|
|
289
|
+
|
|
290
|
+
// Create tree structure
|
|
291
|
+
const treeNodes = new CommonTreeNodes<MyData>({
|
|
292
|
+
keyField: 'id',
|
|
293
|
+
textField: 'name',
|
|
294
|
+
parentKeyField: 'parentId',
|
|
295
|
+
checkIsRoot: (item) => item.parentId === null,
|
|
296
|
+
checkIsDirectory: (node) => node.children.length > 0,
|
|
297
|
+
expendDepth: 2
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
// Initialize data
|
|
301
|
+
treeNodes.setData([
|
|
302
|
+
{ id: 1, name: "Root", parentId: null },
|
|
303
|
+
{ id: 2, name: "Child 1", parentId: 1 },
|
|
304
|
+
{ id: 3, name: "Child 2", parentId: 1 }
|
|
305
|
+
]);
|
|
306
|
+
|
|
307
|
+
// Add child node
|
|
308
|
+
function addChild() {
|
|
309
|
+
if (!activeNode) return;
|
|
310
|
+
|
|
311
|
+
activeNode.append({
|
|
312
|
+
id: Date.now(),
|
|
313
|
+
name: "New Child",
|
|
314
|
+
parentId: activeNode.item.id
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Delete current node
|
|
319
|
+
function deleteCurrentNode() {
|
|
320
|
+
if (!activeNode) return;
|
|
321
|
+
activeNode.detach();
|
|
322
|
+
activeNode = null;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Delete specific child
|
|
326
|
+
function deleteChild(childId: number) {
|
|
327
|
+
if (!activeNode) return;
|
|
328
|
+
activeNode.removeChild(childId);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Clear all children
|
|
332
|
+
function clearChildren() {
|
|
333
|
+
if (!activeNode) return;
|
|
334
|
+
activeNode.removeChildren();
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Rename node
|
|
338
|
+
function renameNode(newName: string) {
|
|
339
|
+
if (!activeNode) return;
|
|
340
|
+
activeNode.replace({
|
|
341
|
+
...activeNode.item,
|
|
342
|
+
name: newName
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// Move node
|
|
347
|
+
function moveNodeTo(newParentId: number) {
|
|
348
|
+
if (!activeNode) return;
|
|
349
|
+
const newParent = treeNodes.nodeMap.get(newParentId);
|
|
350
|
+
if (newParent) {
|
|
351
|
+
activeNode.moveTo(newParent);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
</script>
|
|
355
|
+
|
|
356
|
+
<div>
|
|
357
|
+
<TreeView
|
|
358
|
+
nodes={treeNodes.nodes}
|
|
359
|
+
version={treeNodes.version}
|
|
360
|
+
textField="name"
|
|
361
|
+
bind:activeNode
|
|
362
|
+
checkIsDirectory={(node) => node.children.length > 0}
|
|
363
|
+
/>
|
|
364
|
+
|
|
365
|
+
{#if activeNode}
|
|
366
|
+
<div class="node-actions">
|
|
367
|
+
<h3>Selected: {activeNode.item.name}</h3>
|
|
368
|
+
<button on:click={addChild}>Add Child</button>
|
|
369
|
+
<button on:click={deleteCurrentNode}>Delete Node</button>
|
|
370
|
+
<button on:click={() => renameNode("New Name")}>Rename</button>
|
|
371
|
+
<button on:click={() => moveNodeTo(1)}>Move to Root</button>
|
|
372
|
+
<button on:click={clearChildren}>Clear Children</button>
|
|
373
|
+
</div>
|
|
374
|
+
{/if}
|
|
375
|
+
</div>
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
## Difference from TreeNodes Class Methods
|
|
379
|
+
|
|
380
|
+
`TreeNode` instance methods and `CommonTreeNodes`/`TreeNodes` class methods have clear responsibilities:
|
|
381
|
+
|
|
382
|
+
### TreeNode Instance Methods (For Operating on Specific Node Instances)
|
|
383
|
+
|
|
384
|
+
```typescript
|
|
385
|
+
// Operate directly on a node instance
|
|
386
|
+
node.append(childItem); // Add child to this node
|
|
387
|
+
node.detach(); // Remove this node from its parent
|
|
388
|
+
node.replace(newItem); // Replace this node's data
|
|
389
|
+
node.moveTo(newParentNode); // Move this node to another parent
|
|
390
|
+
node.removeChild(childId); // Remove specific child
|
|
391
|
+
node.removeChildren(); // Remove all children
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
### CommonTreeNodes/TreeNodes Class Methods (For Tree-Level Operations)
|
|
395
|
+
|
|
396
|
+
```typescript
|
|
397
|
+
// Tree-level operations
|
|
398
|
+
treeNodes.setData(data); // Initialize tree structure
|
|
399
|
+
treeNodes.append(item); // Add new node (auto-find parent)
|
|
400
|
+
treeNodes.nodes; // Get root node list
|
|
401
|
+
treeNodes.version; // Get version number
|
|
402
|
+
treeNodes.getHierarchyList(); // Get expanded node list (TreeNodes only)
|
|
403
|
+
treeNodes.extractDirectories(item); // Extract directory structure (TreeNodes only)
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
## Key Properties
|
|
407
|
+
|
|
408
|
+
### item (readonly)
|
|
409
|
+
The data object stored in the node. Cannot be modified directly. Use `replace()` to update data.
|
|
410
|
+
|
|
411
|
+
### children (readonly)
|
|
412
|
+
Returns a read-only array of child nodes. To modify children, use the methods:
|
|
413
|
+
- `append()` - add a child
|
|
414
|
+
- `removeChild()` - remove a specific child
|
|
415
|
+
- `removeChildren()` - remove all children
|
|
416
|
+
|
|
417
|
+
### expand
|
|
418
|
+
Controls whether the node is expanded (showing its children). Can be set directly:
|
|
419
|
+
```typescript
|
|
420
|
+
node.expand = true; // Expand the node
|
|
421
|
+
node.expand = false; // Collapse the node
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
### loading
|
|
425
|
+
Indicates if the node is currently loading data (used for lazy loading). Can be set directly:
|
|
426
|
+
```typescript
|
|
427
|
+
node.loading = true; // Show loading state
|
|
428
|
+
node.loading = false; // Hide loading state
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
### parent
|
|
432
|
+
Reference to the parent node. Is `null` for root nodes.
|
|
433
|
+
|
|
434
|
+
```typescript
|
|
435
|
+
if (node.parent) {
|
|
436
|
+
console.log("Parent name:", node.parent.item.name);
|
|
437
|
+
}
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
## Reactive Updates
|
|
441
|
+
|
|
442
|
+
All node method operations automatically trigger `version` updates, ensuring Svelte components can respond to data changes:
|
|
443
|
+
|
|
444
|
+
```svelte
|
|
445
|
+
<TreeView
|
|
446
|
+
nodes={treeNodes.nodes}
|
|
447
|
+
version={treeNodes.version} ← Auto-updated
|
|
448
|
+
...
|
|
449
|
+
/>
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
You don't need to manually trigger updates; node methods handle it automatically.
|
|
453
|
+
|
|
454
|
+
## FAQ
|
|
455
|
+
|
|
456
|
+
### Q: How to get a node instance?
|
|
457
|
+
|
|
458
|
+
```typescript
|
|
459
|
+
// Method 1: Through activeNode (selected node)
|
|
460
|
+
let activeNode: TreeNode | null = null;
|
|
461
|
+
<TreeView bind:activeNode />
|
|
462
|
+
|
|
463
|
+
// Method 2: Through nodeMap (need to keep TreeNodes reference)
|
|
464
|
+
const node = treeNodes.nodeMap.get(nodeId);
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
### Q: How to iterate over child nodes?
|
|
468
|
+
|
|
469
|
+
```typescript
|
|
470
|
+
// children is readonly, but you can iterate over it
|
|
471
|
+
node.children.forEach(child => {
|
|
472
|
+
console.log(child.item.name);
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
// Or use for...of
|
|
476
|
+
for (const child of node.children) {
|
|
477
|
+
console.log(child.item.name);
|
|
478
|
+
}
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
### Q: How to find a specific child node?
|
|
482
|
+
|
|
483
|
+
```typescript
|
|
484
|
+
function findChild(node: TreeNode, childId: number) {
|
|
485
|
+
return node.children.find(child => child.item.id === childId) || null;
|
|
486
|
+
}
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
### Q: Can I modify the children array directly?
|
|
490
|
+
|
|
491
|
+
No. The `children` property is read-only. Use the provided methods instead:
|
|
492
|
+
- `append()` to add
|
|
493
|
+
- `removeChild()` to remove specific child
|
|
494
|
+
- `removeChildren()` to remove all
|
|
495
|
+
|
|
496
|
+
### Q: How to recursively operate on all descendants?
|
|
497
|
+
|
|
498
|
+
```typescript
|
|
499
|
+
function traverse(node: TreeNode, callback: (node: TreeNode) => void) {
|
|
500
|
+
callback(node);
|
|
501
|
+
for (const child of node.children) {
|
|
502
|
+
traverse(child, callback);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// Usage
|
|
507
|
+
traverse(rootNode, (node) => {
|
|
508
|
+
console.log(node.item.name);
|
|
509
|
+
node.expand = true; // Expand all nodes
|
|
510
|
+
});
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
### Q: What's the difference between TreeNode and NodeViewOptions?
|
|
514
|
+
|
|
515
|
+
- **TreeNode**: Represents the data structure. Each node is an instance with methods for manipulation.
|
|
516
|
+
- **NodeViewOptions**: Configuration class for how nodes should be displayed (text field, icon, CSS, etc.)
|
|
517
|
+
|
|
518
|
+
This separation allows TreeNode to be used in different contexts (TreeView, TreeDataGrid, etc.) without coupling to view-specific concerns.
|
|
519
|
+
|
|
520
|
+
## Best Practices
|
|
521
|
+
|
|
522
|
+
1. **Prefer node methods**: When you have a node instance, calling node methods directly is more intuitive
|
|
523
|
+
|
|
524
|
+
2. **Keep ID unchanged**: When using `replace()`, ensure the `id` field remains unchanged
|
|
525
|
+
|
|
526
|
+
3. **Use moveTo correctly**: The `moveTo()` method takes a TreeNode instance, not an ID:
|
|
527
|
+
```typescript
|
|
528
|
+
// ❌ Wrong
|
|
529
|
+
node.moveTo(123);
|
|
530
|
+
|
|
531
|
+
// ✅ Correct
|
|
532
|
+
const parentNode = treeNodes.nodeMap.get(123);
|
|
533
|
+
node.moveTo(parentNode);
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
4. **Version management**: No need to manually manage versions, node methods update automatically
|
|
537
|
+
|
|
538
|
+
5. **Type safety**: Use TypeScript generics to ensure type safety
|
|
539
|
+
|
|
540
|
+
```typescript
|
|
541
|
+
// ✅ Recommended: Use generics
|
|
542
|
+
const treeNodes = new CommonTreeNodes<MyData>({ ... });
|
|
543
|
+
|
|
544
|
+
// Node type is automatically inferred
|
|
545
|
+
node.append({ id: 1, name: "..." }); // Type checking
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
6. **Children is readonly**: Don't try to modify the children array directly. Always use the provided methods.
|
|
549
|
+
|
|
550
|
+
## Performance Considerations
|
|
551
|
+
|
|
552
|
+
- All node methods have O(1) or O(n) time complexity, where n is the number of child nodes
|
|
553
|
+
- Delete operations also remove from internal map to maintain consistency
|
|
554
|
+
- For large batch operations, consider using `setData()` to rebuild the tree structure
|
|
555
|
+
|
|
556
|
+
## Architecture
|
|
557
|
+
|
|
558
|
+
### Decoupling from View
|
|
559
|
+
|
|
560
|
+
TreeNode is designed to be independent of any specific view component:
|
|
561
|
+
- No view-specific logic in TreeNode
|
|
562
|
+
- NodeViewOptions handles view configuration separately
|
|
563
|
+
- Can be used in TreeView, TreeDataGrid, or any other tree-based component
|
|
564
|
+
|
|
565
|
+
### Data Immutability
|
|
566
|
+
|
|
567
|
+
The `item` property is readonly to ensure data integrity:
|
|
568
|
+
- Prevents accidental mutations
|
|
569
|
+
- Makes state changes explicit through `replace()` method
|
|
570
|
+
- Helps with debugging and state tracking
|
|
571
|
+
|
|
572
|
+
## Related Components
|
|
573
|
+
|
|
574
|
+
- `CommonTreeNodes` - Base tree management class
|
|
575
|
+
- `TreeNodes` - Extended tree management with hierarchy features
|
|
576
|
+
- `TreeView` - Tree view component
|
|
577
|
+
- `NodeViewOptions` - View configuration class
|