@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.
@@ -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