@ticatec/uniface-element 0.3.16 → 0.3.18

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.
@@ -2,52 +2,88 @@
2
2
 
3
3
  ## Overview
4
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.
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
6
 
7
- Every node obtained from `TreeNodes` automatically has bound operation methods that can be called directly.
7
+ Each node created from `TreeNodes` or `CommonTreeNodes` is an instance of the `TreeNode` class with built-in methods.
8
8
 
9
- ## Type Definition
9
+ ## TreeNode Class
10
10
 
11
11
  ```typescript
12
- interface TreeNode<T> {
13
- /** The node's data object */
14
- item: T;
12
+ class TreeNode<T> {
13
+ /** The node's data object (readonly) */
14
+ public readonly item: T;
15
15
 
16
16
  /** Whether the node is expanded (showing children) */
17
- expand?: boolean;
17
+ public expand: boolean;
18
18
 
19
- /** Array of child nodes */
20
- children?: TreeNode<T>[];
21
- }
22
- ```
19
+ /** Whether the node is currently loading (for lazy loading) */
20
+ public loading: boolean;
23
21
 
24
- ### TreeNodeWithMethods<T>
22
+ /** Parent node (null for root nodes) */
23
+ public parent: TreeNode<T> | null;
25
24
 
26
- This is the actual node type in use, extending `TreeNode<T>` with operation methods:
25
+ /** Child nodes (readonly, use methods to modify) */
26
+ get children(): ReadonlyArray<TreeNode<T>>;
27
27
 
28
- ```typescript
29
- interface TreeNodeWithMethods<T> extends TreeNode<T> {
30
28
  /** Add a child node to the current node */
31
- append: (childItem: T) => void;
29
+ append(childItem: T): void;
32
30
 
33
31
  /** Remove the current node (from its parent) */
34
- remove: () => void;
35
-
36
- /** Replace the current node's data */
37
- replace: (newItem: T) => void;
32
+ detach(): void;
38
33
 
39
34
  /** Move the current node to a different parent */
40
- moveTo: (newParentId: string) => void;
35
+ moveTo(newParent: TreeNode<T>): void;
36
+
37
+ /** Replace the current node's data */
38
+ replace(newItem: T): void;
41
39
 
42
- /** Remove a specific child node */
43
- removeChild: (childId: any) => void;
40
+ /** Remove a specific child node by ID */
41
+ removeChild(childId: any): void;
44
42
 
45
43
  /** Remove all child nodes */
46
- removeChildren: () => void;
44
+ removeChildren(): void;
47
45
  }
48
46
  ```
49
47
 
50
- ## Node Methods
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
51
87
 
52
88
  ### 1. append(childItem)
53
89
 
@@ -62,33 +98,31 @@ Add a child node to the current node.
62
98
  parentNode.append({
63
99
  id: 123,
64
100
  name: "New Child",
65
- parent: parentNode.item.id // Optional, will be set automatically
101
+ parentId: parentNode.item.id // Will be set automatically
66
102
  });
67
103
  ```
68
104
 
69
105
  **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
106
+ - Automatically initializes children array if needed
74
107
  - If a sort function is configured, nodes will be automatically sorted
75
108
  - The parent's `expand` is automatically set to `true`
109
+ - Triggers version update for reactive updates
76
110
 
77
111
  ---
78
112
 
79
- ### 2. remove()
113
+ ### 2. detach()
80
114
 
81
- Remove the current node (from its parent).
115
+ Remove the current node from its parent.
82
116
 
83
117
  **Parameter**: None
84
118
 
85
119
  **Example**:
86
120
  ```typescript
87
- // Remove the node
88
- node.remove();
121
+ // Remove the node from its parent
122
+ node.detach();
89
123
 
90
124
  // Clear reference after removal
91
- activeNode.remove();
125
+ activeNode.detach();
92
126
  activeNode = null;
93
127
  ```
94
128
 
@@ -96,10 +130,40 @@ activeNode = null;
96
130
  - Removes from parent's `children` array
97
131
  - Deletes from internal map
98
132
  - If it's a root node, removes from root node list
133
+ - Triggers version update for reactive updates
99
134
 
100
135
  ---
101
136
 
102
- ### 3. replace(newItem)
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)
103
167
 
104
168
  Replace the current node's data.
105
169
 
@@ -127,32 +191,7 @@ node.replace({
127
191
  - Usually need to keep the `id` field unchanged
128
192
  - If a sort function is configured and sort field changes, will automatically re-sort
129
193
  - 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
194
+ - Preserves children, expand state, and loading state
156
195
 
157
196
  ---
158
197
 
@@ -177,6 +216,7 @@ parentNode.removeChild(childId);
177
216
  - Only removes direct children, not grandchildren recursively
178
217
  - Silently ignores if child doesn't exist
179
218
  - All descendants of the child node are also removed
219
+ - Triggers version update for reactive updates
180
220
 
181
221
  ---
182
222
 
@@ -200,37 +240,68 @@ parentNode.append(newChildData);
200
240
  - Removes all children and their descendants
201
241
  - Node's `expand` state remains unchanged
202
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
+ ```
203
273
 
