@ticatec/uniface-element 0.2.4 → 0.3.0

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 (176) hide show
  1. package/dist/accordion/README.md +169 -0
  2. package/dist/accordion/README_CN.md +176 -0
  3. package/dist/action-bar/README.md +275 -0
  4. package/dist/action-bar/README_CN.md +285 -0
  5. package/dist/app-layout/README.md +374 -0
  6. package/dist/app-layout/README_CN.md +381 -0
  7. package/dist/app-top-bar/README.md +357 -0
  8. package/dist/app-top-bar/README_CN.md +365 -0
  9. package/dist/attachment-files/AttachmentFilesField.svelte +67 -1
  10. package/dist/attachment-files/AttachmentFilesField.svelte.d.ts +8 -1
  11. package/dist/base-calendar/CalendarBase.svelte +0 -3
  12. package/dist/box/README.md +385 -0
  13. package/dist/box/README_CN.md +393 -0
  14. package/dist/breadcrumbs/README.md +442 -0
  15. package/dist/breadcrumbs/README_CN.md +442 -0
  16. package/dist/button/Button.svelte +27 -7
  17. package/dist/button/Button.svelte.d.ts +3 -1
  18. package/dist/button/IconButton.svelte +26 -6
  19. package/dist/button/IconButton.svelte.d.ts +3 -1
  20. package/dist/button/IconButton_README.md +513 -0
  21. package/dist/button/IconButton_README_CN.md +513 -0
  22. package/dist/button/README.md +428 -0
  23. package/dist/button/README_CN.md +428 -0
  24. package/dist/button/TextButton.svelte +26 -6
  25. package/dist/button/TextButton.svelte.d.ts +3 -1
  26. package/dist/button/TextButton_README.md +571 -0
  27. package/dist/button/TextButton_README_CN.md +571 -0
  28. package/dist/card/README.md +666 -0
  29. package/dist/card/README_CN.md +666 -0
  30. package/dist/cascade-options-select/CascadeOptionSelect.svelte +43 -7
  31. package/dist/cascade-options-select/CascadeOptionSelect.svelte.d.ts +3 -4
  32. package/dist/cascade-options-select/README.md +663 -2
  33. package/dist/cascade-options-select/README_CN.md +664 -0
  34. package/dist/checkbox/CheckBox.svelte +20 -6
  35. package/dist/checkbox/CheckBox.svelte.d.ts +4 -2
  36. package/dist/checkbox/README.md +562 -0
  37. package/dist/checkbox/README_CN.md +727 -0
  38. package/dist/common-editor/NumberInput.svelte +39 -26
  39. package/dist/common-editor/NumberInput.svelte.d.ts +5 -1
  40. package/dist/concise-data-table/ConciseListTable.svelte +133 -36
  41. package/dist/concise-data-table/ConciseListTable.svelte.d.ts +8 -1
  42. package/dist/concise-data-table/DataCell.svelte +5 -6
  43. package/dist/concise-data-table/DataRow.svelte +22 -2
  44. package/dist/concise-data-table/DataRow.svelte.d.ts +1 -0
  45. package/dist/concise-data-table/README.md +606 -0
  46. package/dist/concise-data-table/README_CN.md +611 -0
  47. package/dist/context-menu/ContextMenu.svelte +138 -21
  48. package/dist/context-menu/ContextMenu.svelte.d.ts +5 -0
  49. package/dist/context-menu/ContextMenuItem.d.ts +20 -4
  50. package/dist/context-menu/README.md +551 -0
  51. package/dist/context-menu/README_CN.md +551 -0
  52. package/dist/criteria-field/CriteriaField.svelte +3 -5
  53. package/dist/criteria-field/README.md +532 -0
  54. package/dist/criteria-field/README_CN.md +532 -0
  55. package/dist/data-table/README.md +712 -0
  56. package/dist/data-table/README_CN.md +712 -0
  57. package/dist/date-picker/DatePicker.svelte +44 -7
  58. package/dist/date-picker/DatePicker.svelte.d.ts +3 -4
  59. package/dist/date-picker/DateTimePicker.svelte +32 -8
  60. package/dist/date-picker/DateTimePicker.svelte.d.ts +3 -4
  61. package/dist/date-picker/README.md +326 -0
  62. package/dist/date-picker/README_CN.md +316 -0
  63. package/dist/date-picker/ScrollBar.svelte +18 -8
  64. package/dist/date-picker/ScrollBar.svelte.d.ts +1 -1
  65. package/dist/date-picker/TimePanel.svelte +5 -5
  66. package/dist/date-picker/TimePanel.svelte.d.ts +1 -1
  67. package/dist/date-range/README.md +374 -0
  68. package/dist/date-range/README_CN.md +374 -0
  69. package/dist/dialog/README.md +596 -0
  70. package/dist/dialog/README_CN.md +596 -0
  71. package/dist/drawer/README.md +679 -1
  72. package/dist/drawer/README_CN.md +679 -0
  73. package/dist/group-box/group-check-box/GroupCheckBox.svelte +62 -5
  74. package/dist/group-box/group-check-box/GroupCheckBox.svelte.d.ts +7 -2
  75. package/dist/group-box/group-check-box/README.md +749 -0
  76. package/dist/group-box/group-check-box/README_CN.md +750 -0
  77. package/dist/group-box/group-radio-box/GroupRadioBox.svelte +69 -4
  78. package/dist/group-box/group-radio-box/GroupRadioBox.svelte.d.ts +8 -2
  79. package/dist/group-box/group-radio-box/README.md +920 -0
  80. package/dist/group-box/group-radio-box/README_CN.md +920 -0
  81. package/dist/image-files/README.md +1346 -0
  82. package/dist/image-files/README_CN.md +1346 -0
  83. package/dist/input-options-select/InputOptionsSelect.svelte +161 -65
  84. package/dist/input-options-select/InputOptionsSelect.svelte.d.ts +4 -1
  85. package/dist/lib/forwardAllEvents.d.ts +3 -0
  86. package/dist/lib/forwardAllEvents.js +24 -0
  87. package/dist/list-box/README.md +579 -0
  88. package/dist/list-box/README_CN.md +682 -0
  89. package/dist/lookup-editor/LookupEditor.svelte +15 -3
  90. package/dist/lookup-editor/LookupEditor.svelte.d.ts +3 -4
  91. package/dist/lookup-editor/README.md +684 -0
  92. package/dist/lookup-editor/README_CN.md +684 -0
  93. package/dist/memo-editor/MemoEditor.svelte +77 -27
  94. package/dist/memo-editor/MemoEditor.svelte.d.ts +6 -3
  95. package/dist/memo-editor/README.md +819 -0
  96. package/dist/memo-editor/README_CN.md +819 -0
  97. package/dist/message-box/README.md +398 -0
  98. package/dist/message-box/README_CN.md +398 -0
  99. package/dist/number-editor/NumberEditor.svelte +2 -2
  100. package/dist/number-editor/NumberEditor.svelte.d.ts +1 -1
  101. package/dist/number-editor/README.md +353 -22
  102. package/dist/number-editor/README_CN.md +357 -0
  103. package/dist/number-range/README.md +443 -0
  104. package/dist/number-range/README_CN.md +443 -0
  105. package/dist/options-multi-select/OptionsMultiSelect.svelte +87 -12
  106. package/dist/options-multi-select/OptionsMultiSelect.svelte.d.ts +3 -1
  107. package/dist/options-multi-select/README.md +480 -0
  108. package/dist/options-multi-select/README_CN.md +480 -0
  109. package/dist/options-select/OptionsSelect.svelte +36 -5
  110. package/dist/options-select/OptionsSelect.svelte.d.ts +3 -4
  111. package/dist/options-select/README.md +632 -0
  112. package/dist/options-select/README_CN.md +632 -0
  113. package/dist/page/README.md +605 -0
  114. package/dist/page/README_CN.md +605 -0
  115. package/dist/pagination/README.md +946 -0
  116. package/dist/pagination/README_CN.md +946 -0
  117. package/dist/pagination-panel/README.md +1075 -0
  118. package/dist/pagination-panel/README_CN.md +1075 -0
  119. package/dist/popup-hint/README.md +841 -0
  120. package/dist/popup-hint/README_CN.md +840 -0
  121. package/dist/progress-bar/README.md +242 -0
  122. package/dist/progress-bar/README_CN.md +242 -0
  123. package/dist/progress-step-bar/README.md +316 -0
  124. package/dist/progress-step-bar/README_CN.md +316 -0
  125. package/dist/prompts-text-editor/PromptsTextEditor.svelte +6 -3
  126. package/dist/prompts-text-editor/PromptsTextEditor.svelte.d.ts +3 -1
  127. package/dist/prompts-text-editor/README.md +329 -0
  128. package/dist/prompts-text-editor/README_CN.md +329 -0
  129. package/dist/property-editor/README.md +969 -0
  130. package/dist/property-editor/README_CN.md +969 -0
  131. package/dist/radio-button/README.md +372 -0
  132. package/dist/radio-button/README_CN.md +372 -0
  133. package/dist/radio-button/RadioButton.svelte +32 -3
  134. package/dist/radio-button/RadioButton.svelte.d.ts +12 -1
  135. package/dist/search-box/README.md +425 -0
  136. package/dist/search-box/README_CN.md +424 -0
  137. package/dist/search-box/SearchBox.svelte +22 -6
  138. package/dist/search-box/SearchBox.svelte.d.ts +4 -1
  139. package/dist/tabs/README.md +919 -0
  140. package/dist/tabs/README_CN.md +920 -0
  141. package/dist/tag/README.md +922 -0
  142. package/dist/tag/README_CN.md +922 -0
  143. package/dist/text-editor/PasswordEditor.svelte +17 -2
  144. package/dist/text-editor/PasswordEditor.svelte.d.ts +3 -3
  145. package/dist/text-editor/PasswordEditor_README.md +551 -0
  146. package/dist/text-editor/PasswordEditor_README_CN.md +551 -0
  147. package/dist/text-editor/README.md +466 -0
  148. package/dist/text-editor/README_CN.md +466 -0
  149. package/dist/text-editor/TextEditor.svelte +51 -19
  150. package/dist/text-editor/TextEditor.svelte.d.ts +0 -1
  151. package/dist/time-editor/README.md +498 -24
  152. package/dist/time-editor/README_CN.md +498 -0
  153. package/dist/time-editor/TimeEditor.svelte +7 -3
  154. package/dist/time-editor/TimeEditor.svelte.d.ts +3 -1
  155. package/dist/toast/README.md +667 -0
  156. package/dist/toast/README_CN.md +667 -0
  157. package/dist/transfer/README.md +795 -0
  158. package/dist/transfer/Transfer.svelte +6 -4
  159. package/dist/transfer/Transfer.svelte.d.ts +3 -1
  160. package/dist/tree-view/README.md +761 -0
  161. package/dist/tree-view/README_CN.md +761 -0
  162. package/dist/tree-view/TreeView.svelte +5 -3
  163. package/dist/tree-view/TreeView.svelte.d.ts +3 -1
  164. package/dist/unit-number-editor/README.md +546 -20
  165. package/dist/unit-number-editor/README_CN.md +525 -0
  166. package/dist/unit-number-editor/UnitNumberEditor.svelte +62 -8
  167. package/dist/unit-number-editor/UnitNumberEditor.svelte.d.ts +5 -1
  168. package/docs/form-field/cn/README_CN.md +364 -0
  169. package/docs/form-field/en/README.md +364 -0
  170. package/docs/form-panel/flex-form/cn/README_CN.md +586 -0
  171. package/docs/form-panel/flex-form/en/README.md +586 -0
  172. package/docs/form-panel/flex-row-form/cn/README_CN.md +787 -0
  173. package/docs/form-panel/flex-row-form/en/README.md +787 -0
  174. package/docs/form-panel/grid-form/en/README.md +744 -0
  175. package/package.json +1 -1
  176. package/dist/drawer/README-CN.md +0 -1
