@ticatec/uniface-element 0.2.3 → 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 (178) 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/DialogBoard.svelte +1 -1
  70. package/dist/dialog/README.md +596 -0
  71. package/dist/dialog/README_CN.md +596 -0
  72. package/dist/drawer/README.md +679 -1
  73. package/dist/drawer/README_CN.md +679 -0
  74. package/dist/group-box/group-check-box/GroupCheckBox.svelte +62 -5
  75. package/dist/group-box/group-check-box/GroupCheckBox.svelte.d.ts +7 -2
  76. package/dist/group-box/group-check-box/README.md +749 -0
  77. package/dist/group-box/group-check-box/README_CN.md +750 -0
  78. package/dist/group-box/group-radio-box/GroupRadioBox.svelte +69 -4
  79. package/dist/group-box/group-radio-box/GroupRadioBox.svelte.d.ts +8 -2
  80. package/dist/group-box/group-radio-box/README.md +920 -0
  81. package/dist/group-box/group-radio-box/README_CN.md +920 -0
  82. package/dist/image-files/README.md +1346 -0
  83. package/dist/image-files/README_CN.md +1346 -0
  84. package/dist/input-options-select/InputOptionsSelect.svelte +161 -65
  85. package/dist/input-options-select/InputOptionsSelect.svelte.d.ts +4 -1
  86. package/dist/lib/forwardAllEvents.d.ts +3 -0
  87. package/dist/lib/forwardAllEvents.js +24 -0
  88. package/dist/list-box/README.md +579 -0
  89. package/dist/list-box/README_CN.md +682 -0
  90. package/dist/lookup-editor/LookupEditor.svelte +15 -3
  91. package/dist/lookup-editor/LookupEditor.svelte.d.ts +3 -4
  92. package/dist/lookup-editor/README.md +684 -0
  93. package/dist/lookup-editor/README_CN.md +684 -0
  94. package/dist/memo-editor/MemoEditor.svelte +77 -27
  95. package/dist/memo-editor/MemoEditor.svelte.d.ts +6 -3
  96. package/dist/memo-editor/README.md +819 -0
  97. package/dist/memo-editor/README_CN.md +819 -0
  98. package/dist/message-box/README.md +398 -0
  99. package/dist/message-box/README_CN.md +398 -0
  100. package/dist/number-editor/NumberEditor.svelte +2 -2
  101. package/dist/number-editor/NumberEditor.svelte.d.ts +1 -1
  102. package/dist/number-editor/README.md +353 -22
  103. package/dist/number-editor/README_CN.md +357 -0
  104. package/dist/number-range/README.md +443 -0
  105. package/dist/number-range/README_CN.md +443 -0
  106. package/dist/options-multi-select/OptionsMultiSelect.svelte +87 -12
  107. package/dist/options-multi-select/OptionsMultiSelect.svelte.d.ts +3 -1
  108. package/dist/options-multi-select/README.md +480 -0
  109. package/dist/options-multi-select/README_CN.md +480 -0
  110. package/dist/options-select/OptionsSelect.svelte +36 -5
  111. package/dist/options-select/OptionsSelect.svelte.d.ts +3 -4
  112. package/dist/options-select/README.md +632 -0
  113. package/dist/options-select/README_CN.md +632 -0
  114. package/dist/page/README.md +605 -0
  115. package/dist/page/README_CN.md +605 -0
  116. package/dist/pagination/README.md +946 -0
  117. package/dist/pagination/README_CN.md +946 -0
  118. package/dist/pagination-panel/README.md +1075 -0
  119. package/dist/pagination-panel/README_CN.md +1075 -0
  120. package/dist/popup-hint/README.md +841 -0
  121. package/dist/popup-hint/README_CN.md +840 -0
  122. package/dist/progress-bar/README.md +242 -0
  123. package/dist/progress-bar/README_CN.md +242 -0
  124. package/dist/progress-step-bar/README.md +316 -0
  125. package/dist/progress-step-bar/README_CN.md +316 -0
  126. package/dist/prompts-text-editor/PromptsTextEditor.svelte +6 -3
  127. package/dist/prompts-text-editor/PromptsTextEditor.svelte.d.ts +3 -1
  128. package/dist/prompts-text-editor/README.md +329 -0
  129. package/dist/prompts-text-editor/README_CN.md +329 -0
  130. package/dist/property-editor/README.md +969 -0
  131. package/dist/property-editor/README_CN.md +969 -0
  132. package/dist/radio-button/README.md +372 -0
  133. package/dist/radio-button/README_CN.md +372 -0
  134. package/dist/radio-button/RadioButton.svelte +32 -3
  135. package/dist/radio-button/RadioButton.svelte.d.ts +12 -1
  136. package/dist/search-box/README.md +425 -0
  137. package/dist/search-box/README_CN.md +424 -0
  138. package/dist/search-box/SearchBox.svelte +22 -6
  139. package/dist/search-box/SearchBox.svelte.d.ts +4 -1
  140. package/dist/tabs/README.md +919 -0
  141. package/dist/tabs/README_CN.md +920 -0
  142. package/dist/tag/README.md +922 -0
  143. package/dist/tag/README_CN.md +922 -0
  144. package/dist/text-editor/PasswordEditor.svelte +17 -2
  145. package/dist/text-editor/PasswordEditor.svelte.d.ts +3 -3
  146. package/dist/text-editor/PasswordEditor_README.md +551 -0
  147. package/dist/text-editor/PasswordEditor_README_CN.md +551 -0
  148. package/dist/text-editor/README.md +466 -0
  149. package/dist/text-editor/README_CN.md +466 -0
  150. package/dist/text-editor/TextEditor.svelte +51 -19
  151. package/dist/text-editor/TextEditor.svelte.d.ts +0 -1
  152. package/dist/ticatec-uniface-web.css +1 -0
  153. package/dist/time-editor/README.md +498 -24
  154. package/dist/time-editor/README_CN.md +498 -0
  155. package/dist/time-editor/TimeEditor.svelte +7 -3
  156. package/dist/time-editor/TimeEditor.svelte.d.ts +3 -1
  157. package/dist/toast/README.md +667 -0
  158. package/dist/toast/README_CN.md +667 -0
  159. package/dist/transfer/README.md +795 -0
  160. package/dist/transfer/Transfer.svelte +6 -4
  161. package/dist/transfer/Transfer.svelte.d.ts +3 -1
  162. package/dist/tree-view/README.md +761 -0
  163. package/dist/tree-view/README_CN.md +761 -0
  164. package/dist/tree-view/TreeView.svelte +5 -3
  165. package/dist/tree-view/TreeView.svelte.d.ts +3 -1
  166. package/dist/unit-number-editor/README.md +546 -20
  167. package/dist/unit-number-editor/README_CN.md +525 -0
  168. package/dist/unit-number-editor/UnitNumberEditor.svelte +62 -8
  169. package/dist/unit-number-editor/UnitNumberEditor.svelte.d.ts +5 -1
  170. package/docs/form-field/cn/README_CN.md +364 -0
  171. package/docs/form-field/en/README.md +364 -0
  172. package/docs/form-panel/flex-form/cn/README_CN.md +586 -0
  173. package/docs/form-panel/flex-form/en/README.md +586 -0
  174. package/docs/form-panel/flex-row-form/cn/README_CN.md +787 -0
  175. package/docs/form-panel/flex-row-form/en/README.md +787 -0
  176. package/docs/form-panel/grid-form/en/README.md +744 -0
  177. package/package.json +1 -1
  178. package/dist/drawer/README-CN.md +0 -1
