@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,443 @@
1
+ # NumberRange 数字范围
2
+
3
+ 用于指定数字范围的双数字输入组件,具有自动验证功能,确保"起始"值不超过"结束"值。
4
+
5
+ ## 功能特性
6
+
7
+ - **范围输入**: 两个连接的数字输入框,用于"起始"和"结束"值
8
+ - **自动验证**: 确保逻辑范围顺序(起始 ≤ 结束)
9
+ - **负数支持**: 可选的负数值支持
10
+ - **边界约束**: 为整个范围设置最小/最大限制
11
+ - **多种变体**: 支持轮廓、填充、朴素和默认样式
12
+ - **紧凑模式**: 用于密集表单的节省空间布局
13
+ - **清除功能**: 内置清除按钮重置两个值
14
+ - **实时验证**: 输入过程中的即时反馈
15
+ - **焦点管理**: 输入框之间的自动焦点处理
16
+
17
+ ## 安装
18
+
19
+ ```bash
20
+ npm install @ticatec/uniface-element
21
+ ```
22
+
23
+ ## 基本用法
24
+
25
+ ```svelte
26
+ <script>
27
+ import NumberRange from '@ticatec/uniface-element/NumberRange';
28
+
29
+ let fromValue = 10;
30
+ let toValue = 50;
31
+ </script>
32
+
33
+ <NumberRange bind:fromValue bind:toValue />
34
+ ```
35
+
36
+ ## API 参考
37
+
38
+ ### 属性
39
+
40
+ | 属性 | 类型 | 默认值 | 描述 |
41
+ |------|------|---------|-------------|
42
+ | `fromValue` | `number \| null` | `null` | 范围的"起始"(最小)值 |
43
+ | `toValue` | `number \| null` | `null` | 范围的"结束"(最大)值 |
44
+ | `variant` | `'' \| 'plain' \| 'outlined' \| 'filled'` | `''` | 视觉样式变体 |
45
+ | `compact` | `boolean` | `false` | 启用紧凑显示模式 |
46
+ | `style` | `string` | `''` | 自定义CSS样式 |
47
+ | `min` | `number \| null` | `null` | 两个输入框的最小允许值 |
48
+ | `max` | `number \| null` | `null` | 两个输入框的最大允许值 |
49
+ | `allowNegative` | `boolean` | `false` | 允许两个输入框输入负数 |
50
+ | `class` | `string` | `''` | CSS类名 |
51
+
52
+ ## 示例
53
+
54
+ ### 基本范围选择
55
+
56
+ ```svelte
57
+ <script>
58
+ import NumberRange from '@ticatec/uniface-element/NumberRange';
59
+ import FormField from '@ticatec/uniface-element/FormField';
60
+
61
+ let ageFrom = 18;
62
+ let ageTo = 65;
63
+ </script>
64
+
65
+ <FormField label="年龄范围">
66
+ <NumberRange
67
+ bind:fromValue={ageFrom}
68
+ bind:toValue={ageTo}
69
+ variant="outlined"
70
+ min={0}
71
+ max={120}
72
+ />
73
+ </FormField>
74
+ ```
75
+
76
+ ### 价格范围过滤器
77
+
78
+ ```svelte
79
+ <script>
80
+ import NumberRange from '@ticatec/uniface-element/NumberRange';
81
+
82
+ let priceMin = 100;
83
+ let priceMax = 1000;
84
+
85
+ $: console.log(`价格范围: ¥${priceMin} - ¥${priceMax}`);
86
+ </script>
87
+
88
+ <div class="price-filter">
89
+ <label>价格范围 (¥)</label>
90
+ <NumberRange
91
+ bind:fromValue={priceMin}
92
+ bind:toValue={priceMax}
93
+ variant="filled"
94
+ min={0}
95
+ max={10000}
96
+ />
97
+ <div class="range-display">
98
+ ¥{priceMin || 0} - ¥{priceMax || 0}
99
+ </div>
100
+ </div>
101
+ ```
102
+
103
+ ### 温度范围(支持负值)
104
+
105
+ ```svelte
106
+ <script>
107
+ import NumberRange from '@ticatec/uniface-element/NumberRange';
108
+
109
+ let tempMin = -10;
110
+ let tempMax = 25;
111
+ </script>
112
+
113
+ <div class="temperature-range">
114
+ <label>温度范围 (°C)</label>
115
+ <NumberRange
116
+ bind:fromValue={tempMin}
117
+ bind:toValue={tempMax}
118
+ variant="outlined"
119
+ allowNegative={true}
120
+ min={-50}
121
+ max={50}
122
+ />
123
+ </div>
124
+ ```
125
+
126
+ ### 搜索条件表单
127
+
128
+ ```svelte
129
+ <script>
130
+ import NumberRange from '@ticatec/uniface-element/NumberRange';
131
+ import CriteriaField from '@ticatec/uniface-element/CriteriaField';
132
+
133
+ let criteria = {
134
+ salaryFrom: null,
135
+ salaryTo: null,
136
+ experienceFrom: 0,
137
+ experienceTo: 10,
138
+ ageFrom: 22,
139
+ ageTo: 60
140
+ };
141
+
142
+ function handleSearch() {
143
+ console.log('搜索条件:', criteria);
144
+ }
145
+ </script>
146
+
147
+ <div class="search-form">
148
+ <CriteriaField label="薪资范围" size="x30">
149
+ <NumberRange
150
+ bind:fromValue={criteria.salaryFrom}
151
+ bind:toValue={criteria.salaryTo}
152
+ variant="outlined"
153
+ min={0}
154
+ max={500000}
155
+ />
156
+ </CriteriaField>
157
+
158
+ <CriteriaField label="工作年限" size="x30">
159
+ <NumberRange
160
+ bind:fromValue={criteria.experienceFrom}
161
+ bind:toValue={criteria.experienceTo}
162
+ variant="outlined"
163
+ min={0}
164
+ max={50}
165
+ />
166
+ </CriteriaField>
167
+
168
+ <CriteriaField label="年龄范围" size="x30">
169
+ <NumberRange
170
+ bind:fromValue={criteria.ageFrom}
171
+ bind:toValue={criteria.ageTo}
172
+ variant="outlined"
173
+ min={16}
174
+ max={80}
175
+ />
176
+ </CriteriaField>
177
+
178
+ <button on:click={handleSearch}>搜索</button>
179
+ </div>
180
+ ```
181
+
182
+ ### 表格紧凑布局
183
+
184
+ ```svelte
185
+ <script>
186
+ import NumberRange from '@ticatec/uniface-element/NumberRange';
187
+
188
+ let filters = [
189
+ { label: '分数', from: 80, to: 100 },
190
+ { label: '数量', from: 1, to: 999 },
191
+ { label: '评级', from: 3, to: 5 }
192
+ ];
193
+ </script>
194
+
195
+ <table class="filter-table">
196
+ <thead>
197
+ <tr>
198
+ <th>过滤器</th>
199
+ <th>范围</th>
200
+ </tr>
201
+ </thead>
202
+ <tbody>
203
+ {#each filters as filter}
204
+ <tr>
205
+ <td>{filter.label}</td>
206
+ <td>
207
+ <NumberRange
208
+ bind:fromValue={filter.from}
209
+ bind:toValue={filter.to}
210
+ compact
211
+ variant="outlined"
212
+ min={0}
213
+ />
214
+ </td>
215
+ </tr>
216
+ {/each}
217
+ </tbody>
218
+ </table>
219
+ ```
220
+
221
+ ### 带验证的动态范围
222
+
223
+ ```svelte
224
+ <script>
225
+ import NumberRange from '@ticatec/uniface-element/NumberRange';
226
+
227
+ let budgetMin = 1000;
228
+ let budgetMax = 5000;
229
+ let isValidRange = true;
230
+
231
+ $: isValidRange = budgetMin <= budgetMax;
232
+ $: rangeSize = budgetMax - budgetMin;
233
+
234
+ function resetRange() {
235
+ budgetMin = 1000;
236
+ budgetMax = 5000;
237
+ }
238
+ </script>
239
+
240
+ <div class="budget-selector">
241
+ <label>预算范围</label>
242
+ <NumberRange
243
+ bind:fromValue={budgetMin}
244
+ bind:toValue={budgetMax}
245
+ variant="outlined"
246
+ min={0}
247
+ max={100000}
248
+ />
249
+
250
+ <div class="range-info" class:error={!isValidRange}>
251
+ {#if isValidRange}
252
+ <span>范围大小: ¥{rangeSize}</span>
253
+ {:else}
254
+ <span>无效范围: 最小值不能超过最大值</span>
255
+ {/if}
256
+ </div>
257
+
258
+ <button on:click={resetRange}>重置</button>
259
+ </div>
260
+
261
+ <style>
262
+ .range-info.error {
263
+ color: #d32f2f;
264
+ }
265
+ </style>
266
+ ```
267
+
268
+ ### 统计分析范围
269
+
270
+ ```svelte
271
+ <script>
272
+ import NumberRange from '@ticatec/uniface-element/NumberRange';
273
+
274
+ let dataRange = { min: -100, max: 100 };
275
+ let outlierThreshold = { from: -50, to: 50 };
276
+
277
+ $: normalDataPercentage = calculateNormalData(dataRange, outlierThreshold);
278
+
279
+ function calculateNormalData(range, threshold) {
280
+ const totalRange = range.max - range.min;
281
+ const normalRange = threshold.to - threshold.from;
282
+ return ((normalRange / totalRange) * 100).toFixed(1);
283
+ }
284
+ </script>
285
+
286
+ <div class="analysis-panel">
287
+ <h3>数据分析</h3>
288
+
289
+ <div class="range-control">
290
+ <label>数据范围</label>
291
+ <NumberRange
292
+ bind:fromValue={dataRange.min}
293
+ bind:toValue={dataRange.max}
294
+ variant="filled"
295
+ allowNegative={true}
296
+ min={-1000}
297
+ max={1000}
298
+ />
299
+ </div>
300
+
301
+ <div class="range-control">
302
+ <label>正常范围(排除异常值)</label>
303
+ <NumberRange
304
+ bind:fromValue={outlierThreshold.from}
305
+ bind:toValue={outlierThreshold.to}
306
+ variant="outlined"
307
+ allowNegative={true}
308
+ min={dataRange.min}
309
+ max={dataRange.max}
310
+ />
311
+ </div>
312
+
313
+ <div class="statistics">
314
+ <p>正常数据覆盖率: {normalDataPercentage}%</p>
315
+ </div>
316
+ </div>
317
+ ```
318
+
319
+ ### 库存管理
320
+
321
+ ```svelte
322
+ <script>
323
+ import NumberRange from '@ticatec/uniface-element/NumberRange';
324
+
325
+ let stockLevels = {
326
+ lowStock: 0,
327
+ highStock: 100,
328
+ reorderFrom: 10,
329
+ reorderTo: 90
330
+ };
331
+
332
+ $: isValidReorderRange = (
333
+ stockLevels.reorderFrom >= stockLevels.lowStock &&
334
+ stockLevels.reorderTo <= stockLevels.highStock &&
335
+ stockLevels.reorderFrom <= stockLevels.reorderTo
336
+ );
337
+ </script>
338
+
339
+ <div class="inventory-config">
340
+ <h3>库存配置</h3>
341
+
342
+ <div class="config-row">
343
+ <label>库存水平范围</label>
344
+ <NumberRange
345
+ bind:fromValue={stockLevels.lowStock}
346
+ bind:toValue={stockLevels.highStock}
347
+ variant="outlined"
348
+ min={0}
349
+ max={10000}
350
+ />
351
+ </div>
352
+
353
+ <div class="config-row">
354
+ <label>补货范围</label>
355
+ <NumberRange
356
+ bind:fromValue={stockLevels.reorderFrom}
357
+ bind:toValue={stockLevels.reorderTo}
358
+ variant="filled"
359
+ min={stockLevels.lowStock}
360
+ max={stockLevels.highStock}
361
+ />
362
+ </div>
363
+
364
+ <div class="validation" class:valid={isValidReorderRange}>
365
+ {isValidReorderRange ? '✓ 配置有效' : '✗ 补货范围无效'}
366
+ </div>
367
+ </div>
368
+
369
+ <style>
370
+ .validation.valid {
371
+ color: #2e7d32;
372
+ }
373
+ .validation:not(.valid) {
374
+ color: #d32f2f;
375
+ }
376
+ </style>
377
+ ```
378
+
379
+ ## 验证逻辑
380
+
381
+ NumberRange 组件自动执行逻辑约束:
382
+
383
+ 1. **范围顺序**: "起始"值不能超过"结束"值
384
+ 2. **边界遵守**: 两个值都遵守全局最小/最大约束
385
+ 3. **交叉验证**: 当一个值改变时,必要时另一个值会调整
386
+ 4. **实时反馈**: 在输入过程中进行验证
387
+
388
+ ## 最佳实践
389
+
390
+ 1. **设置适当的边界**: 使用 min/max 防止不现实的值
391
+ 2. **提供清晰的标签**: 指明范围代表什么
392
+ 3. **处理空状态**: 考虑值为 null 时的情况
393
+ 4. **明智使用紧凑模式**: 在密集布局或表格中
394
+ 5. **验证范围逻辑**: 确保满足业务规则
395
+ 6. **考虑用户工作流**: 用户通常应该先填写哪个输入框
396
+ 7. **提供反馈**: 显示范围计算或验证消息
397
+
398
+ ## 样式
399
+
400
+ NumberRange 组件使用以下 CSS 结构:
401
+
402
+ ```css
403
+ .uniface-range-editor {
404
+ display: flex;
405
+ align-items: center;
406
+ border: 1px solid #e0e0e0;
407
+ border-radius: 4px;
408
+ }
409
+
410
+ .uniface-range-editor .divider {
411
+ width: 8px;
412
+ height: 1px;
413
+ background: #ccc;
414
+ margin: 0 4px;
415
+ }
416
+
417
+ .uniface-range-editor.compact {
418
+ padding: 4px 8px;
419
+ }
420
+
421
+ .uniface-range-editor.outlined {
422
+ border: 2px solid #1976d2;
423
+ }
424
+
425
+ .uniface-range-editor.filled {
426
+ background-color: #f5f5f5;
427
+ }
428
+ ```
429
+
430
+ ## 无障碍功能
431
+
432
+ - 为屏幕阅读器提供适当的 ARIA 标签
433
+ - 输入框之间的键盘导航
434
+ - 焦点管理和视觉指示器
435
+ - 支持高对比度模式
436
+ - 逻辑的 Tab 顺序
437
+
438
+ ## 相关组件
439
+
440
+ - [NumberEditor](../number-editor/README.md) - 用于单个数字输入
441
+ - [DateRange](../date-range/README.md) - 用于日期范围选择
442
+ - [CriteriaField](../criteria-field/README.md) - 用于搜索表单布局
443
+ - [FormField](../form-field/README.md) - 用于表单字段标签和布局
@@ -1,7 +1,6 @@
1
1
  <script lang="ts">
