@ticatec/uniface-element 0.2.0 → 0.2.2

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 (209) hide show
  1. package/README.md +320 -1
  2. package/README_CN.md +392 -0
  3. package/dist/accordion/Accordion.svelte +1 -1
  4. package/dist/action-bar/ActionBar.svelte +7 -1
  5. package/dist/attachment-files/FileRender.svelte +1 -1
  6. package/dist/attachment-files/FileUploadBar.svelte +7 -6
  7. package/dist/attachment-files/FileUploadPanel.svelte +2 -2
  8. package/dist/base-calendar/DateContext.js +5 -4
  9. package/dist/base-calendar/YearPickupPanel.svelte +4 -4
  10. package/dist/card/Card.svelte +1 -1
  11. package/dist/card/Card.svelte.d.ts +1 -1
  12. package/dist/cascade-options-select/CascadeOptionSelect.svelte +2 -3
  13. package/dist/cascade-options-select/CascadeOptionSelect.svelte.d.ts +46 -1
  14. package/dist/cascade-options-select/CascadePanel.svelte +1 -1
  15. package/dist/checkbox/CheckBox.svelte +1 -1
  16. package/dist/color-picker/ColorPicker.svelte +9 -9
  17. package/dist/color-picker/ColorPicker.svelte.d.ts +0 -2
  18. package/dist/color-picker/ColorPickerPanel.svelte +11 -15
  19. package/dist/color-picker/ColorPickerPanel.svelte.d.ts +1 -1
  20. package/dist/common/CommonPicker.svelte +2 -2
  21. package/dist/common/CommonPicker.svelte.d.ts +1 -1
  22. package/dist/common/MouseClickHandler.d.ts +1 -1
  23. package/dist/common/Popover.svelte +28 -6
  24. package/dist/common/Popover.svelte.d.ts +9 -3
  25. package/dist/common/uniface-utils.js +6 -4
  26. package/dist/common-editor/CommonEditor.svelte +1 -1
  27. package/dist/common-editor/CommonEditor.svelte.d.ts +1 -1
  28. package/dist/common-editor/NumberInput.svelte +1 -1
  29. package/dist/common-editor/NumberInput.svelte.d.ts +1 -1
  30. package/dist/concise-data-table/ConciseListTable.svelte +7 -2
  31. package/dist/concise-data-table/ConciseListTable.svelte.d.ts +1 -0
  32. package/dist/concise-data-table/DataRow.svelte +1 -1
  33. package/dist/concise-data-table/TableOptions.d.ts +1 -1
  34. package/dist/data-table/DataTable.svelte +1 -1
  35. package/dist/data-table/parts/ActionsPanel.svelte +4 -3
  36. package/dist/data-table/parts/ActionsRow.svelte +3 -3
  37. package/dist/data-table/parts/ContentPanel.svelte +3 -2
  38. package/dist/data-table/parts/FixedColumnsPanel.svelte +0 -1
  39. package/dist/data-table/parts/FixedColumnsPanel.svelte.d.ts +1 -2
  40. package/dist/data-table/parts/FixedHeaderPanel.svelte +2 -2
  41. package/dist/data-table/parts/FixedRow.svelte +2 -1
  42. package/dist/data-table/parts/FixedRow.svelte.d.ts +1 -1
  43. package/dist/data-table/parts/HrefCell.svelte +23 -2
  44. package/dist/data-table/parts/PopupMenu.svelte +25 -2
  45. package/dist/data-table/parts/PopupMenu.svelte.d.ts +8 -1
  46. package/dist/date-picker/DatePicker.svelte +0 -1
  47. package/dist/date-picker/DateTimePicker.svelte +3 -3
  48. package/dist/date-picker/ScrollBar.svelte +4 -7
  49. package/dist/date-range/DateRangeEditor.svelte +1 -1
  50. package/dist/dialog/CommonDialog.svelte +8 -5
  51. package/dist/dialog/CommonDialog.svelte.d.ts +2 -1
  52. package/dist/dialog/Dialog.svelte +37 -12
  53. package/dist/dialog/Dialog.svelte.d.ts +4 -3
  54. package/dist/dialog/DialogBoard.svelte +11 -8
  55. package/dist/dialog/DialogWrapper.svelte +10 -0
  56. package/dist/{common-editor/DateInput.svelte.d.ts → dialog/DialogWrapper.svelte.d.ts} +6 -13
  57. package/dist/dialog/Dialogs.js +1 -1
  58. package/dist/dialog/ModalResult.d.ts +5 -0
  59. package/dist/dialog/ModalResult.js +5 -0
  60. package/dist/drawer/Drawer.svelte +1 -1
  61. package/dist/form-field/FormField.svelte +2 -1
  62. package/dist/form-field/FormField.svelte.d.ts +1 -0
  63. package/dist/form-panel/flex-row-form/FormContainer.svelte +1 -1
  64. package/dist/form-panel/grid-form/EmptyCell.svelte +8 -7
  65. package/dist/form-panel/grid-form/EmptyCell.svelte.d.ts +2 -2
  66. package/dist/form-panel/grid-form/GridField.svelte +1 -1
  67. package/dist/i18nRes/i18nRes.d.ts +2 -0
  68. package/dist/i18nRes/i18nRes.js +44 -0
  69. package/dist/i18nRes/index.d.ts +2 -0
  70. package/dist/i18nRes/index.js +2 -0
  71. package/dist/image-files/ImageFilesField.svelte +1 -1
  72. package/dist/indicator/IndicatorBoard.svelte +1 -1
  73. package/dist/inline-cell-editor/InvalidEditor.svelte +1 -0
  74. package/dist/input-options-select/InputOptionsSelect.svelte +6 -6
  75. package/dist/input-options-select/InputOptionsSelect.svelte.d.ts +1 -1
  76. package/dist/layout/flex/FlexBlock.svelte +1 -1
  77. package/dist/lookup-editor/LookupEditor.svelte +1 -1
  78. package/dist/memo-editor/MemoEditor.svelte +2 -2
  79. package/dist/message-box/IMessageBox.d.ts +2 -4
  80. package/dist/message-box/IMessageBox.js +4 -6
  81. package/dist/message-box/MessageBoxBoard.svelte +1 -3
  82. package/dist/options-multi-select/OptionsMultiSelect.svelte +1 -1
  83. package/dist/prompts-text-editor/PromptsTextEditor.svelte +2 -2
  84. package/dist/prompts-text-editor/PromptsTextEditor.svelte.d.ts +1 -1
  85. package/dist/property-editor/PropertyEditor.svelte +3 -3
  86. package/dist/text-editor/PasswordEditor.svelte +1 -1
  87. package/dist/transfer/Transfer.svelte +17 -12
  88. package/dist/transfer/Transfer.svelte.d.ts +0 -1
  89. package/dist/unit-number-editor/UnitNumberEditor.svelte +2 -2
  90. package/docs/buttons/cn/ActionBar.md +459 -0
  91. package/docs/buttons/cn/Button.md +228 -0
  92. package/docs/buttons/cn/IconButton.md +294 -0
  93. package/docs/buttons/cn/README.md +600 -0
  94. package/docs/buttons/cn/TextButton.md +163 -0
  95. package/docs/buttons/en/ActionBar.md +459 -0
  96. package/docs/buttons/en/Button.md +228 -0
  97. package/docs/buttons/en/IconButton.md +294 -0
  98. package/docs/buttons/en/README.md +600 -0
  99. package/docs/buttons/en/TextButton.md +163 -0
  100. package/docs/containers/cn/accordion.md +622 -0
  101. package/docs/containers/cn/box.md +445 -0
  102. package/docs/containers/cn/card.md +404 -0
  103. package/docs/containers/cn/page.md +576 -0
  104. package/docs/containers/cn/tabs.md +686 -0
  105. package/docs/containers/en/accordion.md +622 -0
  106. package/docs/containers/en/box.md +445 -0
  107. package/docs/containers/en/card.md +404 -0
  108. package/docs/containers/en/page.md +576 -0
  109. package/docs/containers/en/tabs.md +686 -0
  110. package/docs/data-display/cn/concise-data-table.md +731 -0
  111. package/docs/data-display/cn/listbox.md +786 -0
  112. package/docs/data-display/cn/treeview.md +863 -0
  113. package/docs/data-display/en/concise-data-table.md +732 -0
  114. package/docs/data-display/en/listbox.md +786 -0
  115. package/docs/data-display/en/treeview.md +863 -0
  116. package/docs/data-table/cn/README.md +596 -0
  117. package/docs/data-table/en/README.md +596 -0
  118. package/docs/dialog/cn/README.md +767 -0
  119. package/docs/dialog/en/README.md +767 -0
  120. package/docs/form/cn/README.md +847 -0
  121. package/docs/form/en/README.md +847 -0
  122. package/docs/form-components/cn/README.md +495 -0
  123. package/docs/form-components/cn/attachmentfilesfield.md +757 -0
  124. package/docs/form-components/cn/cascade-options-select.md +374 -0
  125. package/docs/form-components/cn/checkbox.md +58 -0
  126. package/docs/form-components/cn/date-picker.md +138 -0
  127. package/docs/form-components/cn/datetime-picker.md +228 -0
  128. package/docs/form-components/cn/group-checkbox.md +405 -0
  129. package/docs/form-components/cn/group-radiobox.md +525 -0
  130. package/docs/form-components/cn/imagefilesfield.md +651 -0
  131. package/docs/form-components/cn/input-options-select.md +250 -0
  132. package/docs/form-components/cn/lookup-editor.md +406 -0
  133. package/docs/form-components/cn/memoeditor.md +409 -0
  134. package/docs/form-components/cn/number-editor.md +261 -0
  135. package/docs/form-components/cn/options-multi-select.md +177 -0
  136. package/docs/form-components/cn/options-select.md +83 -0
  137. package/docs/form-components/cn/passwordeditor.md +538 -0
  138. package/docs/form-components/cn/prompts-text-editor.md +241 -0
  139. package/docs/form-components/cn/radiobutton.md +84 -0
  140. package/docs/form-components/cn/switch.md +178 -0
  141. package/docs/form-components/cn/text-editor.md +142 -0
  142. package/docs/form-components/cn/time-editor.md +159 -0
  143. package/docs/form-components/cn/unit-number-editor.md +164 -0
  144. package/docs/form-components/en/README.md +463 -0
  145. package/docs/form-components/en/attachmentfilesfield.md +757 -0
  146. package/docs/form-components/en/cascade-options-select.md +358 -0
  147. package/docs/form-components/en/checkbox.md +59 -0
  148. package/docs/form-components/en/date-picker.md +138 -0
  149. package/docs/form-components/en/datetime-picker.md +228 -0
  150. package/docs/form-components/en/group-checkbox.md +395 -0
  151. package/docs/form-components/en/group-radiobox.md +498 -0
  152. package/docs/form-components/en/imagefilesfield.md +651 -0
  153. package/docs/form-components/en/input-options-select.md +239 -0
  154. package/docs/form-components/en/lookup-editor.md +389 -0
  155. package/docs/form-components/en/memoeditor.md +409 -0
  156. package/docs/form-components/en/number-editor.md +249 -0
  157. package/docs/form-components/en/options-multi-select.md +177 -0
  158. package/docs/form-components/en/options-select.md +83 -0
  159. package/docs/form-components/en/passwordeditor.md +538 -0
  160. package/docs/form-components/en/prompts-text-editor.md +241 -0
  161. package/docs/form-components/en/radiobutton.md +84 -0
  162. package/docs/form-components/en/switch.md +180 -0
  163. package/docs/form-components/en/text-editor.md +142 -0
  164. package/docs/form-components/en/time-editor.md +159 -0
  165. package/docs/form-components/en/unit-number-editor.md +164 -0
  166. package/docs/form-layout/cn/criteria-field.md +652 -0
  167. package/docs/form-layout/cn/flex-form.md +454 -0
  168. package/docs/form-layout/cn/flex-row-form.md +542 -0
  169. package/docs/form-layout/cn/form-field.md +923 -0
  170. package/docs/form-layout/cn/grid-form.md +676 -0
  171. package/docs/form-layout/en/criteria-field.md +652 -0
  172. package/docs/form-layout/en/flex-form.md +454 -0
  173. package/docs/form-layout/en/flex-row-form.md +542 -0
  174. package/docs/form-layout/en/form-field.md +923 -0
  175. package/docs/form-layout/en/grid-form.md +676 -0
  176. package/docs/global/cn/README.md +534 -0
  177. package/docs/global/en/README.md +534 -0
  178. package/docs/layout/cn/classiclayout.md +460 -0
  179. package/docs/layout/cn/headerlayout.md +524 -0
  180. package/docs/layout/cn/sidebarlayout.md +263 -0
  181. package/docs/layout/en/classiclayout.md +951 -0
  182. package/docs/layout/en/headerlayout.md +524 -0
  183. package/docs/layout/en/sidebarlayout.md +262 -0
  184. package/docs/misc/cn/README.md +1217 -0
  185. package/docs/misc/en/README.md +1217 -0
  186. package/docs/navigation/cn/breadcrumb.md +291 -0
  187. package/docs/navigation/cn/navigator.md +322 -0
  188. package/docs/navigation/cn/navigatormenu.md +512 -0
  189. package/docs/navigation/cn/progressbar.md +199 -0
  190. package/docs/navigation/cn/progressstepbar.md +299 -0
  191. package/docs/navigation/en/breadcrumb.md +291 -0
  192. package/docs/navigation/en/navigator.md +322 -0
  193. package/docs/navigation/en/navigatormenu.md +512 -0
  194. package/docs/navigation/en/progressbar.md +199 -0
  195. package/docs/navigation/en/progressstepbar.md +299 -0
  196. package/docs/search-filters/cn/daterange.md +1156 -0
  197. package/docs/search-filters/cn/numberrange.md +688 -0
  198. package/docs/search-filters/cn/searchbox.md +1582 -0
  199. package/docs/search-filters/en/daterange.md +1156 -0
  200. package/docs/search-filters/en/numberrange.md +688 -0
  201. package/docs/search-filters/en/searchbox.md +1582 -0
  202. package/package.json +53 -13
  203. package/dist/common-editor/DateInput.svelte +0 -27
  204. package/dist/i18n_resources/index.d.ts +0 -3
  205. package/dist/i18n_resources/index.js +0 -3
  206. /package/dist/{i18n_resources → i18nRes}/uniface_cn_resource.d.ts +0 -0
  207. /package/dist/{i18n_resources → i18nRes}/uniface_cn_resource.js +0 -0
  208. /package/dist/{i18n_resources → i18nRes}/uniface_en_resource.d.ts +0 -0
  209. /package/dist/{i18n_resources → i18nRes}/uniface_en_resource.js +0 -0