@@ -0,0 +1,682 @@
1
+ # ListBox 列表框组件
2
+
3
+ 一个多功能的列表组件,支持过滤、懒加载、单选/多选模式和自定义项目渲染。
4
+
5
+ ## 特性
6
+
7
+ - **多种选择模式**: 支持无选择、单选和多选模式
8
+ - **搜索和过滤**: 内置搜索功能,支持自定义过滤函数
9
+ - **懒加载**: 支持分页数据加载和无限滚动
10
+ - **自定义渲染**: 灵活的项目渲染,支持自定义组件
11
+ - **交互状态**: 悬停、选中和点击处理
12
+ - **头部/底部插槽**: 可自定义的头部和底部区域
13
+ - **加载状态**: 内置异步操作的加载指示器
14
+ - **无障碍访问**: 键盘导航和屏幕阅读器支持
15
+
16
+ ## 安装
17
+
18
+ ```bash
19
+ npm install @ticatec/uniface-element
20
+ ```
21
+
22
+ ## 使用方法
23
+
24
+ ### 基本用法
25
+
26
+ ```svelte
27
+ <script>
28
+ import ListBox from '@ticatec/uniface-element/list-box';
29
+ import MyItemComponent from './MyItemComponent.svelte';
30
+
31
+ let items = [
32
+ { id: 1, name: '项目 1', description: '第一个项目' },
33
+ { id: 2, name: '项目 2', description: '第二个项目' },
34
+ { id: 3, name: '项目 3', description: '第三个项目' }
35
+ ];
36
+
37
+ let selectedItem = null;
38
+ </script>
39
+
40
+ <ListBox
41
+ list={items}
42
+ itemRender={MyItemComponent}
43
+ selectMode="single"
44
+ bind:selectedItem
45
+ style="width: 300px; height: 400px;"
46
+ />
47
+ ```
48
+
49
+ ### 只读列表显示
50
+
51
+ ```svelte
52
+ <ListBox
53
+ list={items}
54
+ itemRender={ItemRenderer}
55
+ selectMode="none"
56
+ readonly={true}
57
+ style="width: 100%; height: 300px;"
58
+ />
59
+ ```
60
+
61
+ ### 多选模式
62
+
63
+ ```svelte
64
+ <script>
65
+ let selectedList = [];
66
+ </script>
67
+
68
+ <ListBox
69
+ list={items}
70
+ itemRender={ItemRenderer}
71
+ selectMode="multiple"
72
+ bind:selectedList
73
+ title="选择多个项目"
74
+ style="width: 100%; height: 400px;"
75
+ />
76
+
77
+ <p>已选择: {selectedList.length} 个项目</p>
78
+ ```
79
+
80
+ ### 带搜索/过滤功能
81
+
82
+ ```svelte
83
+ <script>
84
+ const filterFunction = (item, searchText) => {
85
+ return item.name.toLowerCase().includes(searchText.toLowerCase()) ||
86
+ item.description.toLowerCase().includes(searchText.toLowerCase());
87
+ };
88
+ </script>
89
+
90
+ <ListBox
91
+ list={items}
92
+ itemRender={ItemRenderer}
93
+ selectMode="single"
94
+ filter={filterFunction}
95
+ title="可搜索列表"
96
+ bind:selectedItem
97
+ />
98
+ ```
99
+
100
+ ### 懒加载
101
+
102
+ ```svelte
103
+ <script>
104
+ const lazyLoader = async (searchText, pageNo) => {
105
+ const response = await fetch(`/api/items?search=${searchText}&page=${pageNo}`);
106
+ const data = await response.json();
107
+
108
+ return {
109
+ list: data.items,
110
+ hasMore: data.hasMore
111
+ };
112
+ };
113
+ </script>
114
+
115
+ <ListBox
116
+ lazyLoader={lazyLoader}
117
+ itemRender={ItemRenderer}
118
+ selectMode="single"
119
+ bind:selectedItem
120
+ style="width: 100%; height: 500px;"
121
+ />
122
+ ```
123
+
124
+ ### 自定义头部和底部
125
+
126
+ ```svelte
127
+ <ListBox
128
+ list={items}
129
+ itemRender={ItemRenderer}
130
+ selectMode="single"
131
+ bind:selectedItem
132
+ >
133
+ <div slot="header" style="padding: 12px; background: #f8f9fa; font-weight: 600;">
134
+ 自定义头部 - 总计: {items.length}
135
+ </div>
136
+
137
+ <div slot="footer" style="padding: 8px; text-align: right; background: #f8f9fa;">
138
+ 已选择: {selectedItem?.name || '无'}
139
+ </div>
140
+ </ListBox>
141
+ ```
142
+
143
+ ## API 参考
144
+
145
+ ### 属性
146
+
147
+ | 属性 | 类型 | 默认值 | 描述 |
148
+ |------|------|---------|-------------|
149
+ | `list` | `Array<any>` | `[]` | 要显示的项目数组 |
150
+ | `itemRender` | `SvelteComponent` | - | 渲染每个项目的组件 |
151
+ | `selectMode` | `'none' \| 'single' \| 'multiple'` | `'none'` | 选择模式 |
152
+ | `selectedItem` | `any` | `null` | 当前选中的项目(单选模式) |
153
+ | `selectedList` | `Array<any>` | `[]` | 选中项目的数组(多选模式) |
154
+ | `filter` | `FunFilter \| null` | `null` | 搜索的过滤函数 |
155
+ | `lazyLoader` | `LazyLoader \| null` | `null` | 懒加载函数 |
156
+ | `readonly` | `boolean` | `false` | 列表是否为只读 |
157
+ | `title` | `string` | - | 头部的标题文本 |
158
+ | `style` | `string` | `''` | 自定义CSS样式 |
159
+ | `header$style` | `string` | `''` | 头部的自定义样式 |
160
+ | `footer$style` | `string` | `''` | 底部的自定义样式 |
161
+ | `round` | `boolean` | `false` | 是否使用圆角 |
162
+ | `item$props` | `any` | `null` | 传递给项目组件的额外属性 |
163
+ | `class` | `string` | `''` | 额外的CSS类 |
164
+
165
+ ### 事件
166
+
167
+ | 事件 | 类型 | 描述 |
168
+ |-------|------|-------------|
169
+ | `onSelectChange` | `(item: any) => void` | 选择改变时触发(单选模式) |
170
+ | `onItemClick` | `(item: any) => void` | 项目被点击时触发 |
171
+ | `onItemDblClick` | `(item: any) => void` | 项目被双击时触发 |
172
+
173
+ ### 插槽
174
+
175
+ | 插槽 | 描述 |
176
+ |------|-------------|
177
+ | `header` | 自定义头部内容 |
178
+ | `footer` | 自定义底部内容 |
179
+ | `loadMoreIndicator` | 懒加载的自定义加载指示器 |
180
+
181
+ ### 类型定义
182
+
183
+ ```typescript
184
+ // 过滤函数类型
185
+ type FunFilter = (item: any, searchText: string) => boolean;
186
+
187
+ // 懒加载函数类型
188
+ type LazyLoader = (searchText: string, pageNo: number) => Promise<LoadResult>;
189
+
190
+ // 加载结果接口
191
+ interface LoadResult {
192
+ list: Array<any>;
193
+ hasMore: boolean;
194
+ }
195
+ ```
196
+
197
+ ## 自定义项目渲染器
198
+
199
+ 创建自定义组件来渲染列表项:
200
+
201
+ ```svelte
202
+ <!-- EmployeeRenderer.svelte -->
203
+ <script lang="ts">
204
+ export let item;
205
+ export let selected = false;
206
+ export let readonly = false;
207
+ </script>
208
+
209
+ <div class="employee-item" class:selected>
210
+ <div class="employee-name">{item.name}</div>
211
+ <div class="employee-department">{item.department}</div>
212
+ <div class="employee-phone">{item.phone}</div>
213
+ </div>
214
+
215
+ <style>
216
+ .employee-item {
217
+ padding: 12px;
218
+ border-radius: 4px;
219
+ transition: background-color 0.2s;
220
+ }
221
+
222
+ .employee-item.selected {
223
+ background-color: #e3f2fd;
224
+ }
225
+
226
+ .employee-name {
227
+ font-weight: 600;
228
+ font-size: 1.1em;
229
+ margin-bottom: 4px;
230
+ }
231
+
232
+ .employee-department {
233
+ color: #666;
234
+ font-size: 0.9em;
235
+ }
236
+
237
+ .employee-phone {
238
+ color: #888;
239
+ font-size: 0.8em;
240
+ margin-top: 4px;
241
+ }
242
+ </style>
243
+ ```
244
+
245
+ ## 示例
246
+
247
+ ### 员工目录
248
+
249
+ ```svelte
250
+ <script>
251
+ import ListBox from '@ticatec/uniface-element/list-box';
252
+ import EmployeeRenderer from './EmployeeRenderer.svelte';
253
+
254
+ let employees = [
255
+ { id: 1, name: '张三', department: '工程部', phone: '+86-138-0013-8000' },
256
+ { id: 2, name: '李四', department: '市场部', phone: '+86-138-0013-8001' },
257
+ { id: 3, name: '王五', department: '销售部', phone: '+86-138-0013-8002' }
258
+ ];
259
+
260
+ let selectedEmployee = null;
261
+
262
+ const filterEmployees = (employee, searchText) => {
263
+ const text = searchText.toLowerCase();
264
+ return employee.name.toLowerCase().includes(text) ||
265
+ employee.department.toLowerCase().includes(text);
266
+ };
267
+
268
+ function handleEmployeeSelect(employee) {
269
+ console.log('选中员工:', employee);
270
+ }
271
+ </script>
272
+
273
+ <ListBox
274
+ list={employees}
275
+ itemRender={EmployeeRenderer}
276
+ selectMode="single"
277
+ filter={filterEmployees}
278
+ bind:selectedItem={selectedEmployee}
279
+ onSelectChange={handleEmployeeSelect}
280
+ title="员工目录"
281
+ style="width: 400px; height: 500px;"
282
+ >
283
+ <div slot="footer" style="padding: 8px; text-align: center; background: #f5f5f5;">
284
+ 总员工数: {employees.length}
285
+ {#if selectedEmployee}
286
+ | 已选择: {selectedEmployee.name}
287
+ {/if}
288
+ </div>
289
+ </ListBox>
290
+ ```
291
+
292
+ ### 带懒加载的产品目录
293
+
294
+ ```svelte
295
+ <script>
296
+ import ListBox from '@ticatec/uniface-element/list-box';
297
+ import ProductRenderer from './ProductRenderer.svelte';
298
+
299
+ let selectedProducts = [];
300
+
301
+ const loadProducts = async (searchText, pageNo) => {
302
+ const params = new URLSearchParams({
303
+ search: searchText || '',
304
+ page: pageNo.toString(),
305
+ limit: '20'
306
+ });
307
+
308
+ try {
309
+ const response = await fetch(`/api/products?${params}`);
310
+ const data = await response.json();
311
+
312
+ return {
313
+ list: data.products,
314
+ hasMore: data.totalPages > pageNo
315
+ };
316
+ } catch (error) {
317
+ console.error('加载产品失败:', error);
318
+ return { list: [], hasMore: false };
319
+ }
320
+ };
321
+ </script>
322
+
323
+ <ListBox
324
+ lazyLoader={loadProducts}
325
+ itemRender={ProductRenderer}
326
+ selectMode="multiple"
327
+ bind:selectedList={selectedProducts}
328
+ style="width: 100%; height: 600px;"
329
+ >
330
+ <div slot="header" style="padding: 12px; background: #2196f3; color: white;">
331
+ <h3 style="margin: 0;">产品目录</h3>
332
+ </div>
333
+
334
+ <div slot="footer" style="padding: 8px; background: #f0f0f0; text-align: right;">
335
+ 已选择: {selectedProducts.length} 个产品
336
+ <button
337
+ on:click={() => selectedProducts = []}
338
+ disabled={selectedProducts.length === 0}
339
+ >
340
+ 清除选择
341
+ </button>
342
+ </div>
343
+
344
+ <div slot="loadMoreIndicator" style="padding: 16px; text-align: center;">
345
+ <div class="loading-spinner"></div>
346
+ 正在加载更多产品...
347
+ </div>
348
+ </ListBox>
349
+ ```
350
+
351
+ ### 带操作的联系人列表
352
+
353
+ ```svelte
354
+ <script>
355
+ import ListBox from '@ticatec/uniface-element/list-box';
356
+ import ContactRenderer from './ContactRenderer.svelte';
357
+
358
+ let contacts = [
359
+ { id: 1, name: '张小明', email: 'zhangxm@example.com', phone: '+86-138-0001-0001' },
360
+ { id: 2, name: '李小红', email: 'lixh@example.com', phone: '+86-138-0001-0002' }
361
+ ];
362
+
363
+ let selectedContact = null;
364
+
365
+ function handleContactDblClick(contact) {
366
+ // 打开联系人详情或编辑模态框
367
+ console.log('打开联系人:', contact);
368
+ }
369
+
370
+ function handleContactCall(contact) {
371
+ console.log('拨打电话:', contact.phone);
372
+ }
373
+
374
+ function handleContactEmail(contact) {
375
+ window.location.href = `mailto:${contact.email}`;
376
+ }
377
+ </script>
378
+
379
+ <ListBox
380
+ list={contacts}
381
+ itemRender={ContactRenderer}
382
+ selectMode="single"
383
+ bind:selectedItem={selectedContact}
384
+ onItemDblClick={handleContactDblClick}
385
+ item$props={{ onCall: handleContactCall, onEmail: handleContactEmail }}
386
+ title="联系人"
387
+ style="width: 350px; height: 450px;"
388
+ >
389
+ <div slot="footer" style="padding: 12px; display: flex; gap: 8px;">
390
+ <button
391
+ disabled={!selectedContact}
392
+ on:click={() => handleContactCall(selectedContact)}
393
+ >
394
+ 拨打电话
395
+ </button>
396
+ <button
397
+ disabled={!selectedContact}
398
+ on:click={() => handleContactEmail(selectedContact)}
399
+ >
400
+ 发送邮件
401
+ </button>
402
+ </div>
403
+ </ListBox>
404
+ ```
405
+
406
+ ## 样式
407
+
408
+ ### CSS 变量
409
+
410
+ ListBox组件使用CSS变量进行一致的主题化:
411
+
412
+ ```css
413
+ :root {
414
+ --uniface-listbox-separator-color: #f0f0f0;
415
+ --uniface-list-box-hover-bg: #ffffe1;
416
+ --uniface-list-box-selected-bg: #b1c8f6;
417
+ --uniface-list-box-header-bottom-border: 1px solid #f0f0f0;
418
+ --uniface-list-box-border-color: #f0f0f0;
419
+ --uniface-list-box-header-bg: #f8fafc;
420
+ --uniface-list-box-footer-bg: #f8fafc;
421
+ }
422
+ ```
423
+
424
+ ### 自定义样式
425
+
426
+ ```css
427
+ .custom-listbox {
428
+ --uniface-list-box-hover-bg: #e3f2fd;
429
+ --uniface-list-box-selected-bg: #1976d2;
430
+ --uniface-list-box-border-color: #1976d2;
431
+ border-radius: 8px;
432
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
433
+ }
434
+
435
+ .custom-listbox .listview-item {
436
+ transition: all 0.2s ease;
437
+ }
438
+
439
+ .custom-listbox .box-header {
440
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
441
+ color: white;
442
+ }
443
+ ```
444
+
445
+ ## 无障碍功能
446
+
447
+ - **键盘导航**: 方向键导航,回车键选择
448
+ - **屏幕阅读器支持**: 适当的ARIA标签和角色
449
+ - **焦点管理**: 可见的焦点指示器和逻辑tab顺序
450
+ - **选择通知**: 选择改变时的屏幕阅读器反馈
451
+ - **搜索无障碍**: 搜索功能的适当标签
452
+
453
+ ## 性能考虑
454
+
455
+ ### 大型列表
456
+
457
+ 对于大型数据集,使用懒加载:
458
+
459
+ ```svelte
460
+ <script>
461
+ const lazyLoader = async (searchText, pageNo) => {
462
+ // 实现高效的服务器端分页
463
+ const response = await fetch(`/api/items?search=${searchText}&page=${pageNo}&limit=50`);
464
+ return await response.json();
465
+ };
466
+ </script>
467
+
468
+ <ListBox
469
+ lazyLoader={lazyLoader}
470
+ itemRender={OptimizedItemRenderer}
471
+ style="height: 400px;"
472
+ />
473
+ ```
474
+
475
+ ### 虚拟滚动
476
+
477
+ 对于极大的列表,可以在项目渲染器中实现虚拟滚动:
478
+
479
+ ```svelte
480
+ <!-- VirtualizedItemRenderer.svelte -->
481
+ <script>
482
+ export let item;
483
+ export let index;
484
+
485
+ // 在这里实现虚拟滚动逻辑
486
+ </script>
487
+ ```
488
+
489
+ ## 最佳实践
490
+
491
+ ### 1. 优化项目渲染器
492
+
493
+ 保持项目渲染器简单高效:
494
+
495
+ ```svelte
496
+ <!-- 好的做法:简单高效 -->
497
+ <script>
498
+ export let item;
499
+ </script>
500
+
501
+ <div class="item">
502
+ <h4>{item.title}</h4>
503
+ <p>{item.description}</p>
504
+ </div>
505
+
506
+ <!-- 避免:在渲染器中进行重计算 -->
507
+ <script>
508
+ export let item;
509
+
510
+ // 不要在这里进行重计算
511
+ $: processedData = heavyComputation(item);
512
+ </script>
513
+ ```
514
+
515
+ ### 2. 实现适当的加载状态
516
+
517
+ ```svelte
518
+ <ListBox
519
+ lazyLoader={dataLoader}
520
+ itemRender={ItemRenderer}
521
+ >
522
+ <div slot="loadMoreIndicator" class="loading-indicator">
523
+ <div class="spinner"></div>
524
+ 正在加载更多项目...
525
+ </div>
526
+ </ListBox>
527
+ ```
528
+
529
+ ### 3. 处理空状态
530
+
531
+ ```svelte
532
+ <script>
533
+ let items = [];
534
+ let isLoading = false;
535
+ </script>
536
+
537
+ {#if isLoading}
538
+ <div class="loading-state">正在加载...</div>
539
+ {:else if items.length === 0}
540
+ <div class="empty-state">未找到项目</div>
541
+ {:else}
542
+ <ListBox list={items} itemRender={ItemRenderer} />
543
+ {/if}
544
+ ```
545
+
546
+ ### 4. 提供清晰的选择反馈
547
+
548
+ ```svelte
549
+ <ListBox
550
+ bind:selectedItem
551
+ selectMode="single"
552
+ style="--uniface-list-box-selected-bg: #2196f3; --uniface-list-box-hover-bg: #e3f2fd;"
553
+ />
554
+
555
+ <div class="selection-info">
556
+ {#if selectedItem}
557
+ 已选择: {selectedItem.name}
558
+ {:else}
559
+ 未选择项目
560
+ {/if}
561
+ </div>
562
+ ```
563
+
564
+ ## 组件优化建议
565
+
566
+ 分析代码后,发现以下可以优化的地方:
567
+
568
+ ### 1. 性能优化
569
+
570
+ ```svelte
571
+ <!-- 当前实现中的问题 -->
572
+ <script>
573
+ // 在每次过滤时都重新创建数组
574
+ const doFilter = () => {
575
+ const newList: Array<any> = [];
576
+ list.forEach(item => {
577
+ if (filter?.(item, filterText)) {
578
+ newList.push(item)
579
+ }
580
+ filteredList = newList; // 这行代码位置有问题
581
+ });
582
+ }
583
+ </script>
584
+ ```
585
+
586
+ 建议优化为:
587
+
588
+ ```svelte
589
+ <script>
590
+ const doFilter = () => {
591
+ if (!list || !filter) {
592
+ filteredList = list || [];
593
+ return;
594
+ }
595
+
596
+ filteredList = list.filter(item => filter(item, filterText));
597
+
598
+ // 清除单选状态
599
+ if (selectMode === 'single') {
600
+ selectedItem = null;
601
+ onSelectChange?.(null);
602
+ }
603
+ };
604
+ </script>
605
+ ```
606
+
607
+ ### 2. 键盘导航优化
608
+
609
+ 当前缺少键盘导航支持,建议添加:
610
+
611
+ ```svelte
612
+ <script>
613
+ let focusedIndex = -1;
614
+
615
+ function handleKeyDown(event) {
616
+ if (!filteredList || filteredList.length === 0) return;
617
+
618
+ switch (event.key) {
619
+ case 'ArrowDown':
620
+ event.preventDefault();
621
+ focusedIndex = Math.min(focusedIndex + 1, filteredList.length - 1);
622
+ break;
623
+ case 'ArrowUp':
624
+ event.preventDefault();
625
+ focusedIndex = Math.max(focusedIndex - 1, 0);
626
+ break;
627
+ case 'Enter':
628
+ if (focusedIndex >= 0 && selectMode === 'single') {
629
+ selectedItem = filteredList[focusedIndex];
630
+ onSelectChange?.(selectedItem);
631
+ }
632
+ break;
633
+ }
634
+ }
635
+ </script>
636
+
637
+ <div on:keydown={handleKeyDown} tabindex="0">
638
+ <!-- 列表内容 -->
639
+ </div>
640
+ ```
641
+
642
+ ### 3. 无障碍访问优化
643
+
644
+ 添加适当的ARIA属性:
645
+
646
+ ```svelte
647
+ <div
648
+ class="listbox-content"
649
+ role="listbox"
650
+ aria-multiselectable={selectMode === 'multiple'}
651
+ bind:this={scrollElement}
652
+ on:scroll={handleScroll}
653
+ >
654
+ {#each filteredList as item, index}
655
+ <div
656
+ class="listview-item"
657
+ role="option"
658
+ aria-selected={selectMode === 'single' ? item === selectedItem : selectedList.includes(item)}
659
+ tabindex="-1"
660
+ >
661
+ <!-- 项目内容 -->
662
+ </div>
663
+ {/each}
664
+ </div>
665
+ ```
666
+
667
+ ## 浏览器支持
668
+
669
+ - **现代浏览器**: Chrome、Firefox、Safari、Edge(最新版本)
670
+ - **移动浏览器**: iOS Safari、Chrome Mobile、Firefox Mobile
671
+ - **Flexbox支持**: 布局功能必需
672
+ - **Intersection Observer**: 懒加载需要(可提供polyfill)
673
+
674
+ ## 相关组件
675
+
676
+ - **SearchBox**: 内部用于过滤功能
677
+ - **CheckBox**: 用于多选模式
678
+ - **Box**: 基础布局组件
679
+
680
+ ## 许可证
681
+
682
+ MIT许可证 - 详情请参阅LICENSE文件。
@@ -6,6 +6,8 @@
6
6
  import CommonPicker from "../common/CommonPicker.svelte";