2
2
 
3
3
  import {DisplayMode} from "../common/DisplayMode";
4
- import CheckBox from "../checkbox/CheckBox.svelte";
5
4
  import CommonPicker from "../common/CommonPicker.svelte";
6
5
  import utils from "../common/utils";
7
6
  import type {OnChangeHandler} from "../common/OnChangeHandler";
@@ -24,7 +23,9 @@
24
23
  export let disableOptions: Array<string> = [];
25
24
  export let hideOptions: Array<string> = [];
26
25
  export let displayMode: DisplayMode = DisplayMode.Edit;
27
- export let onChange: OnChangeHandler<Array<string>> = null as unknown as OnChangeHandler<Array<string>>;
26
+ export let onchange: OnChangeHandler<Array<string>> = null as unknown as OnChangeHandler<Array<string>>;
27
+ export let onfocus: (() => void) | null = null;
28
+ export let onblur: (() => void) | null = null;
28
29
  export let tagColor: string = '';
29
30
  export let tagVariant: 'borderless' | 'border' | 'round' = 'border';
30
31
 
@@ -35,12 +36,15 @@
35
36
  }
36
37
 
37
38
  let selectedList: Array<string> = [];
38
-
39
39
  let textValue: string;
40
40
  let mh;
41
+ let hasFocus: boolean = false;
41
42
 
