@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,525 @@
1
+ # 单位数字编辑器组件
2
+
3
+ 一个专为数字输入设计的组件,具备单位转换功能。允许用户输入带有不同单位(如 kg、lb、cm、inch)的数字,并自动在不同单位间进行转换。包含下拉单位选择器和基于比率的自动转换功能。
4
+
5
+ ## 安装
6
+
7
+ ```bash
8
+ npm install @ticatec/uniface-element
9
+ ```
10
+
11
+ ## 导入
12
+
13
+ ```typescript
14
+ import UnitNumberEditor, { type UnitOption } from "@ticatec/uniface-element/UnitNumberEditor";
15
+ ```
16
+
17
+ ## 基本用法
18
+
19
+ ```svelte
20
+ <script>
21
+ import UnitNumberEditor, { type UnitOption } from "@ticatec/uniface-element/UnitNumberEditor";
22
+
23
+ let weightValue = 75; // 标准单位值(kg)
24
+ let weightUnit = 'kg';
25
+
26
+ const weightUnits: UnitOption[] = [
27
+ { code: 'kg', text: '千克', ratio: 1, precision: 2 },
28
+ { code: 'lb', text: '磅', ratio: 2.205, precision: 2 },
29
+ { code: 'g', text: '克', ratio: 1000, precision: 0 }
30
+ ];
31
+ </script>
32
+
33
+ <UnitNumberEditor
34
+ bind:value={weightValue}
35
+ bind:unitCode={weightUnit}
36
+ units={weightUnits}
37
+ placeholder="输入重量"
38
+ />
39
+ ```
40
+
41
+ ## 属性
42
+
43
+ | 属性 | 类型 | 默认值 | 描述 |
44
+ |------|------|---------|-------------|
45
+ | `value` | `number \| null` | 必需 | 标准单位的数字值 |
46
+ | `unitCode` | `string` | 必需 | 当前选中的单位代码 |
47
+ | `units` | `Array<UnitOption>` | 必需 | 可用的单位选项 |
48
+ | `disabled` | `boolean` | `false` | 输入框是否禁用 |
49
+ | `readonly` | `boolean` | `false` | 输入框是否只读 |
50
+ | `variant` | `"" \| "plain" \| "outlined" \| "filled"` | `""` | 输入框的视觉变体 |
51
+ | `compact` | `boolean` | `false` | 是否使用紧凑间距 |
52
+ | `style` | `string` | `""` | 附加 CSS 样式 |
53
+ | `prefix` | `string` | `""` | 显示的文本前缀 |
54
+ | `allowNegative` | `boolean` | `false` | 是否允许负数 |
55
+ | `displayMode` | `DisplayMode` | `DisplayMode.Edit` | 显示模式(编辑/查看) |
56
+ | `removable` | `boolean` | `true` | 是否显示清除按钮 |
57
+ | `menu$height` | `number` | `0` | 单位下拉菜单的固定高度 |
58
+ | `placeholder` | `string` | `""` | 占位符文本 |
59
+ | `class` | `string` | `""` | CSS 类名 |
60
+ | `onchange` | `OnChangeHandler<number \| null>` | `null` | 值更改时的回调函数 |
61
+ | `onfocus` | `((event: FocusEvent) => void) \| null` | `null` | 输入框获得焦点时的回调函数 |
62
+ | `onblur` | `((event: FocusEvent) => void) \| null` | `null` | 输入框失去焦点时的回调函数 |
63
+
64
+ ## UnitOption 接口
65
+
66
+ ```typescript
67
+ interface UnitOption {
68
+ code: string; // 单位标识符(例如 'kg', 'lb')
69
+ text?: string; // 显示名称(例如 '千克')
70
+ ratio?: number; // 转换为标准单位的比率
71
+ precision?: number; // 该单位的小数位数
72
+ max?: number; // 该单位的最大值
73
+ min?: number; // 该单位的最小值
74
+ }
75
+ ```
76
+
77
+ ## 方法
78
+
79
+ | 方法 | 描述 |
80
+ |--------|-------------|
81
+ | `setFocus()` | 以编程方式使输入框获得焦点 |
82
+
83
+ ## 示例
84
+
85
+ ### 重量转换器
86
+
87
+ ```svelte
88
+ <script>
89
+ import UnitNumberEditor, { type UnitOption } from "@ticatec/uniface-element/UnitNumberEditor";
90
+
91
+ let weight = 70; // 70 千克作为标准单位
92
+ let weightUnit = 'kg';
93
+
94
+ const weightUnits: UnitOption[] = [
95
+ { code: 'kg', text: '千克', ratio: 1, precision: 2 },
96
+ { code: 'lb', text: '磅', ratio: 2.20462, precision: 2 },
97
+ { code: 'g', text: '克', ratio: 1000, precision: 0 },
98
+ { code: 'oz', text: '盎司', ratio: 35.274, precision: 2 }
99
+ ];
100
+
101
+ function handleWeightChange(newWeight) {
102
+ console.log(`重量更改:${newWeight} 千克`);
103
+ }
104
+ </script>
105
+
106
+ <div class="converter">
107
+ <label>重量:</label>
108
+ <UnitNumberEditor
109
+ bind:value={weight}
110
+ bind:unitCode={weightUnit}
111
+ units={weightUnits}
112
+ onchange={handleWeightChange}
113
+ placeholder="输入重量"
114
+ />
115
+ </div>
116
+
117
+ <style>
118
+ .converter {
119
+ margin-bottom: 16px;
120
+ }
121
+
122
+ label {
123
+ display: block;
124
+ margin-bottom: 4px;
125
+ font-weight: 500;
126
+ }
127
+ </style>
128
+ ```
129
+
130
+ ### 距离测量
131
+
132
+ ```svelte
133
+ <script>
134
+ import UnitNumberEditor, { type UnitOption } from "@ticatec/uniface-element/UnitNumberEditor";
135
+
136
+ let distance = null;
137
+ let distanceUnit = 'm';
138
+
139
+ const distanceUnits: UnitOption[] = [
140
+ { code: 'm', text: '米', ratio: 1, precision: 2 },
141
+ { code: 'ft', text: '英尺', ratio: 3.28084, precision: 2 },
142
+ { code: 'in', text: '英寸', ratio: 39.3701, precision: 1 },
143
+ { code: 'cm', text: '厘米', ratio: 100, precision: 0 },
144
+ { code: 'mm', text: '毫米', ratio: 1000, precision: 0 }
145
+ ];
146
+ </script>
147
+
148
+ <div class="form-group">
149
+ <label>距离:</label>
150
+ <UnitNumberEditor
151
+ bind:value={distance}
152
+ bind:unitCode={distanceUnit}
153
+ units={distanceUnits}
154
+ placeholder="输入距离"
155
+ />
156
+ </div>
157
+
158
+ {#if distance !== null}
159
+ <div class="conversion-display">
160
+ <h4>转换结果:</h4>
161
+ {#each distanceUnits as unit}
162
+ <p>
163
+ {unit.text}: {distance !== null ? (distance * (unit.ratio ?? 1)).toFixed(unit.precision ?? 2) : 'N/A'} {unit.code}
164
+ </p>
165
+ {/each}
166
+ </div>
167
+ {/if}
168
+
169
+ <style>
170
+ .form-group {
171
+ margin-bottom: 16px;
172
+ }
173
+
174
+ .conversion-display {
175
+ background: #f8f9fa;
176
+ padding: 16px;
177
+ border-radius: 4px;
178
+ margin-top: 16px;
179
+ }
180
+
181
+ .conversion-display h4 {
182
+ margin: 0 0 8px 0;
183
+ }
184
+
185
+ .conversion-display p {
186
+ margin: 4px 0;
187
+ font-family: monospace;
188
+ }
189
+ </style>
190
+ ```
191
+
192
+ ### 温度转换器
193
+
194
+ ```svelte
195
+ <script>
196
+ import UnitNumberEditor, { type UnitOption } from "@ticatec/uniface-element/UnitNumberEditor";
197
+
198
+ let temperature = 20; // 20°C 作为标准单位
199
+ let tempUnit = 'C';
200
+
201
+ // 注意:温度转换需要特殊处理,因为存在偏移量
202
+ // 此示例展示了简化的方法 - 实际实现需要自定义转换逻辑
203
+ const temperatureUnits: UnitOption[] = [
204
+ { code: 'C', text: '°摄氏度', ratio: 1, precision: 1 },
205
+ { code: 'F', text: '°华氏度', ratio: 1.8, precision: 1 }, // 简化 - 未考虑 +32 偏移
206
+ { code: 'K', text: '开氏度', ratio: 1, precision: 2 } // 简化 - 未考虑 +273.15 偏移
207
+ ];
208
+
209
+ // 为了精确的温度转换,你需要实现自定义转换逻辑
210
+ function convertTemperature(value, fromUnit, toUnit) {
211
+ if (value === null) return null;
212
+
213
+ // 先转换为摄氏度
214
+ let celsius = value;
215
+ if (fromUnit === 'F') celsius = (value - 32) / 1.8;
216
+ if (fromUnit === 'K') celsius = value - 273.15;
217
+
218
+ // 从摄氏度转换为目标单位
219
+ if (toUnit === 'F') return celsius * 1.8 + 32;
220
+ if (toUnit === 'K') return celsius + 273.15;
221
+ return celsius;
222
+ }
223
+ </script>
224
+
225
+ <div class="form-group">
226
+ <label>温度:</label>
227
+ <UnitNumberEditor
228
+ bind:value={temperature}
229
+ bind:unitCode={tempUnit}
230
+ units={temperatureUnits}
231
+ placeholder="输入温度"
232
+ allowNegative={true}
233
+ />
234
+ </div>
235
+ ```
236
+
237
+ ### 带事件处理程序
238
+
239
+ ```svelte
240
+ <script>
241
+ import UnitNumberEditor, { type UnitOption } from "@ticatec/uniface-element/UnitNumberEditor";
242
+
243
+ let value = null;
244
+ let unit = 'kg';
245
+ let isFocused = false;
246
+
247
+ const units: UnitOption[] = [
248
+ { code: 'kg', text: '千克', ratio: 1, precision: 2 },
249
+ { code: 'lb', text: '磅', ratio: 2.20462, precision: 2 }
250
+ ];
251
+
252
+ function handleChange(newValue) {
253
+ console.log('值更改:', newValue);
254
+ }
255
+
256
+ function handleFocus(event) {
257
+ console.log('输入框已聚焦');
258
+ isFocused = true;
259
+ }
260
+
261
+ function handleBlur(event) {
262
+ console.log('输入框已失焦');
263
+ isFocused = false;
264
+ }
265
+ </script>
266
+
267
+ <UnitNumberEditor
268
+ bind:value
269
+ bind:unitCode={unit}
270
+ {units}
271
+ onchange={handleChange}
272
+ onfocus={handleFocus}
273
+ onblur={handleBlur}
274
+ placeholder="输入值"
275
+ />
276
+
277
+ {#if isFocused}
278
+ <p>输入框当前已聚焦</p>
279
+ {/if}
280
+ ```
281
+
282
+ ### 不同变体
283
+
284
+ ```svelte
285
+ <script>
286
+ import UnitNumberEditor, { type UnitOption } from "@ticatec/uniface-element/UnitNumberEditor";
287
+
288
+ let value1 = 100, value2 = 200, value3 = 300;
289
+ let unit1 = 'm', unit2 = 'm', unit3 = 'm';
290
+
291
+ const units: UnitOption[] = [
292
+ { code: 'm', text: '米', ratio: 1, precision: 2 },
293
+ { code: 'ft', text: '英尺', ratio: 3.28084, precision: 2 }
294
+ ];
295
+ </script>
296
+
297
+ <!-- 默认变体 -->
298
+ <UnitNumberEditor bind:value={value1} bind:unitCode={unit1} {units} placeholder="默认" />
299
+
300
+ <!-- 轮廓变体 -->
301
+ <UnitNumberEditor bind:value={value2} bind:unitCode={unit2} {units} variant="outlined" placeholder="轮廓" />
302
+
303
+ <!-- 填充变体 -->
304
+ <UnitNumberEditor bind:value={value3} bind:unitCode={unit3} {units} variant="filled" placeholder="填充" />
305
+ ```
306
+
307
+ ### 带最小/最大约束
308
+
309
+ ```svelte
310
+ <script>
311
+ import UnitNumberEditor, { type UnitOption } from "@ticatec/uniface-element/UnitNumberEditor";
312
+
313
+ let age = null;
314
+ let ageUnit = 'years';
315
+
316
+ const ageUnits: UnitOption[] = [
317
+ { code: 'years', text: '年', ratio: 1, precision: 0, min: 0, max: 150 },
318
+ { code: 'months', text: '月', ratio: 12, precision: 0, min: 0, max: 1800 },
319
+ { code: 'days', text: '天', ratio: 365.25, precision: 0, min: 0, max: 54750 }
320
+ ];
321
+ </script>
322
+
323
+ <div class="form-group">
324
+ <label>年龄:</label>
325
+ <UnitNumberEditor
326
+ bind:value={age}
327
+ bind:unitCode={ageUnit}
328
+ units={ageUnits}
329
+ placeholder="输入年龄"
330
+ />
331
+ </div>
332
+
333
+ {#if age !== null}
334
+ <p>年龄:{age} {ageUnits.find(u => u.code === ageUnit)?.text?.toLowerCase()}</p>
335
+ {/if}
336
+ ```
337
+
338
+ ### 食谱原料转换器
339
+
340
+ ```svelte
341
+ <script>
342
+ import UnitNumberEditor, { type UnitOption } from "@ticatec/uniface-element/UnitNumberEditor";
343
+ import Button from "@ticatec/uniface-element/Button";
344
+
345
+ let ingredients = [
346
+ { name: '面粉', amount: 500, unit: 'g' },
347
+ { name: '糖', amount: 200, unit: 'g' },
348
+ { name: '牛奶', amount: 250, unit: 'ml' }
349
+ ];
350
+
351
+ const weightUnits: UnitOption[] = [
352
+ { code: 'g', text: '克', ratio: 1, precision: 0 },
353
+ { code: 'kg', text: '千克', ratio: 0.001, precision: 3 },
354
+ { code: 'oz', text: '盎司', ratio: 0.035274, precision: 2 },
355
+ { code: 'lb', text: '磅', ratio: 0.00220462, precision: 4 }
356
+ ];
357
+
358
+ const volumeUnits: UnitOption[] = [
359
+ { code: 'ml', text: '毫升', ratio: 1, precision: 0 },
360
+ { code: 'l', text: '升', ratio: 0.001, precision: 3 },
361
+ { code: 'cup', text: '杯', ratio: 0.00422675, precision: 3 },
362
+ { code: 'tbsp', text: '汤匙', ratio: 0.067628, precision: 2 }
363
+ ];
364
+
365
+ function getUnitsForIngredient(name) {
366
+ return name.toLowerCase().includes('milk') ? volumeUnits : weightUnits;
367
+ }
368
+
369
+ function addIngredient() {
370
+ ingredients = [...ingredients, { name: '', amount: null, unit: 'g' }];
371
+ }
372
+
373
+ function removeIngredient(index) {
374
+ ingredients = ingredients.filter((_, i) => i !== index);
375
+ }
376
+ </script>
377
+
378
+ <div class="recipe-converter">
379
+ <h3>食谱原料转换器</h3>
380
+
381
+ {#each ingredients as ingredient, index}
382
+ <div class="ingredient-row">
383
+ <input
384
+ bind:value={ingredient.name}
385
+ placeholder="原料名称"
386
+ class="ingredient-name"
387
+ />
388
+ <UnitNumberEditor
389
+ bind:value={ingredient.amount}
390
+ bind:unitCode={ingredient.unit}
391
+ units={getUnitsForIngredient(ingredient.name)}
392
+ placeholder="数量"
393
+ compact
394
+ />
395
+ <Button
396
+ label="×"
397
+ onClick={() => removeIngredient(index)}
398
+ style="width: 32px; height: 32px;"
399
+ />
400
+ </div>
401
+ {/each}
402
+
403
+ <Button label="添加原料" onClick={addIngredient} />
404
+ </div>
405
+
406
+ <style>
407
+ .recipe-converter {
408
+ max-width: 600px;
409
+ padding: 20px;
410
+ border: 1px solid #ddd;
411
+ border-radius: 8px;
412
+ }
413
+
414
+ .ingredient-row {
415
+ display: grid;
416
+ grid-template-columns: 1fr 1fr auto;
417
+ gap: 12px;
418
+ align-items: center;
419
+ margin-bottom: 12px;
420
+ }
421
+
422
+ .ingredient-name {
423
+ padding: 8px 12px;
424
+ border: 1px solid #ccc;
425
+ border-radius: 4px;
426
+ font-size: 14px;
427
+ }
428
+
429
+ h3 {
430
+ margin-bottom: 20px;
431
+ }
432
+ </style>
433
+ ```
434
+
435
+ ### 编程式焦点
436
+
437
+ ```svelte
438
+ <script>
439
+ import UnitNumberEditor, { type UnitOption } from "@ticatec/uniface-element/UnitNumberEditor";
440
+ import Button from "@ticatec/uniface-element/Button";
441
+
442
+ let unitEditor;
443
+ let value = null;
444
+ let unit = 'kg';
445
+
446
+ const units: UnitOption[] = [
447
+ { code: 'kg', text: '千克', ratio: 1, precision: 2 },
448
+ { code: 'lb', text: '磅', ratio: 2.20462, precision: 2 }
449
+ ];
450
+
451
+ function focusInput() {
452
+ unitEditor.setFocus();
453
+ }
454
+ </script>
455
+
456
+ <Button label="聚焦输入框" onClick={focusInput} />
457
+
458
+ <UnitNumberEditor
459
+ bind:this={unitEditor}
460
+ bind:value
461
+ bind:unitCode={unit}
462
+ {units}
463
+ placeholder="点击按钮聚焦我"
464
+ />
465
+ ```
466
+
467
+ ## 功能
468
+
469
+ - **自动转换**:基于比率在不同单位间无缝转换
470
+ - **灵活精度**:每个单位可设置自己的小数位数
471
+ - **验证**:每个单位的最小/最大约束及自动验证
472
+ - **清除功能**:内置清除按钮以重置值
473
+ - **焦点管理**:为可访问性提供适当的焦点处理
474
+ - **自定义样式**:支持多种视觉变体和自定义 CSS
475
+ - **单位下拉菜单**:交互式下拉菜单用于单位选择
476
+ - **负数支持**:可选支持负数值
477
+ - **前缀支持**:添加文本前缀以增强上下文
478
+
479
+ ## 单位转换逻辑
480
+
481
+ 组件使用基于比率的转换系统:
482
+
483
+ 1. **标准单位**:一个单位应具有 `ratio: 1` 作为基本单位
484
+ 2. **转换**:其他单位通过其比率乘以标准单位进行转换
485
+ 3. **显示**:值会自动转换为所选单位的显示值
486
+ 4. **存储**:`value` 属性始终包含标准单位的值
487
+
488
+ 示例:
489
+ ```typescript
490
+ // 如果 kg 是标准单位(ratio: 1),lb 的 ratio 为 2.20462
491
+ // 用户输入 100 lb → 存储为约 45.36 kg
492
+ // 用户切换到 kg → 显示约 45.36 kg
493
+ ```
494
+
495
+ ## 可访问性
496
+
497
+ - 在输入框和下拉菜单之间提供适当的键盘导航
498
+ - 语义化 HTML 结构,兼容屏幕阅读器
499
+ - 交互元素的焦点指示器
500
+ - 下拉菜单的 ARIA 属性
501
+ - 交互状态的清晰视觉反馈
502
+
503
+ ## 最佳实践
504
+
505
+ 1. **标准单位**:始终定义一个 `ratio: 1` 的基本单位
506
+ 2. **精度**:为每个单位设置适当的精度(整数或小数)
507
+ 3. **约束**:使用最小/最大值防止无效输入
508
+ 4. **单位名称**:在 `text` 属性中提供清晰、描述性的单位名称
509
+ 5. **转换逻辑**:对于复杂转换(如温度),实现自定义逻辑
510
+ 6. **验证**:如有需要,在更改处理程序中添加额外验证
511
+
512
+ ## 浏览器支持
513
+
514
+ - 支持完整输入事件的现代浏览器
515
+ - 兼容 Svelte 5+
516
+ - 支持键盘导航
517
+ - 触控友好下拉界面
518
+ - 完整的 TypeScript 支持
519
+
520
+ ## 相关组件
521
+
522
+ - `NumberEditor` - 不带单位的数字输入
523
+ - `TextEditor` - 文本输入组件
524
+ - `OptionsSelect` - 下拉选择组件
525
+ - `TimeEditor` - 时间输入组件
@@ -21,11 +21,21 @@
21
21
  export let displayMode: DisplayMode = DisplayMode.Edit;