@@ -0,0 +1,664 @@
1
+ # CascadeOptionSelect 级联选择组件
2
+
3
+ 一个用于从嵌套数据结构中选择选项的分层下拉组件。非常适合位置选择器(国家/省份/城市)、分类层次结构、组织架构以及任何多级选择场景。
4
+
5
+ ## 功能特性
6
+
7
+ - **分层选择**: 导航通过嵌套选项层级
8
+ - **动态加载**: 通过回调异步加载子选项
9
+ - **灵活数据结构**: 可配置字段映射适应不同数据格式
10
+ - **叶子节点检测**: 自定义逻辑确定可选择的终端节点
11
+ - **显示模式**: 编辑和显示模式适应不同UI场景
12
+ - **键盘和焦点支持**: 完整的键盘导航和焦点管理
13
+ - **加载状态**: 异步操作的内置加载指示器
14
+ - **可定制样式**: 灵活的外观选项和变体
15
+ - **清除/必选选项**: 可选的清除功能和必选选择
16
+
17
+ ## 基本用法
18
+
19
+ ```svelte
20
+ <script>
21
+ import CascadeOptionSelect from '@ticatec/uniface-element/CascadeOptionSelect';
22
+
23
+ const locationData = [
24
+ {
25
+ code: 'CN',
26
+ text: '中国',
27
+ abbr: '中国',
28
+ children: [
29
+ {
30
+ code: 'BJ',
31
+ text: '北京市',
32
+ abbr: '北京',
33
+ children: [
34
+ { code: 'HD', text: '海淀区', abbr: '海淀' },
35
+ { code: 'CY', text: '朝阳区', abbr: '朝阳' }
36
+ ]
37
+ }
38
+ ]
39
+ }
40
+ ];
41
+
42
+ let selectedValue = null;
43
+
44
+ const handleChange = (value) => {
45
+ console.log('选择:', value);
46
+ selectedValue = value;
47
+ };
48
+ </script>
49
+
50
+ <CascadeOptionSelect
51
+ nodes={locationData}
52
+ bind:value={selectedValue}
53
+ onchange={handleChange}
54
+ placeholder="选择位置..."
55
+ />
56
+ ```
57
+
58
+ ## 属性参数
59
+
60
+ | 属性 | 类型 | 默认值 | 描述 |
61
+ |------|------|---------|-------------|
62
+ | `variant` | `'' \| 'plain' \| 'outlined' \| 'filled'` | `''` | 视觉变体样式 |
63
+ | `disabled` | `boolean` | `false` | 是否禁用组件 |
64
+ | `readonly` | `boolean` | `false` | 是否只读 |
65
+ | `compact` | `boolean` | `false` | 紧凑显示模式 |
66
+ | `mandatory` | `boolean` | `false` | 是否必选(禁用清除功能) |
67
+ | `value` | `any` | `null` | 选中的值(键字段值) |
68
+ | `keyField` | `string` | `'code'` | 选项键/值的字段名 |
69
+ | `textField` | `string` | `'text'` | 选项显示文本的字段名 |
70
+ | `abbrField` | `string` | `'abbr'` | 下拉框中缩写文本的字段名 |
71
+ | `childrenField` | `string` | `'children'` | 子选项的字段名 |
72
+ | `style` | `string` | `''` | 自定义CSS样式 |
73
+ | `placeholder` | `string` | `''` | 占位符文本 |
74
+ | `displayMode` | `DisplayMode` | `DisplayMode.Edit` | 显示或编辑模式 |
75
+ | `emptyText` | `string \| null` | `null` | 无选择时显示的文本 |
76
+ | `checkLeaf` | `IsLeafDetermine \| null` | `null` | 判断项目是否为可选叶子节点的函数 |
77
+ | `text` | `string` | `''` | 显示文本(自动管理) |
78
+ | `nodes` | `Array<any>` | `[]` | 根级选项数组 |
79
+ | `menu$height` | `number` | `150` | 下拉菜单高度(像素) |
80
+ | `onSelect` | `OnSelectOption` | - | 加载子选项的异步函数 |
81
+ | `box$style` | `string` | `''` | 选项框的自定义样式 |
82
+ | `onchange` | `OnChangeHandler<any>` | - | 值变化事件处理器 |
83
+ | `onfocus` | `(() => void) \| null` | `null` | 聚焦事件处理器 |
84
+ | `onblur` | `(() => void) \| null` | `null` | 失焦事件处理器 |
85
+
86
+ ## 类型定义
87
+
88
+ ```typescript
89
+ // 异步加载子选项的函数
90
+ type OnSelectOption = (item: any) => Promise<Array<any>>;
91
+
92
+ // 判断项目是否为可选叶子节点的函数
93
+ type IsLeafDetermine = (item: any) => boolean;
94
+
95
+ // 值变化事件处理器
96
+ type OnChangeHandler<T> = (value: T) => void;
97
+ ```
98
+
99
+ ## 静态数据示例
100
+
101
+ ```svelte
102
+ <script>
103
+ const categories = [
104
+ {
105
+ code: 'electronics',
106
+ text: '电子产品',
107
+ abbr: '电子',
108
+ children: [
109
+ {
110
+ code: 'computers',
111
+ text: '计算机',
112
+ abbr: '计算机',
113
+ children: [
114
+ { code: 'laptops', text: '笔记本电脑', abbr: '笔记本' },
115
+ { code: 'desktops', text: '台式机', abbr: '台式机' }
116
+ ]
117
+ },
118
+ {
119
+ code: 'phones',
120
+ text: '手机',
121
+ abbr: '手机',
122
+ children: [
123
+ { code: 'smartphones', text: '智能手机', abbr: '智能机' },
124
+ { code: 'basic-phones', text: '功能手机', abbr: '功能机' }
125
+ ]
126
+ }
127
+ ]
128
+ }
129
+ ];
130
+
131
+ let selectedCategory = null;
132
+ </script>
133
+
134
+ <CascadeOptionSelect
135
+ nodes={categories}
136
+ bind:value={selectedCategory}
137
+ placeholder="选择分类..."
138
+ onchange={(value) => console.log('选择的分类:', value)}
139
+ />
140
+ ```
141
+
142
+ ## 动态加载示例
143
+
144
+ ```svelte
145
+ <script>
146
+ const loadRegions = async (country) => {
147
+ // 模拟API调用
148
+ const response = await fetch(`/api/regions?country=${country.code}`);
149
+ return await response.json();
150
+ };
151
+
152
+ const loadCities = async (region) => {
153
+ const response = await fetch(`/api/cities?region=${region.code}`);
154
+ return await response.json();
155
+ };
156
+
157
+ const dynamicLoader = async (item) => {
158
+ // 根据项目类型确定要加载的数据
159
+ if (!item.children) {
160
+ if (item.type === 'country') {
161
+ return await loadRegions(item);
162
+ } else if (item.type === 'region') {
163
+ return await loadCities(item);
164
+ }
165
+ }
166
+ return item.children || [];
167
+ };
168
+
169
+ const countries = [
170
+ { code: 'CN', text: '中国', abbr: '中国', type: 'country' },
171
+ { code: 'US', text: '美国', abbr: '美国', type: 'country' },
172
+ { code: 'JP', text: '日本', abbr: '日本', type: 'country' }
173
+ ];
174
+
175
+ let selectedLocation = null;
176
+ </script>
177
+
178
+ <CascadeOptionSelect
179
+ nodes={countries}
180
+ bind:value={selectedLocation}
181
+ onSelect={dynamicLoader}
182
+ placeholder="选择位置..."
183
+ onchange={(value) => console.log('选择的位置:', value)}
184
+ />
185
+ ```
186
+
187
+ ## 自定义字段映射
188
+
189
+ ```svelte
190
+ <script>
191
+ // 使用不同字段名的数据
192
+ const orgData = [
193
+ {
194
+ id: 'corp',
195
+ name: '集团公司',
196
+ short: '集团',
197
+ divisions: [
198
+ {
199
+ id: 'tech',
200
+ name: '技术部门',
201
+ short: '技术',
202
+ departments: [
203
+ { id: 'dev', name: '开发部', short: '开发' },
204
+ { id: 'qa', name: '质量保证部', short: '质保' }
205
+ ]
206
+ }
207
+ ]
208
+ }
209
+ ];
210
+
211
+ let selectedOrg = null;
212
+ </script>
213
+
214
+ <CascadeOptionSelect
215
+ nodes={orgData}
216
+ bind:value={selectedOrg}
217
+ keyField="id"
218
+ textField="name"
219
+ abbrField="short"
220
+ childrenField="divisions"
221
+ placeholder="选择组织..."
222
+ />
223
+ ```
224
+
225
+ ## 叶子节点检测示例
226
+
227
+ ```svelte
228
+ <script>
229
+ const menuData = [
230
+ {
231
+ code: 'file',
232
+ text: '文件',
233
+ abbr: '文件',
234
+ isLeaf: false,
235
+ children: [
236
+ { code: 'new', text: '新建文件', abbr: '新建', isLeaf: true },
237
+ { code: 'open', text: '打开文件', abbr: '打开', isLeaf: true },
238
+ { code: 'save', text: '保存文件', abbr: '保存', isLeaf: true }
239
+ ]
240
+ }
241
+ ];
242
+
243
+ const checkIfLeaf = (item) => {
244
+ return item.isLeaf === true;
245
+ };
246
+
247
+ let selectedMenuItem = null;
248
+ </script>
249
+
250
+ <CascadeOptionSelect
251
+ nodes={menuData}
252
+ bind:value={selectedMenuItem}
253
+ checkLeaf={checkIfLeaf}
254
+ placeholder="选择菜单项..."
255
+ />
256
+ ```
257
+
258
+ ## 不同变体样式
259
+
260
+ ```svelte
261
+ <!-- 默认变体 -->
262
+ <CascadeOptionSelect
263
+ nodes={data}
264
+ placeholder="默认样式"
265
+ />
266
+
267
+ <!-- 平面变体 -->
268
+ <CascadeOptionSelect
269
+ nodes={data}
270
+ variant="plain"
271
+ placeholder="平面样式"
272
+ />
273
+
274
+ <!-- 轮廓变体 -->
275
+ <CascadeOptionSelect
276
+ nodes={data}
277
+ variant="outlined"
278
+ placeholder="轮廓样式"
279
+ />
280
+
281
+ <!-- 填充变体 -->
282
+ <CascadeOptionSelect
283
+ nodes={data}
284
+ variant="filled"
285
+ placeholder="填充样式"
286
+ />
287
+ ```
288
+
289
+ ## 事件处理
290
+
291
+ ```svelte
292
+ <script>
293
+ let currentValue = null;
294
+ let focusCount = 0;
295
+
296
+ const handleChange = (value) => {
297
+ console.log('值变化为:', value);
298
+ currentValue = value;
299
+ };
300
+
301
+ const handleFocus = () => {
302
+ console.log('组件获得焦点');
303
+ focusCount++;
304
+ };
305
+
306
+ const handleBlur = () => {
307
+ console.log('组件失去焦点');
308
+ };
309
+ </script>
310
+
311
+ <CascadeOptionSelect
312
+ nodes={data}
313
+ bind:value={currentValue}
314
+ onchange={handleChange}
315
+ onfocus={handleFocus}
316
+ onblur={handleBlur}
317
+ placeholder="事件处理示例"
318
+ />
319
+
320
+ <p>当前值: {currentValue}</p>
321
+ <p>聚焦次数: {focusCount}</p>
322
+ ```
323
+
324
+ ## 高级示例
325
+
326
+ ### 带API集成的位置选择器
327
+
328
+ ```svelte
329
+ <script>
330
+ import { onMount } from 'svelte';
331
+
332
+ let countries = [];
333
+ let selectedLocation = null;
334
+
335
+ onMount(async () => {
336
+ // 加载初始国家列表
337
+ const response = await fetch('/api/countries');
338
+ countries = await response.json();
339
+ });
340
+
341
+ const loadLocationChildren = async (location) => {
342
+ let endpoint = '';
343
+
344
+ if (location.type === 'country') {
345
+ endpoint = `/api/provinces?country=${location.code}`;
346
+ } else if (location.type === 'province') {
347
+ endpoint = `/api/cities?province=${location.code}`;
348
+ }
349
+
350
+ if (endpoint) {
351
+ const response = await fetch(endpoint);
352
+ return await response.json();
353
+ }
354
+
355
+ return [];
356
+ };
357
+
358
+ const isLocationLeaf = (location) => {
359
+ return location.type === 'city';
360
+ };
361
+ </script>
362
+
363
+ <div class="location-selector">
364
+ <label>选择位置:</label>
365
+ <CascadeOptionSelect
366
+ nodes={countries}
367
+ bind:value={selectedLocation}
368
+ onSelect={loadLocationChildren}
369
+ checkLeaf={isLocationLeaf}
370
+ placeholder="国家 / 省份 / 城市"
371
+ mandatory={true}
372
+ variant="outlined"
373
+ />
374
+ </div>
375
+ ```
376
+
377
+ ### 产品分类导航器
378
+
379
+ ```svelte
380
+ <script>
381
+ const productCategories = [
382
+ {
383
+ code: 'electronics',
384
+ text: '电子产品',
385
+ abbr: '电子',
386
+ children: [
387
+ {
388
+ code: 'computers',
389
+ text: '计算机及配件',
390
+ abbr: '计算机',
391
+ children: [
392
+ { code: 'laptops', text: '笔记本电脑', abbr: '笔记本' },
393
+ { code: 'desktops', text: '台式电脑', abbr: '台式机' },
394
+ { code: 'accessories', text: '电脑配件', abbr: '配件' }
395
+ ]
396
+ },
397
+ {
398
+ code: 'mobile',
399
+ text: '移动设备',
400
+ abbr: '移动',
401
+ children: [
402
+ { code: 'smartphones', text: '智能手机', abbr: '手机' },
403
+ { code: 'tablets', text: '平板电脑', abbr: '平板' },
404
+ { code: 'wearables', text: '可穿戴设备', abbr: '穿戴' }
405
+ ]
406
+ }
407
+ ]
408
+ },
409
+ {
410
+ code: 'clothing',
411
+ text: '服装时尚',
412
+ abbr: '服装',
413
+ children: [
414
+ {
415
+ code: 'mens',
416
+ text: '男装',
417
+ abbr: '男装',
418
+ children: [
419
+ { code: 'shirts', text: '衬衫', abbr: '衬衫' },
420
+ { code: 'pants', text: '裤子', abbr: '裤子' },
421
+ { code: 'shoes', text: '鞋子', abbr: '鞋子' }
422
+ ]
423
+ }
424
+ ]
425
+ }
426
+ ];
427
+
428
+ let selectedCategory = null;
429
+
430
+ $: if (selectedCategory) {
431
+ console.log('浏览分类的产品:', selectedCategory);
432
+ }
433
+ </script>
434
+
435
+ <div class="category-navigator">
436
+ <h3>浏览产品</h3>
437
+ <CascadeOptionSelect
438
+ nodes={productCategories}
439
+ bind:value={selectedCategory}
440
+ placeholder="选择分类浏览..."
441
+ variant="filled"
442
+ menu$height={200}
443
+ />
444
+ </div>
445
+ ```
446
+
447
+ ### 组织层次结构选择器
448
+
449
+ ```svelte
450
+ <script>
451
+ const organizationData = [
452
+ {
453
+ code: 'headquarters',
454
+ text: '总部',
455
+ abbr: '总部',
456
+ children: [
457
+ {
458
+ code: 'exec',
459
+ text: '高管',
460
+ abbr: '高管',
461
+ children: [
462
+ { code: 'ceo', text: '首席执行官', abbr: 'CEO' },
463
+ { code: 'cto', text: '首席技术官', abbr: 'CTO' }
464
+ ]
465
+ },
466
+ {
467
+ code: 'engineering',
468
+ text: '工程部',
469
+ abbr: '工程',
470
+ children: [
471
+ { code: 'frontend', text: '前端开发', abbr: '前端' },
472
+ { code: 'backend', text: '后端开发', abbr: '后端' },
473
+ { code: 'devops', text: '运维开发', abbr: '运维' }
474
+ ]
475
+ }
476
+ ]
477
+ }
478
+ ];
479
+
480
+ let assignedDepartment = null;
481
+
482
+ const handleDepartmentChange = (value) => {
483
+ console.log('分配到部门:', value);
484
+ // 更新用户分配
485
+ };
486
+ </script>
487
+
488
+ <div class="department-assignment">
489
+ <label>分配到部门:</label>
490
+ <CascadeOptionSelect
491
+ nodes={organizationData}
492
+ bind:value={assignedDepartment}
493
+ onchange={handleDepartmentChange}
494
+ placeholder="选择部门..."
495
+ mandatory={true}
496
+ compact={true}
497
+ />
498
+ </div>
499
+ ```
500
+
501
+ ### 带动态加载的菜单系统
502
+
503
+ ```svelte
504
+ <script>
505
+ let menuItems = [
506
+ { code: 'file', text: '文件', abbr: '文件', hasChildren: true },
507
+ { code: 'edit', text: '编辑', abbr: '编辑', hasChildren: true },
508
+ { code: 'view', text: '查看', abbr: '查看', hasChildren: true }
509
+ ];
510
+
511
+ const loadMenuChildren = async (menuItem) => {
512
+ // 模拟从服务器加载菜单项
513
+ await new Promise(resolve => setTimeout(resolve, 500));
514
+
515
+ const menuMap = {
516
+ 'file': [
517
+ { code: 'new', text: '新建', abbr: '新建', action: 'file.new' },
518
+ { code: 'open', text: '打开', abbr: '打开', action: 'file.open' },
519
+ { code: 'save', text: '保存', abbr: '保存', action: 'file.save' }
520
+ ],
521
+ 'edit': [
522
+ { code: 'cut', text: '剪切', abbr: '剪切', action: 'edit.cut' },
523
+ { code: 'copy', text: '复制', abbr: '复制', action: 'edit.copy' },
524
+ { code: 'paste', text: '粘贴', abbr: '粘贴', action: 'edit.paste' }
525
+ ],
526
+ 'view': [
527
+ { code: 'zoom-in', text: '放大', abbr: '放大', action: 'view.zoomIn' },
528
+ { code: 'zoom-out', text: '缩小', abbr: '缩小', action: 'view.zoomOut' }
529
+ ]
530
+ };
531
+
532
+ return menuMap[menuItem.code] || [];
533
+ };
534
+
535
+ const isMenuItem = (item) => {
536
+ return item.action != null;
537
+ };
538
+
539
+ let selectedAction = null;
540
+
541
+ $: if (selectedAction) {
542
+ console.log('执行操作:', selectedAction);
543
+ }
544
+ </script>
545
+
546
+ <CascadeOptionSelect
547
+ nodes={menuItems}
548
+ bind:value={selectedAction}
549
+ onSelect={loadMenuChildren}
550
+ checkLeaf={isMenuItem}
551
+ placeholder="选择操作..."
552
+ variant="outlined"
553
+ />
554
+ ```
555
+
556
+ ## 样式定制
557
+
558
+ 组件使用以下CSS类,可以通过以下方式进行样式定制:
559
+
560
+ - 主容器的自定义 `style` 属性
561
+ - 单个选项框的 `box$style` 属性
562
+ - 下拉框高度的 `menu$height` 属性
563
+ - 深度定制的CSS自定义属性
564
+
565
+ ### 样式示例
566
+
567
+ ```svelte
568
+ <CascadeOptionSelect
569
+ nodes={data}
570
+ style="border-radius: 8px; border: 2px solid #007acc;"
571
+ box$style="background: #f5f5f5; border-radius: 4px;"
572
+ menu$height={250}
573
+ />
574
+ ```
575
+
576
+ ## 无障碍访问
577
+
578
+ 组件包含:
579
+
580
+ - 适当的焦点管理
581
+ - 键盘导航支持
582
+ - 屏幕阅读器的ARIA属性
583
+ - 聚焦和失焦事件处理
584
+ - 选中项目的清晰视觉指示器
585
+
586
+ ## 最佳实践
587
+
588
+ 1. **数据结构**: 在所有层级使用一致的字段命名
589
+ 2. **加载状态**: 在异步操作期间提供反馈
590
+ 3. **错误处理**: 在onSelect回调中优雅地处理API失败
591
+ 4. **性能**: 对于非常大的数据集考虑虚拟化
592
+ 5. **验证**: 当mandatory为true时验证叶子节点选择
593
+ 6. **响应式设计**: 在移动设备上测试下拉框行为
594
+ 7. **用户体验**: 在选项显示中提供清晰的视觉层次
595
+
596
+ ## 常见模式
597
+
598
+ ### 表单集成
599
+
600
+ ```svelte
601
+ <script>
602
+ let formData = {
603
+ location: null,
604
+ category: null
605
+ };
606
+
607
+ const validateForm = () => {
608
+ return formData.location && formData.category;
609
+ };
610
+ </script>
611
+
612
+ <form>
613
+ <div class="form-field">
614
+ <label>位置:</label>
615
+ <CascadeOptionSelect
616
+ nodes={locationData}
617
+ bind:value={formData.location}
618
+ mandatory={true}
619
+ placeholder="选择位置..."
620
+ />
621
+ </div>
622
+
623
+ <button type="submit" disabled={!validateForm()}>
624
+ 提交
625
+ </button>
626
+ </form>
627
+ ```
628
+
629
+ ### 状态管理
630
+
631
+ ```svelte
632
+ <script>
633
+ import { writable } from 'svelte/store';
634
+
635
+ const selectedValues = writable({
636
+ location: null,
637
+ category: null,
638
+ department: null
639
+ });
640
+
641
+ $: console.log('当前选择:', $selectedValues);
642
+ </script>
643
+
644
+ <CascadeOptionSelect
645
+ nodes={locationData}
646
+ value={$selectedValues.location}
647
+ onchange={(value) => selectedValues.update(s => ({...s, location: value}))}
648
+ />
649
+ ```
650
+
651
+ ## 浏览器支持
652
+
653
+ 在支持以下特性的所有现代浏览器中工作:
654
+ - ES6+ JavaScript
655
+ - CSS Flexbox
656
+ - 现代DOM API
657
+ - Promises/async-await
658
+
659
+ ## 性能考虑
660
+
661
+ - 通过选项框中的虚拟滚动高效处理大数据集
662
+ - 防抖异步加载以防止过度API调用
663
+ - 适当清理事件监听器和超时
664
+ - 使用Svelte响应式系统优化重新渲染
@@ -10,8 +10,10 @@
10
10
  export let indeterminate: boolean = false;