42
43
 
43
- const handleChange = (key: string) => (checked: boolean) => {
44
+ const handleCheckboxChange = (key: string) => (event: Event) => {
45
+ const target = event.target as HTMLInputElement;
46
+ const checked = target.checked;
47
+
44
48
  if (checked) {
45
49
  if (selectedList.indexOf(key) < 0) {
46
50
  selectedList.push(key);
@@ -50,9 +54,23 @@
50
54
  } else {
51
55
  removeSelectCode(key);
52
56
  }
53
- onChange?.(selectedList);
54
- editor.focus();
57
+ onchange?.(selectedList);
58
+
59
+ // 确保焦点状态正确
60
+ if (!hasFocus) {
61
+ editor.focus();
62
+ }
63
+ }
55
64
 
65
+ const handleOptionClick = (key: string) => (event: MouseEvent) => {
66
+ if (disableOptions.indexOf(key) > -1) return;
67
+
68
+ const checkbox = event.currentTarget.querySelector('input[type="checkbox"]') as HTMLInputElement;
69
+ if (checkbox && !checkbox.disabled) {
70
+ checkbox.checked = !checkbox.checked;
71
+ handleCheckboxChange(key)({ target: checkbox } as Event);
72
+ }
73
+ event.preventDefault();
56
74
  }
57
75
 
58
76
  const getOptionItem = (code: string) => {
@@ -81,6 +99,15 @@
81
99
 
82
100
  const removeOption = (code: string) => async (e: MouseEvent) => {
83
101
  removeSelectCode(code);
102
+ onchange?.(selectedList);
103
+
104
+ // 删除tag后确保获得焦点
105
+ if (!hasFocus) {
106
+ setTimeout(() => {
107
+ editor?.focus();
108
+ }, 10);
109
+ }
110
+
84
111
  e.stopPropagation();
85
112
  e.preventDefault();
86
113
  }
@@ -91,9 +118,30 @@
91
118
  }
92
119
  }
93
120
 
121
+ const handleFocus = () => {
122
+ if (!hasFocus) {
123
+ hasFocus = true;
124
+ onfocus?.();
125
+ }
126
+ };
127
+
128
+ const handleBlur = (event: FocusEvent) => {
129
+ if (!showPopup) {
130
+ hasFocus = false;
131
+ onblur?.();
132
+ }
133
+ };
134
+
94
135
  const cleanData = () => {
95
136
  value = '';
96
- onChange?.([])
137
+ onchange?.([]);
138
+
139
+ // 清除所有选项后确保获得焦点
140
+ if (!hasFocus) {
141
+ setTimeout(() => {
142
+ editor?.focus();
143
+ }, 10);
144
+ }
97
145
  }
98
146
 
99
147
  let showPopup: boolean = false;
@@ -108,13 +156,35 @@
108
156
  editor.focus();
109
157
  }