22
22
  export let removable: boolean = true;
23
23
  export let unitCode: string = null as unknown as string;
24
- export let onChange: OnChangeHandler<number | null> = null as unknown as OnChangeHandler<number | null>;
24
+ export let onchange: OnChangeHandler<number | null> = null as unknown as OnChangeHandler<number | null>;
25
+ export let onfocus: ((event: FocusEvent) => void) | null = null;
26
+ export let onblur: ((event: FocusEvent) => void) | null = null;
25
27
  export let units: Array<UnitOption>;
26
28
  export let menu$height: number = 0;
27
29
  export let placeholder: string = '';
28
30
 
31
+ export const setFocus = () => {
32
+ setTimeout(() => {
33
+ if (editor && editor.setFocus) {
34
+ editor.setFocus();
35
+ }
36
+ }, 50);
37
+ }
38
+
29
39
 
30
40
  let className: string = '';
31
41
  let editor: any;
@@ -33,6 +43,9 @@
33
43
  let standardUnit: UnitOption | undefined;
34
44
 
35
45
  let unitValue: number | null = value;
46
+ let oldValue = value;
47
+ let isUserChange: boolean = false;
48
+ let hasFocus: boolean = false;
36
49
 
37
50
  let showPopup: boolean = false;
38
51
  let divPanel: any;