7
7
 
8
8
  type OnActionHandler = () => void;
9
+ type OnFocusHandler = (event: FocusEvent) => void;
10
+ type OnBlurHandler = (event: FocusEvent) => void;
9
11
 
10
12
  export let variant: '' | 'plain' | 'outlined' | 'filled' = '';
11
13
  export let disabled: boolean = false;
@@ -17,8 +19,10 @@
17
19
  export let placeholder: string = "";
18
20
  export let displayMode: DisplayMode = DisplayMode.Edit;
19
21
  export let text: string;
20
- export let onChange: OnChangeHandler<any> = null as unknown as OnChangeHandler<any>;
22
+ export let onchange: OnChangeHandler<any> = null as unknown as OnChangeHandler<any>;
21
23
  export let onAction: OnActionHandler = null as unknown as OnActionHandler;
24
+ export let onfocus: OnFocusHandler = null as unknown as OnFocusHandler;
25
+ export let onblur: OnBlurHandler = null as unknown as OnBlurHandler;
22
26
 
23
27
  let oldValue = value;
24
28
 
@@ -29,17 +33,25 @@
29
33
  const clean = () => {
30
34
  value = null;
31
35
  text = '';
32
- onChange?.(value)
36
+ onchange?.(value)
33
37
  }
34
38
 
35
39
  const handleActionIconClick = async (event: MouseEvent) => {
36
40
  onAction?.();
37
41
  }
38
42
 
43
+ const handleFocus = (event: FocusEvent) => {
44
+ onfocus?.(event);
45
+ }
46
+
47
+ const handleBlur = (event: FocusEvent) => {
48
+ onblur?.(event);
49
+ }
50
+
39
51
  </script>
40
52
 
41
53
  <CommonPicker {displayMode} {variant} {style} {compact} className="multiple" dropDownIcon="icon_google_keyboard_control"
42
54
  canClean={!mandatory && value!=null} autoFit {clean} textValue={text} {readonly} {disabled} iconClickHandler={handleActionIconClick}>
43
55
  <input style="width: 100%" {placeholder} class="text-editor" readonly value={text??''} {disabled}
44
- on:keydown on:focus on:click={handleActionIconClick}/>
56
+ on:focus={handleFocus} on:blur={handleBlur} on:click={handleActionIconClick}/>
45
57
  </CommonPicker>