204
274
  ## Complete Usage Example
205
275
 
206
276
  ```svelte
207
277
  <script>
208
278
  import TreeView from "@ticatec/uniface-element/TreeView";
209
- import TreeNodes, { type TreeNode } from "@ticatec/uniface-element/TreeNodes";
279
+ import { CommonTreeNodes, type TreeNode } from "@ticatec/uniface-element/lib/TreeNodes";
210
280
 
211
281
  // Data type definition
212
282
  interface MyData {
213
283
  id: number;
214
284
  name: string;
215
- parent: number | null;
285
+ parentId: number | null;
216
286
  }
217
287
 
218
- let activeNode: TreeNodeWithMethods<MyData> | null = null;
288
+ let activeNode: TreeNode<MyData> | null = null;
219
289
 
220
290
  // Create tree structure
221
- const treeNodes = new TreeNodes<MyData>({
291
+ const treeNodes = new CommonTreeNodes<MyData>({
222
292
  keyField: 'id',
223
293
  textField: 'name',
224
- parentKeyField: 'parent',
225
- checkIsRoot: (item) => item.parent === null,
226
- checkIsDirectory: (node) => node.children != null
294
+ parentKeyField: 'parentId',
295
+ checkIsRoot: (item) => item.parentId === null,
296
+ checkIsDirectory: (node) => node.children.length > 0,
297
+ expendDepth: 2
227
298
  });
228
299
 
229
300
  // Initialize data
230
301
  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 }
302
+ { id: 1, name: "Root", parentId: null },
303
+ { id: 2, name: "Child 1", parentId: 1 },
304
+ { id: 3, name: "Child 2", parentId: 1 }
234
305
  ]);
235
306
 
236
307
  // Add child node
@@ -240,14 +311,14 @@ parentNode.append(newChildData);
240
311
  activeNode.append({
241
312
  id: Date.now(),
242
313
  name: "New Child",
243
- parent: activeNode.item.id
314
+ parentId: activeNode.item.id
244
315
  });
245
316
  }
246
317
 
247
318
  // Delete current node
248
319
  function deleteCurrentNode() {
249
320
  if (!activeNode) return;
250
- activeNode.remove();
321
+ activeNode.detach();
251
322
  activeNode = null;
252
323
  }
253
324
 
@@ -275,7 +346,10 @@ parentNode.append(newChildData);
275
346
  // Move node
276
347
  function moveNodeTo(newParentId: number) {
277
348
  if (!activeNode) return;
278
- activeNode.moveTo(newParentId);
349
+ const newParent = treeNodes.nodeMap.get(newParentId);
350
+ if (newParent) {
351
+ activeNode.moveTo(newParent);
352
+ }
279
353
  }
280
354
  </script>
281
355
 
@@ -285,7 +359,7 @@ parentNode.append(newChildData);
285
359
  version={treeNodes.version}
286
360
  textField="name"
287
361
  bind:activeNode
288
- checkIsDirectory={(node) => node.children != null}
362
+ checkIsDirectory={(node) => node.children.length > 0}
289
363
  />
290
364
 
291
365
  {#if activeNode}
@@ -303,30 +377,64 @@ parentNode.append(newChildData);
303
377
 
304
378
  ## Difference from TreeNodes Class Methods
305
379
 
306
- `TreeNode` methods and `TreeNodes` class methods have clear responsibilities:
380
+ `TreeNode` instance methods and `CommonTreeNodes`/`TreeNodes` class methods have clear responsibilities:
307
381
 
308
- ### TreeNode Node Methods (Recommended for Operating on Specific Nodes)
382
+ ### TreeNode Instance Methods (For Operating on Specific Node Instances)
309
383
 
310
384
  ```typescript
311
- // Operate directly on node - Recommended when you have a node instance
385
+ // Operate directly on a node instance
312
386
  node.append(childItem); // Add child to this node
313
- node.remove(); // Remove this node
387
+ node.detach(); // Remove this node from its parent
314
388
  node.replace(newItem); // Replace this node's data
315
- node.moveTo(newParentId); // Move this node
389
+ node.moveTo(newParentNode); // Move this node to another parent
316
390
  node.removeChild(childId); // Remove specific child