@@ -45,6 +58,7 @@
45
58
  }
46
59
 
47
60
  const handleValueChange = (uv: number | null) => {
61
+ isUserChange = true;
48
62
  if (uv == null) {
49
63
  value = null;
50
64
  } else if (standardUnit) {
@@ -52,21 +66,47 @@
52
66
  } else {
53
67
  value = uv;
54
68
  }
55
- onChange?.(value)
56
69
  }
57
70
 
58
71
  const handleUnitClick = (item: any) => (e: MouseEvent) => {
72
+ isUserChange = true;
59
73
  unit = item;
60
74
  unitCode = unit.code;
61
- if (standardUnit == null) {
62
- onChange?.(value);
63
- }
64
75
  showPopup = false;
65
76
  calculateUnitValue();
77
+ setTimeout(() => {
78
+ if (editor && editor.setFocus) {
79
+ editor.setFocus();
80
+ }
81
+ }, 50);
66
82
  e.stopPropagation();
67
83
  e.preventDefault();
68
84
  }
69
85
 
86
+ // 处理focus/blur事件
87
+ const handleFocus = (event: FocusEvent) => {
88
+ if (!hasFocus) {
89
+ hasFocus = true;
90
+ onfocus?.(event);
91
+ }
92
+ }
93
+
94
+ const handleBlur = (event: FocusEvent) => {
95
+ if (hasFocus && !showPopup) {
96
+ hasFocus = false;
97
+ onblur?.(event);
98
+ }
99
+ }
100
+
101
+ const handlePopupClose = () => {
102
+ showPopup = false;
103
+ setTimeout(() => {
104
+ if (document.activeElement !== editor?.inputElement) {
105
+ handleBlur();
106
+ }
107
+ }, 50);
108
+ }
109
+
70
110
  const calculateUnitValue = () => {
71
111
  if (standardUnit) {
72
112
  unitValue = value == null ? null : value * (unit.ratio ?? 1)
@@ -77,7 +117,11 @@
77
117
  const clean = () => {
78
118
  value = null;
79
119
  unitValue = null;
80
- editor.setFocus();
120
+ setTimeout(() => {
121
+ if (editor && editor.setFocus) {
122
+ editor.setFocus();
123
+ }
124
+ }, 50);
81
125
  }
82
126
 
83
127
 
@@ -95,6 +139,15 @@
95
139
 
96
140
  $: determineValueChangeFormOutside(unitCode, value);
97
141
 
142
+ // 处理onchange事件
143
+ $: if (oldValue !== value) {
144
+ if (isUserChange) {
145
+ onchange?.(value);
146
+ isUserChange = false;
147
+ }
148
+ oldValue = value;
149
+ }
150
+
98
151
  </script>
99
152
 
100
153
  <div bind:this={divPanel}>
@@ -110,7 +163,8 @@
110
163
  </svelte:fragment>
111
164
  <NumberInput bind:this={editor} style="flex: 1 1 auto" {disabled} {placeholder} bind:value={unitValue} precision={unit?.precision}
112
165
  {allowNegative}
113
- max={unit?.max} min={unit?.min} {readonly} onChange={handleValueChange}/>
166
+ max={unit?.max} min={unit?.min} {readonly} onchange={handleValueChange}
167
+ onfocus={handleFocus} onblur={handleBlur}/>
114
168
  <svelte:fragment slot="trailing-icon">
115
169
  {#if !disabled && !readonly}
116
170
  <i style="font-size: 22px" class="option-dropdown icon_google_arrow_drop_down" aria-hidden="true" on:click={openPopup}></i>
@@ -119,7 +173,7 @@
119
173
  </CommonEditor>
120
174
 
121
175
  {#if showPopup}
122
- <Popover target={divPanel} autoFit={false} align="right" onClose={()=>{showPopup=false}}>
176
+ <Popover target={divPanel} autoFit={false} align="right" onClose={handlePopupClose}>
123
177
  <div class="options-popover" style="min-width: 90px; box-sizing: border-box; text-align: center; {mh}" >
124
178
  {#each units as op}
125
179
  <div class="option-item" style="padding-left: 12px; padding-right: 12px" on:click={handleUnitClick(op)} aria-hidden="true">
@@ -27,16 +27,20 @@ declare const UnitNumberEditor: $$__sveltets_2_IsomorphicComponent<{
27
27
  displayMode?: DisplayMode;
28
28
  removable?: boolean;
29
29
  unitCode?: string;
30
- onChange?: OnChangeHandler<number | null>;
30
+ onchange?: OnChangeHandler<number | null>;
31
+ onfocus?: ((event: FocusEvent) => void) | null;
32
+ onblur?: ((event: FocusEvent) => void) | null;
31
33
  units: Array<UnitOption>;
32
34
  menu$height?: number;
33
35
  placeholder?: string;
36
+ setFocus?: () => void;
34
37
  }, {
35
38
  [evt: string]: CustomEvent<any>;
36
39
  }, {
37
40
  'leading-icon': {};
38
41
  }, {
39
42
  class: string;
43
+ setFocus: () => void;
40
44
  }, string>;
41
45
  type UnitNumberEditor = InstanceType<typeof UnitNumberEditor>;
42
46
  export default UnitNumberEditor;