110
158
 
159
+ // 监听popup关闭,如果是点击外部关闭且之前有焦点,触发blur
160
+ $: if (!showPopup && hasFocus) {
161
+ // 延迟检查,如果input仍然有焦点,说明是选择项目导致的关闭
162
+ setTimeout(() => {
163
+ if (document.activeElement !== editor) {
164
+ hasFocus = false;
165
+ onblur?.();
166
+ }
167
+ }, 50);
168
+ }
169
+
111
170
  </script>
112
171
 
113
172
  <CommonPicker {displayMode} {variant} {style} {compact} className="multiple" dropDownIcon="icon_google_arrow_drop_down"
114
173
  bind:showPopup canClean={value!=null} autoFit clean={cleanData} {textValue} {readonly} {disabled}>
115
174
 
116
- <div class="option-field" aria-hidden="true" on:click={()=>{showPopup = true}}>
117
- <input bind:this={editor} style="position: absolute; left: 1px; width: 1px; visibility: hidden" on:keydown={handleKeyDown}/>
175
+ <div class="option-field" on:click={()=>{
176
+ if (!hasFocus) {
177
+ showPopup = true;
178
+ setTimeout(() => {
179
+ editor?.focus();
180
+ }, 10);
181
+ } else {
182
+ showPopup = true;
183
+ }
184
+ }}>
185
+ <input bind:this={editor} style="position: absolute; left: 0; top: 0; width: 1px; height: 1px; opacity: 0; pointer-events: none"
186
+ tabindex="-1" aria-label="Focus management input"
187
+ on:keydown={handleKeyDown} on:focus={handleFocus} on:blur={handleBlur}/>
118
188
  <div style="width: 100%; overflow: hidden; display: flex; flex-direction: row; gap: 6px">