@@ -0,0 +1,863 @@
1
+ # TreeView Component
2
+
3
+ A hierarchical data display component that renders tree structures with expandable/collapsible nodes, lazy loading support, and interactive node selection.
4
+
5
+ ## Overview
6
+
7
+ The TreeView component provides a sophisticated interface for displaying hierarchical data structures. It supports node expansion/collapse, lazy loading for large datasets, custom node rendering, context menus, and flexible node selection. Perfect for file systems, organization charts, menu structures, and any hierarchical data visualization.
8
+
9
+ ## Installation
10
+
11
+ ```typescript
12
+ import TreeView from '@ticatec/uniface-element/TreeView';
13
+ import type { LazyLoader, NodeVisibleFun, OnNodeSelectionChange, LoadChildrenFun } from '@ticatec/uniface-element/TreeView';
14
+ import type { TreeNode, CheckIsDirectory, GetText } from '@ticatec/uniface-element/TreeNodes';
15
+ ```
16
+
17
+ ## Basic Usage
18
+
19
+ ```svelte
20
+ <script lang="ts">
21
+ import TreeView from '@ticatec/uniface-element/TreeView';
22
+ import type { TreeNode, CheckIsDirectory } from '@ticatec/uniface-element/TreeNodes';
23
+
24
+ // Sample hierarchical data
25
+ let treeData: TreeNode<any>[] = [
26
+ {
27
+ item: { id: '1', name: 'Root Folder', type: 'folder' },
28
+ expand: true,
29
+ children: [
30
+ {
31
+ item: { id: '2', name: 'Documents', type: 'folder' },
32
+ expand: false,
33
+ children: [
34
+ { item: { id: '3', name: 'file1.txt', type: 'file' } },
35
+ { item: { id: '4', name: 'file2.pdf', type: 'file' } }
36
+ ]
37
+ },
38
+ {
39
+ item: { id: '5', name: 'Images', type: 'folder' },
40
+ expand: false,
41
+ children: [
42
+ { item: { id: '6', name: 'photo.jpg', type: 'file' } }
43
+ ]
44
+ }
45
+ ]
46
+ }
47
+ ];
48
+
49
+ const checkIsDirectory: CheckIsDirectory<any> = (node) => {
50
+ return node.item.type === 'folder';
51
+ };
52
+
53
+ let activeNode = null;
54
+ </script>
55
+
56
+ <TreeView
57
+ nodes={treeData}
58
+ textField="name"
59
+ {checkIsDirectory}
60
+ bind:activeNode
61
+ />
62
+ ```
63
+
64
+ ## Properties
65
+
66
+ | Property | Type | Default | Description |
67
+ |----------|------|---------|-------------|
68
+ | `nodes` | `Array<TreeNode<any>>` | - | Array of tree nodes to display |
69
+ | `textField` | `string \| GetText<any>` | - | Field name or function to get node text |
70
+ | `style` | `string` | `""` | Custom CSS styles for the tree container |
71
+ | `lazyLoader` | `LazyLoader \| null` | `null` | Lazy loading configuration |
72
+ | `activeNode` | `any` | `null` | Currently active/selected node |
73
+ | `class` | `string` | `""` | Additional CSS class names |
74
+ | `isVisible` | `NodeVisibleFun` | `null` | Function to determine node visibility |
75
+ | `onSelectionChange` | `OnNodeSelectionChange` | `null` | Node selection change callback |
76
+ | `onContextMenu` | `OnContextMenu` | `null` | Context menu callback |
77
+ | `checkIsDirectory` | `CheckIsDirectory<any>` | - | Function to check if node is a directory |
78
+
79
+ ## Type Definitions
80
+
81
+ ```typescript
82
+ /**
83
+ * Tree node structure
84
+ */
85
+ export type TreeNode<T> = {
86
+ item: T;
87
+ expand?: boolean;
88
+ children?: TreeNode<T>[];
89
+ };
90
+
91
+ /**
92
+ * Function to check if node is a directory/branch
93
+ */
94
+ export type CheckIsDirectory<T> = (node: TreeNode<T>) => boolean;
95
+
96
+ /**
97
+ * Function to get text from node item
98
+ */
99
+ export type GetText<T> = (data: T) => string;
100
+
101
+ /**
102
+ * Node selection change callback
103
+ */
104
+ export type OnNodeSelectionChange = (node: TreeNode<any>) => Promise<boolean>;
105
+
106
+ /**
107
+ * Node visibility filter function
108
+ */
109
+ export type NodeVisibleFun = (node: TreeNode<any>) => boolean;
110
+
111
+ /**
112
+ * Load children function for lazy loading
113
+ */
114
+ export type LoadChildrenFun = (item: TreeNode<any>) => Promise<Array<TreeNode<any>>>;
115
+
116
+ /**
117
+ * Lazy loader configuration
118
+ */
119
+ export interface LazyLoader {
120
+ /**
121
+ * Check if node is a branch (has children)
122
+ */
123
+ isBranch: CheckIsDirectory<any>;
124
+
125
+ /**
126
+ * Data loader function
127
+ */
128
+ load: LoadChildrenFun;
129
+ }
130
+ ```
131
+
132
+ ## Usage Examples
133
+
134
+ ### File System Tree
135
+
136
+ ```svelte
137
+ <script lang="ts">
138
+ import TreeView from '@ticatec/uniface-element/TreeView';
139
+ import type { TreeNode, CheckIsDirectory } from '@ticatec/uniface-element/TreeNodes';
140
+
141
+ interface FileSystemItem {
142
+ id: string;
143
+ name: string;
144
+ type: 'file' | 'folder';
145
+ size?: number;
146
+ modified?: string;
147
+ }
148
+
149
+ let fileSystem: TreeNode<FileSystemItem>[] = [
150
+ {
151
+ item: { id: 'root', name: 'Project Root', type: 'folder' },
152
+ expand: true,
153
+ children: [
154
+ {
155
+ item: { id: 'src', name: 'src', type: 'folder' },
156
+ expand: true,
157
+ children: [
158
+ {
159
+ item: { id: 'components', name: 'components', type: 'folder' },
160
+ expand: false,
161
+ children: [
162
+ { item: { id: 'header', name: 'Header.svelte', type: 'file', size: 2048 } },
163
+ { item: { id: 'footer', name: 'Footer.svelte', type: 'file', size: 1024 } }
164
+ ]
165
+ },
166
+ { item: { id: 'main', name: 'main.ts', type: 'file', size: 512 } },
167
+ { item: { id: 'app', name: 'App.svelte', type: 'file', size: 3072 } }
168
+ ]
169
+ },
170
+ {
171
+ item: { id: 'public', name: 'public', type: 'folder' },
172
+ expand: false,
173
+ children: [
174
+ { item: { id: 'index', name: 'index.html', type: 'file', size: 1536 } },
175
+ { item: { id: 'favicon', name: 'favicon.ico', type: 'file', size: 256 } }
176
+ ]
177
+ },
178
+ { item: { id: 'package', name: 'package.json', type: 'file', size: 1024 } },
179
+ { item: { id: 'readme', name: 'README.md', type: 'file', size: 2048 } }
180
+ ]
181
+ }
182
+ ];
183
+
184
+ const checkIsDirectory: CheckIsDirectory<FileSystemItem> = (node) => {
185
+ return node.item.type === 'folder';
186
+ };
187
+
188
+ let selectedNode: TreeNode<FileSystemItem> | null = null;
189
+
190
+ async function handleNodeSelection(node: TreeNode<FileSystemItem>): Promise<boolean> {
191
+ console.log('Selected node:', node.item.name);
192
+ selectedNode = node;
193
+ return true; // Allow selection
194
+ }
195
+ </script>
196
+
197
+ <div class="file-explorer">
198
+ <TreeView
199
+ nodes={fileSystem}
200
+ textField="name"
201
+ checkIsDirectory={checkIsDirectory}
202
+ onSelectionChange={handleNodeSelection}
203
+ style="border: 1px solid #ddd; height: 400px; overflow-y: auto;"
204
+ />
205
+
206
+ {#if selectedNode}
207
+ <div class="file-details">
208
+ <h3>Selected: {selectedNode.item.name}</h3>
209
+ <p><strong>Type:</strong> {selectedNode.item.type}</p>
210
+ {#if selectedNode.item.size}
211
+ <p><strong>Size:</strong> {selectedNode.item.size} bytes</p>
212
+ {/if}
213
+ </div>
214
+ {/if}
215
+ </div>
216
+
217
+ <style>
218
+ .file-explorer {
219
+ display: flex;
220
+ gap: 20px;
221
+ }
222
+
223
+ .file-details {
224
+ padding: 16px;
225
+ background: #f8f9fa;
226
+ border-radius: 4px;
227
+ min-width: 200px;
228
+ }
229
+ </style>
230
+ ```
231
+
232
+ ### Organization Chart
233
+
234
+ ```svelte
235
+ <script lang="ts">
236
+ import TreeView from '@ticatec/uniface-element/TreeView';
237
+ import type { TreeNode, CheckIsDirectory, GetText } from '@ticatec/uniface-element/TreeNodes';
238
+
239
+ interface Employee {
240
+ id: string;
241
+ name: string;
242
+ position: string;
243
+ department: string;
244
+ hasSubordinates: boolean;
245
+ }
246
+
247
+ let organizationChart: TreeNode<Employee>[] = [
248
+ {
249
+ item: {
250
+ id: 'ceo',
251
+ name: 'John Smith',
252
+ position: 'CEO',
253
+ department: 'Executive',
254
+ hasSubordinates: true
255
+ },
256
+ expand: true,
257
+ children: [
258
+ {
259
+ item: {
260
+ id: 'cto',
261
+ name: 'Sarah Johnson',
262
+ position: 'CTO',
263
+ department: 'Technology',
264
+ hasSubordinates: true
265
+ },
266
+ expand: true,
267
+ children: [
268
+ {
269
+ item: {
270
+ id: 'dev-manager',
271
+ name: 'Mike Wilson',
272
+ position: 'Development Manager',
273
+ department: 'Engineering',
274
+ hasSubordinates: true
275
+ },
276
+ expand: false,
277
+ children: [
278
+ { item: { id: 'dev1', name: 'Alice Brown', position: 'Senior Developer', department: 'Engineering', hasSubordinates: false } },
279
+ { item: { id: 'dev2', name: 'Bob Davis', position: 'Developer', department: 'Engineering', hasSubordinates: false } }
280
+ ]
281
+ },
282
+ { item: { id: 'qa', name: 'Emily Chen', position: 'QA Lead', department: 'Quality Assurance', hasSubordinates: false } }
283
+ ]
284
+ },
285
+ {
286
+ item: {
287
+ id: 'cfo',
288
+ name: 'David Rodriguez',
289
+ position: 'CFO',
290
+ department: 'Finance',
291
+ hasSubordinates: true
292
+ },
293
+ expand: false,
294
+ children: [
295
+ { item: { id: 'accountant', name: 'Lisa Wang', position: 'Senior Accountant', department: 'Finance', hasSubordinates: false } }
296
+ ]
297
+ }
298
+ ]
299
+ }
300
+ ];
301
+
302
+ const checkIsDirectory: CheckIsDirectory<Employee> = (node) => {
303
+ return node.item.hasSubordinates;
304
+ };
305
+
306
+ const getEmployeeText: GetText<Employee> = (employee) => {
307
+ return `${employee.name} - ${employee.position}`;
308
+ };
309
+
310
+ let selectedEmployee: TreeNode<Employee> | null = null;
311
+ </script>
312
+
313
+ <div class="org-chart">
314
+ <h2>Organization Chart</h2>
315
+
316
+ <TreeView
317
+ nodes={organizationChart}
318
+ textField={getEmployeeText}
319
+ checkIsDirectory={checkIsDirectory}
320
+ bind:activeNode={selectedEmployee}
321
+ style="background: white; border: 1px solid #e0e0e0; padding: 16px; border-radius: 8px;"
322
+ />
323
+
324
+ {#if selectedEmployee}
325
+ <div class="employee-card">
326
+ <div class="employee-header">
327
+ <h3>{selectedEmployee.item.name}</h3>
328
+ <span class="position">{selectedEmployee.item.position}</span>
329
+ </div>
330
+ <div class="employee-details">
331
+ <p><strong>Department:</strong> {selectedEmployee.item.department}</p>
332
+ <p><strong>ID:</strong> {selectedEmployee.item.id}</p>
333
+ <p><strong>Has Team:</strong> {selectedEmployee.item.hasSubordinates ? 'Yes' : 'No'}</p>
334
+ </div>
335
+ </div>
336
+ {/if}
337
+ </div>
338
+
339
+ <style>
340
+ .org-chart {
341
+ max-width: 800px;
342
+ margin: 0 auto;
343
+ }
344
+
345
+ .employee-card {
346
+ margin-top: 20px;
347
+ padding: 20px;
348
+ background: #f8f9fa;
349
+ border-radius: 8px;
350
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
351
+ }
352
+
353
+ .employee-header {
354
+ margin-bottom: 12px;
355
+ }
356
+
357
+ .employee-header h3 {
358
+ margin: 0 0 4px 0;
359
+ color: #333;
360
+ }
361
+
362
+ .position {
363
+ background: #007bff;
364
+ color: white;
365
+ padding: 4px 8px;
366
+ border-radius: 4px;
367
+ font-size: 0.875rem;
368
+ }
369
+
370
+ .employee-details p {
371
+ margin: 8px 0;
372
+ color: #555;
373
+ }
374
+ </style>
375
+ ```
376
+
377
+ ### Lazy Loading Tree
378
+
379
+ ```svelte
380
+ <script lang="ts">
381
+ import TreeView from '@ticatec/uniface-element/TreeView';
382
+ import type { TreeNode, CheckIsDirectory } from '@ticatec/uniface-element/TreeNodes';
383
+ import type { LazyLoader, LoadChildrenFun } from '@ticatec/uniface-element/TreeView';
384
+
385
+ interface ApiNode {
386
+ id: string;
387
+ name: string;
388
+ type: 'folder' | 'file';
389
+ hasChildren?: boolean;
390
+ }
391
+
392
+ // Initial root nodes
393
+ let initialNodes: TreeNode<ApiNode>[] = [
394
+ {
395
+ item: { id: 'api', name: 'API Endpoints', type: 'folder', hasChildren: true },
396
+ expand: false
397
+ },
398
+ {
399
+ item: { id: 'components', name: 'Components', type: 'folder', hasChildren: true },
400
+ expand: false
401
+ }
402
+ ];
403
+
404
+ const checkIsDirectory: CheckIsDirectory<ApiNode> = (node) => {
405
+ return node.item.type === 'folder' && node.item.hasChildren;
406
+ };
407
+
408
+ // Simulate API call for loading children
409
+ const loadChildren: LoadChildrenFun = async (parentNode) => {
410
+ console.log('Loading children for:', parentNode.item.name);
411
+
412
+ // Simulate network delay
413
+ await new Promise(resolve => setTimeout(resolve, 1000));
414
+
415
+ // Mock data based on parent node
416
+ if (parentNode.item.id === 'api') {
417
+ return [
418
+ {
419
+ item: { id: 'users', name: 'Users API', type: 'folder', hasChildren: true }
420
+ },
421
+ {
422
+ item: { id: 'orders', name: 'Orders API', type: 'folder', hasChildren: true }
423
+ },
424
+ {
425
+ item: { id: 'auth', name: 'Authentication', type: 'file' }
426
+ }
427
+ ];
428
+ } else if (parentNode.item.id === 'components') {
429
+ return [
430
+ {
431
+ item: { id: 'ui', name: 'UI Components', type: 'folder', hasChildren: true }
432
+ },
433
+ {
434
+ item: { id: 'forms', name: 'Form Components', type: 'folder', hasChildren: true }
435
+ }
436
+ ];
437
+ } else if (parentNode.item.id === 'users') {
438
+ return [
439
+ { item: { id: 'get-users', name: 'GET /users', type: 'file' } },
440
+ { item: { id: 'post-user', name: 'POST /users', type: 'file' } },
441
+ { item: { id: 'put-user', name: 'PUT /users/:id', type: 'file' } }
442
+ ];
443
+ } else if (parentNode.item.id === 'orders') {
444
+ return [
445
+ { item: { id: 'get-orders', name: 'GET /orders', type: 'file' } },
446
+ { item: { id: 'post-order', name: 'POST /orders', type: 'file' } }
447
+ ];
448
+ }
449
+
450
+ return [];
451
+ };
452
+
453
+ const lazyLoader: LazyLoader = {
454
+ isBranch: checkIsDirectory,
455
+ load: loadChildren
456
+ };
457
+
458
+ let activeNode: TreeNode<ApiNode> | null = null;
459
+ let loadingNodes = new Set();
460
+
461
+ async function handleNodeSelection(node: TreeNode<ApiNode>): Promise<boolean> {
462
+ console.log('Selected:', node.item.name);
463
+ return true;
464
+ }
465
+ </script>
466
+
467
+ <div class="api-explorer">
468
+ <h2>API Explorer (Lazy Loading)</h2>
469
+
470
+ <TreeView
471
+ nodes={initialNodes}
472
+ textField="name"
473
+ checkIsDirectory={checkIsDirectory}
474
+ lazyLoader={lazyLoader}
475
+ onSelectionChange={handleNodeSelection}
476
+ bind:activeNode
477
+ style="height: 500px; border: 1px solid #ddd; overflow-y: auto; padding: 8px;"
478
+ />
479
+
480
+ {#if activeNode}
481
+ <div class="node-info">
482
+ <h3>Selected: {activeNode.item.name}</h3>
483
+ <p><strong>Type:</strong> {activeNode.item.type}</p>
484
+ <p><strong>ID:</strong> {activeNode.item.id}</p>
485
+ {#if activeNode.item.hasChildren}
486
+ <p><strong>Has Children:</strong> Yes</p>
487
+ {/if}
488
+ </div>
489
+ {/if}
490
+ </div>
491
+
492
+ <style>
493
+ .api-explorer {
494
+ max-width: 600px;
495
+ margin: 20px 0;
496
+ }
497
+
498
+ .node-info {
499
+ margin-top: 20px;
500
+ padding: 16px;
501
+ background: #f8f9fa;
502
+ border-radius: 4px;
503
+ }
504
+
505
+ .node-info h3 {
506
+ margin: 0 0 12px 0;
507
+ color: #333;
508
+ }
509
+
510
+ .node-info p {
511
+ margin: 6px 0;
512
+ color: #555;
513
+ }
514
+ </style>
515
+ ```
516
+
517
+ ### Filtered Tree with Context Menu
518
+
519
+ ```svelte
520
+ <script lang="ts">
521
+ import TreeView from '@ticatec/uniface-element/TreeView';
522
+ import type { TreeNode, CheckIsDirectory } from '@ticatec/uniface-element/TreeNodes';
523
+ import type { NodeVisibleFun, OnNodeSelectionChange } from '@ticatec/uniface-element/TreeView';
524
+ import type { OnContextMenu } from '@ticatec/uniface-element/ContextMenu';
525
+
526
+ interface MenuItem {
527
+ id: string;
528
+ label: string;
529
+ icon?: string;
530
+ children?: MenuItem[];
531
+ category: string;
532
+ }
533
+
534
+ let menuStructure: TreeNode<MenuItem>[] = [
535
+ {
536
+ item: { id: 'dashboard', label: 'Dashboard', icon: '📊', category: 'main' },
537
+ expand: true,
538
+ children: [
539
+ { item: { id: 'analytics', label: 'Analytics', icon: '📈', category: 'dashboard' } },
540
+ { item: { id: 'reports', label: 'Reports', icon: '📋', category: 'dashboard' } }
541
+ ]
542
+ },
543
+ {
544
+ item: { id: 'users', label: 'User Management', icon: '👥', category: 'main' },
545
+ expand: false,
546
+ children: [
547
+ { item: { id: 'user-list', label: 'User List', icon: '📝', category: 'users' } },
548
+ { item: { id: 'user-roles', label: 'Roles & Permissions', icon: '🔒', category: 'users' } },
549
+ { item: { id: 'user-settings', label: 'User Settings', icon: '⚙️', category: 'users' } }
550
+ ]
551
+ },
552
+ {
553
+ item: { id: 'content', label: 'Content Management', icon: '📄', category: 'main' },
554
+ expand: false,
555
+ children: [
556
+ { item: { id: 'pages', label: 'Pages', icon: '📖', category: 'content' } },
557
+ { item: { id: 'media', label: 'Media Library', icon: '🖼️', category: 'content' } }
558
+ ]
559
+ },
560
+ {
561
+ item: { id: 'settings', label: 'System Settings', icon: '🔧', category: 'main' },
562
+ expand: false,
563
+ children: [
564
+ { item: { id: 'general', label: 'General', icon: '⚙️', category: 'settings' } },
565
+ { item: { id: 'security', label: 'Security', icon: '🛡️', category: 'settings' } }
566
+ ]
567
+ }
568
+ ];
569
+
570
+ let searchQuery = '';
571
+ let selectedNode: TreeNode<MenuItem> | null = null;
572
+
573
+ const checkIsDirectory: CheckIsDirectory<MenuItem> = (node) => {
574
+ return node.children && node.children.length > 0;
575
+ };
576
+
577
+ // Filter function for search
578
+ const nodeVisibilityFilter: NodeVisibleFun = (node) => {
579
+ if (!searchQuery.trim()) return true;
580
+
581
+ const query = searchQuery.toLowerCase();
582
+ return (
583
+ node.item.label.toLowerCase().includes(query) ||
584
+ node.item.category.toLowerCase().includes(query)
585
+ );
586
+ };
587
+
588
+ // Node selection handler
589
+ const handleNodeSelection: OnNodeSelectionChange = async (node) => {
590
+ selectedNode = node;
591
+ console.log('Selected menu item:', node.item.label);
592
+ return true;
593
+ };
594
+
595
+ // Context menu handler
596
+ const handleContextMenu: OnContextMenu = (event, node) => {
597
+ event.preventDefault();
598
+ console.log('Context menu for:', node.item.label);
599
+
600
+ // You could show a custom context menu here
601
+ const menuItems = [
602
+ { label: 'Edit', action: () => console.log('Edit', node.item.label) },
603
+ { label: 'Delete', action: () => console.log('Delete', node.item.label) },
604
+ { label: 'Duplicate', action: () => console.log('Duplicate', node.item.label) }
605
+ ];
606
+
607
+ // Show context menu at cursor position
608
+ showContextMenu(event.clientX, event.clientY, menuItems);
609
+ };
610
+
611
+ function showContextMenu(x: number, y: number, items: any[]) {
612
+ // Implementation would show a context menu at the specified position
613
+ alert(`Context menu at (${x}, ${y})\nItems: ${items.map(i => i.label).join(', ')}`);
614
+ }
615
+
616
+ function clearSearch() {
617
+ searchQuery = '';
618
+ }
619
+ </script>
620
+
621
+ <div class="menu-manager">
622
+ <h2>Navigation Menu Manager</h2>
623
+
624
+ <div class="search-controls">
625
+ <input
626
+ type="text"
627
+ bind:value={searchQuery}
628
+ placeholder="Search menu items..."
629
+ class="search-input"
630
+ />
631
+ {#if searchQuery}
632
+ <button on:click={clearSearch} class="clear-btn">Clear</button>
633
+ {/if}
634
+ </div>
635
+
636
+ <div class="tree-container">
637
+ <TreeView
638
+ nodes={menuStructure}
639
+ textField="label"
640
+ checkIsDirectory={checkIsDirectory}
641
+ isVisible={nodeVisibilityFilter}
642
+ onSelectionChange={handleNodeSelection}
643
+ onContextMenu={handleContextMenu}
644
+ style="height: 400px; border: 1px solid #ddd; padding: 12px; overflow-y: auto;"
645
+ />
646
+
647
+ {#if selectedNode}
648
+ <div class="menu-item-details">
649
+ <h3>
650
+ {#if selectedNode.item.icon}
651
+ <span class="item-icon">{selectedNode.item.icon}</span>
652
+ {/if}
653
+ {selectedNode.item.label}
654
+ </h3>
655
+ <div class="details-grid">
656
+ <div class="detail-item">
657
+ <strong>ID:</strong> {selectedNode.item.id}
658
+ </div>
659
+ <div class="detail-item">
660
+ <strong>Category:</strong> {selectedNode.item.category}
661
+ </div>
662
+ <div class="detail-item">
663
+ <strong>Type:</strong> {checkIsDirectory(selectedNode) ? 'Menu Group' : 'Menu Item'}
664
+ </div>
665
+ {#if selectedNode.children}
666
+ <div class="detail-item">
667
+ <strong>Children:</strong> {selectedNode.children.length}
668
+ </div>
669
+ {/if}
670
+ </div>
671
+
672
+ <div class="action-buttons">
673
+ <button class="btn btn-primary">Edit</button>
674
+ <button class="btn btn-secondary">Duplicate</button>
675
+ <button class="btn btn-danger">Delete</button>
676
+ </div>
677
+ </div>
678
+ {/if}
679
+ </div>
680
+ </div>
681
+
682
+ <style>
683
+ .menu-manager {
684
+ max-width: 800px;
685
+ margin: 0 auto;
686
+ padding: 20px;
687
+ }
688
+
689
+ .search-controls {
690
+ display: flex;
691
+ gap: 8px;
692
+ margin-bottom: 16px;
693
+ }
694
+
695
+ .search-input {
696
+ flex: 1;
697
+ padding: 8px 12px;
698
+ border: 1px solid #ddd;
699
+ border-radius: 4px;
700
+ font-size: 14px;
701
+ }
702
+
703
+ .clear-btn {
704
+ padding: 8px 16px;
705
+ background: #f8f9fa;
706
+ border: 1px solid #ddd;
707
+ border-radius: 4px;
708
+ cursor: pointer;
709
+ }
710
+
711
+ .clear-btn:hover {
712
+ background: #e9ecef;
713
+ }
714
+
715
+ .tree-container {
716
+ display: flex;
717
+ gap: 20px;
718
+ }
719
+
720
+ .menu-item-details {
721
+ flex: 0 0 300px;
722
+ padding: 20px;
723
+ background: #f8f9fa;
724
+ border-radius: 8px;
725
+ height: fit-content;
726
+ }
727
+
728
+ .menu-item-details h3 {
729
+ display: flex;
730
+ align-items: center;
731
+ gap: 8px;
732
+ margin: 0 0 16px 0;
733
+ color: #333;
734
+ }
735
+
736
+ .item-icon {
737
+ font-size: 1.2em;
738
+ }
739
+
740
+ .details-grid {
741
+ display: grid;
742
+ gap: 8px;
743
+ margin-bottom: 20px;
744
+ }
745
+
746
+ .detail-item {
747
+ padding: 8px 0;
748
+ border-bottom: 1px solid #e9ecef;
749
+ color: #555;
750
+ }
751
+
752
+ .action-buttons {
753
+ display: flex;
754
+ gap: 8px;
755
+ flex-wrap: wrap;
756
+ }
757
+
758
+ .btn {
759
+ padding: 6px 12px;
760
+ border: none;
761
+ border-radius: 4px;
762
+ cursor: pointer;
763
+ font-size: 0.875rem;
764
+ }
765
+
766
+ .btn-primary {
767
+ background: #007bff;
768
+ color: white;
769
+ }
770
+
771
+ .btn-secondary {
772
+ background: #6c757d;
773
+ color: white;
774
+ }
775
+
776
+ .btn-danger {
777
+ background: #dc3545;
778
+ color: white;
779
+ }
780
+
781
+ .btn:hover {
782
+ opacity: 0.8;
783
+ }
784
+ </style>
785
+ ```
786
+
787
+ ## Styling
788
+
789
+ The TreeView component uses the following CSS classes:
790
+
791
+ ```css
792
+ .uniface-tree-view {
793
+ /* Main tree container */
794
+ }
795
+
796
+ /* Individual tree nodes are styled by the TreeNodeView component */
797
+ ```
798
+
799
+ ## API Reference
800
+
801
+ ### TreeView Component Props
802
+
803
+ ```typescript
804
+ interface TreeViewProps {
805
+ nodes: Array<TreeNode<any>>; // Tree nodes to display
806
+ textField: string | GetText<any>; // Field or function for node text
807
+ style?: string; // Custom styles
808
+ lazyLoader?: LazyLoader | null; // Lazy loading config
809
+ activeNode?: any; // Currently active node
810
+ class?: string; // CSS classes
811
+ isVisible?: NodeVisibleFun; // Node visibility filter
812
+ onSelectionChange?: OnNodeSelectionChange; // Selection callback
813
+ onContextMenu?: OnContextMenu; // Context menu callback
814
+ checkIsDirectory: CheckIsDirectory<any>; // Directory check function
815
+ }
816
+ ```
817
+
818
+ ## Integration with TreeNodes Utility
819
+
820
+ The TreeView component works seamlessly with the TreeNodes utility class for managing hierarchical data:
821
+
822
+ ```svelte
823
+ <script lang="ts">
824
+ import TreeView from '@ticatec/uniface-element/TreeView';
825
+ import TreeNodes from '@ticatec/uniface-element/TreeNodes';
826
+
827
+ // Create TreeNodes instance for data management
828
+ const treeManager = new TreeNodes({
829
+ keyField: 'id',
830
+ parentKeyField: 'parentId',
831
+ textField: 'name',
832
+ checkIsRoot: (item) => !item.parentId,
833
+ checkIsDirectory: (node) => node.item.type === 'folder'
834
+ });
835
+
836
+ // Set flat data array - TreeNodes will build the hierarchy
837
+ let flatData = [
838
+ { id: '1', name: 'Root', type: 'folder', parentId: null },
839
+ { id: '2', name: 'Child 1', type: 'folder', parentId: '1' },
840
+ { id: '3', name: 'File 1', type: 'file', parentId: '2' }
841
+ ];
842
+
843
+ treeManager.setData(flatData);
844
+ let treeNodes = treeManager.nodes;
845
+ </script>
846
+
847
+ <TreeView
848
+ nodes={treeNodes}
849
+ textField="name"
850
+ checkIsDirectory={treeManager.checkIsDirectory}
851
+ />
852
+ ```
853
+
854
+ ## Best Practices
855
+
856
+ 1. **Performance** - Use lazy loading for large tree structures (>100 nodes)
857
+ 2. **Node Text** - Use consistent text field or function for node display
858
+ 3. **State Management** - Track active/selected nodes for user interaction
859
+ 4. **Accessibility** - Ensure keyboard navigation support
860
+ 5. **Error Handling** - Handle loading errors gracefully in lazy loader
861
+ 6. **Visual Feedback** - Provide loading indicators during lazy loading
862
+ 7. **Context Actions** - Implement context menus for node operations
863
+ 8. **Filtering** - Use visibility functions for search/filter functionality