317
391
  node.removeChildren(); // Remove all children
318
392
  ```
319
393
 
320
- ### TreeNodes Class Methods (For Batch Operations or Initialization)
394
+ ### CommonTreeNodes/TreeNodes Class Methods (For Tree-Level Operations)
321
395
 
322
396
  ```typescript
323
- // Class-level operations - Suitable when you only have data
397
+ // Tree-level operations
324
398
  treeNodes.setData(data); // Initialize tree structure
325
399
  treeNodes.append(item); // Add new node (auto-find parent)
326
400
  treeNodes.nodes; // Get root node list
327
401
  treeNodes.version; // Get version number
328
- treeNodes.getHierarchyList(); // Get expanded node list
329
- treeNodes.extractDirectories(item); // Extract directory structure
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
+ }
330
438
  ```
331
439
 
332
440
  ## Reactive Updates
@@ -359,10 +467,14 @@ const node = treeNodes.nodeMap.get(nodeId);
359
467
  ### Q: How to iterate over child nodes?
360
468
 
361
469
  ```typescript
362
- if (node.children) {
363
- node.children.forEach(child => {
364
- console.log(child.item.name);
365
- });
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);
366
478
  }
367
479
  ```
368
480
 
@@ -370,40 +482,24 @@ if (node.children) {
370
482
 
371
483
  ```typescript
372
484
  function findChild(node: TreeNode, childId: number) {
373
- if (!node.children) return null;
374
485
  return node.children.find(child => child.item.id === childId) || null;
375
486
  }
376
487
  ```
377
488
 
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
489
+ ### Q: Can I modify the children array directly?
394
490
 
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
- ```
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
399
495
 
400
496
  ### Q: How to recursively operate on all descendants?
401
497
 
402
498
  ```typescript
403
499
  function traverse(node: TreeNode, callback: (node: TreeNode) => void) {
404
500
  callback(node);
405
- if (node.children) {
406
- node.children.forEach(child => traverse(child, callback));
501
+ for (const child of node.children) {
502
+ traverse(child, callback);
407
503
  }
408
504
  }
409
505
 
@@ -414,36 +510,68 @@ traverse(rootNode, (node) => {
414
510
  });
415
511
  ```
416
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
+
417
520
  ## Best Practices
418
521
 
419
522
  1. **Prefer node methods**: When you have a node instance, calling node methods directly is more intuitive
420
523
 
421
524
  2. **Keep ID unchanged**: When using `replace()`, ensure the `id` field remains unchanged
422
525
 
423
- 3. **Check children existence**: Before operating on child nodes, check if `node.children` exists first
526
+ 3. **Use moveTo correctly**: The `moveTo()` method takes a TreeNode instance, not an ID:
527
+ ```typescript
528
+ // ❌ Wrong
529
+ node.moveTo(123);
424
530
 
425
- 4. **Handle lazy nodes**: For lazy-loaded nodes, wait for loading to complete before operations
531
+ // Correct
532
+ const parentNode = treeNodes.nodeMap.get(123);
533
+ node.moveTo(parentNode);
534
+ ```
426
535
 
427
- 5. **Version management**: No need to manually manage versions, node methods update automatically
536
+ 4. **Version management**: No need to manually manage versions, node methods update automatically
428
537
 
429
- 6. **Type safety**: Use TypeScript generics to ensure type safety
538
+ 5. **Type safety**: Use TypeScript generics to ensure type safety
430
539
 
431
540
  ```typescript
432
541
  // ✅ Recommended: Use generics
433
- const treeNodes = new TreeNodes<MyData>({ ... });
542
+ const treeNodes = new CommonTreeNodes<MyData>({ ... });
434
543
 
435
544
  // Node type is automatically inferred
436
545
  node.append({ id: 1, name: "..." }); // Type checking
437
546
  ```
438
547
 
548
+ 6. **Children is readonly**: Don't try to modify the children array directly. Always use the provided methods.
549
+
439
550
  ## Performance Considerations
440
551
 
441
552
  - All node methods have O(1) or O(n) time complexity, where n is the number of child nodes
442
553
  - Delete operations also remove from internal map to maintain consistency
443
554
  - For large batch operations, consider using `setData()` to rebuild the tree structure
444
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
+
445
572
  ## Related Components
446
573
 
447
- - `TreeNodes` - Tree management class
574
+ - `CommonTreeNodes` - Base tree management class
575
+ - `TreeNodes` - Extended tree management with hierarchy features
448
576
  - `TreeView` - Tree view component
449
- - `LazyLoader` - Lazy loading interface
577
+ - `NodeViewOptions` - View configuration class