119
189
  {#if selectedList.length > 0 }
120
190
  {#each selectedList as code}
@@ -131,9 +201,14 @@
131
201
  <div class="options-popover" style={mh} slot="popup-panel">
132
202
  {#each options as op}
133
203
  {#if hideOptions.indexOf(op[keyField]) < 0}
134
- <CheckBox style="width: 100%" label={op[textField]} onChange={handleChange(op[keyField])}
135
- value={selectedList.indexOf(op[keyField])>-1}
136
- disabled={disableOptions.indexOf(op[keyField])>-1}/>
204
+ <div class="option-item" class:disabled={disableOptions.indexOf(op[keyField])>-1}
205
+ on:click={handleOptionClick(op[keyField])}>
206
+ <input type="checkbox"
207
+ checked={selectedList.indexOf(op[keyField])>-1}
208
+ disabled={disableOptions.indexOf(op[keyField])>-1}
209
+ on:change={handleCheckboxChange(op[keyField])}/>
210
+ <span>{op[textField]}</span>
211
+ </div>
137
212
  {/if}
138
213
  {/each}
139
214
  </div>
@@ -30,7 +30,9 @@ declare const OptionsMultiSelect: $$__sveltets_2_IsomorphicComponent<{
30
30
  disableOptions?: Array<string>;
31
31
  hideOptions?: Array<string>;
32
32
  displayMode?: DisplayMode;
33
- onChange?: OnChangeHandler<Array<string>>;
33
+ onchange?: OnChangeHandler<Array<string>>;
34
+ onfocus?: (() => void) | null;
35
+ onblur?: (() => void) | null;
34
36
  tagColor?: string;
35
37
  tagVariant?: "borderless" | "border" | "round";
36
38
  setFocus?: () => void;