11
11
  export let style: string = '';
12
12
  export let compact: boolean = false;
13
- export let onClick: ((event: MouseEvent) => void) | null = null;
14
- export let onChange: OnChangeHandler<boolean> = null as unknown as OnChangeHandler<boolean>;
13
+ export let onclick: ((event: MouseEvent) => void) | null = null;
14
+ export let onchange: OnChangeHandler<boolean> = null as unknown as OnChangeHandler<boolean>;
15
+ export let onfocus: ((event: FocusEvent) => void) | null = null;
16
+ export let onblur: ((event: FocusEvent) => void) | null = null;
15
17
  export let autoFocus: boolean = false;
16
18
 
17
19
  export const setFocus = () => {
@@ -33,17 +35,29 @@
33
35
  } else {
34
36
  indeterminate = false;
35
37
  setTimeout(async () => {
36
- onClick?.(e);
38
+ onclick?.(e);
37
39
  await tick();
38
40
  if (checked != oldValue) {
39
41
  value = checked;
40
- onChange?.(value)
42
+ onchange?.(value)
41
43
  }
42
44
  }, 50)
43
45
  e.stopPropagation();
44
46
  }
45
47
  }
46
48
 
49
+ const handleFocus = (e: FocusEvent) => {
50
+ if (!disabled) {
51
+ onfocus?.(e);
52
+ }
53
+ }
54
+
55
+ const handleBlur = (e: FocusEvent) => {
56
+ if (!disabled) {
57
+ onblur?.(e);
58
+ }
59
+ }
60
+
47
61
  $: if (checkbox) {
48
62
  checkbox.indeterminate = indeterminate;
49
63
  }
@@ -59,8 +73,8 @@
59
73
 
60
74
  </script>
61
75
  <div class="uniface-checkbox" class:disabled class:compact {style}>
62
- <label aria-hidden="true" on:click={handleClickEvent}>
63
- <input bind:this={checkbox} type="checkbox" bind:checked={checked} {readonly} {disabled}/>
76
+ <label on:click={handleClickEvent}>
77
+ <input bind:this={checkbox} type="checkbox" bind:checked={checked} {readonly} {disabled} on:focus={handleFocus} on:blur={handleBlur}/>
64
78
  {#if label}
65
79
  <span>{label}</span>
66
80
  {/if}