@ticatec/uniface-element 5.0.0 → 5.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (282) hide show
  1. package/README.md +29 -54
  2. package/README_CN.md +37 -57
  3. package/dist/app_layout/ClassicLayout.svelte +8 -40
  4. package/dist/app_layout/ColumnsLayout.svelte +3 -3
  5. package/dist/app_layout/HeaderLayout.svelte +4 -20
  6. package/dist/app_layout/RowsLayout.svelte +3 -3
  7. package/dist/app_layout/SidebarLayout.svelte +4 -20
  8. package/dist/base-calendar/CalendarBase.svelte +29 -6
  9. package/dist/base-calendar/CalendarBase.svelte.d.ts +4 -0
  10. package/dist/base-calendar/MonthPickupPanel.svelte +19 -6
  11. package/dist/base-calendar/MonthPickupPanel.svelte.d.ts +1 -1
  12. package/dist/base-calendar/MonthlyCalendar.svelte +18 -2
  13. package/dist/base-calendar/YearPickupPanel.svelte +24 -6
  14. package/dist/base-calendar/YearPickupPanel.svelte.d.ts +1 -1
  15. package/dist/composite/pagination_panel/PaginationPanel.svelte +2 -0
  16. package/dist/composite/transfer/Transfer.svelte +12 -6
  17. package/dist/data_display/accordion/Accordion.svelte +1 -4
  18. package/dist/data_display/card/CommonCardActionBar.svelte +31 -37
  19. package/dist/data_display/concise_data_table/Column.d.ts +6 -0
  20. package/dist/data_display/concise_data_table/ConciseListTable.svelte +4 -3
  21. package/dist/data_display/concise_data_table/DataCell.svelte +3 -3
  22. package/dist/data_display/list_box/ListBox.svelte +96 -23
  23. package/dist/data_display/list_box/ListBox.svelte.d.ts +4 -0
  24. package/dist/data_display/tree_view/TreeNodeView.svelte +25 -4
  25. package/dist/data_table/DataTable.svelte +5 -5
  26. package/dist/data_table/DataTable.svelte.d.ts +36 -16
  27. package/dist/data_table/lib/ActionsColumn.d.ts +2 -2
  28. package/dist/data_table/lib/DataColumn.d.ts +7 -1
  29. package/dist/data_table/lib/RowAction.d.ts +5 -5
  30. package/dist/data_table/lib/clickOutSide.js +6 -5
  31. package/dist/data_table/parts/FixedHeaderPanel.svelte +18 -3
  32. package/dist/data_table/parts/FixedRow.svelte +10 -1
  33. package/dist/data_table/parts/HrefCell.svelte +1 -1
  34. package/dist/data_table/parts/TableHeaderPanel.svelte +18 -2
  35. package/dist/data_table/parts/TextCell.svelte +2 -2
  36. package/dist/form/attachment_files/AttachmentEditor.svelte +20 -15
  37. package/dist/form/attachment_files/FileRender.svelte +1 -1
  38. package/dist/form/attachment_files/FileUploadBar.svelte +5 -2
  39. package/dist/form/attachment_files/FileUploadPanel.svelte +11 -73
  40. package/dist/form/cascade_options_select/CascadeOptionSelect.svelte +10 -2
  41. package/dist/form/cascade_options_select/OptionsBox.svelte +48 -2
  42. package/dist/form/checkbox/CheckBox.svelte +2 -7
  43. package/dist/form/color_picker/ColorPicker.svelte +150 -35
  44. package/dist/form/color_picker/ColorPicker.svelte.d.ts +7 -25
  45. package/dist/form/color_picker/ColorPickerPanel.svelte +23 -20
  46. package/dist/form/color_picker/ColorPickerPanel.svelte.d.ts +5 -18
  47. package/dist/form/color_picker/README.md +14 -7
  48. package/dist/form/color_picker/README_CN.md +14 -7
  49. package/dist/form/common_editor/CommonEditor.svelte +18 -4
  50. package/dist/form/common_editor/CommonPicker.svelte +14 -10
  51. package/dist/form/common_editor/CommonPopupEditor.svelte +8 -1
  52. package/dist/form/common_range_editor/CommonRangeEditor.svelte +11 -2
  53. package/dist/form/date_picker/DatePicker.svelte +1 -1
  54. package/dist/form/date_picker/DateTimePicker.svelte +1 -1
  55. package/dist/form/date_picker/ScrollBar.svelte +20 -12
  56. package/dist/form/date_picker/ScrollBar.svelte.d.ts +4 -18
  57. package/dist/form/date_range/DateRangeEditor.svelte +11 -3
  58. package/dist/form/group_box/group-check-box/GroupCheckBox.svelte +6 -1
  59. package/dist/form/group_box/group-radio-box/GroupRadioBox.svelte +6 -1
  60. package/dist/form/image-files/ImageFilesField.svelte +51 -7
  61. package/dist/form/image-files/ImagePreview.svelte +23 -9
  62. package/dist/form/image-files/ImageRender.svelte +69 -11
  63. package/dist/form/input_options_select/InputOptionsSelect.svelte +10 -2
  64. package/dist/form/lookup_editor/LookupEditor.svelte +9 -2
  65. package/dist/form/memo_editor/MemoEditor.svelte +2 -2
  66. package/dist/form/number_editor/NumberEditor.svelte +2 -2
  67. package/dist/form/options_multi_select/OptionsMultiSelect.svelte +20 -12
  68. package/dist/form/options_select/OptionsSelect.svelte +15 -6
  69. package/dist/form/options_select/OptionsSelect.svelte.d.ts +39 -17
  70. package/dist/form/prompts_text_editor/PromptsTextEditor.svelte +10 -2
  71. package/dist/form/radio_button/RadioButton.svelte +2 -8
  72. package/dist/form/switch/Switch.svelte +4 -4
  73. package/dist/form/text_editor/TextEditor.svelte +2 -2
  74. package/dist/form/unit-number-editor/UnitNumberEditor.svelte +10 -2
  75. package/dist/general/button/Button.svelte +5 -0
  76. package/dist/general/button/Button.svelte.d.ts +1 -0
  77. package/dist/general/button/IconButton.svelte +3 -1
  78. package/dist/general/button/IconButton.svelte.d.ts +1 -0
  79. package/dist/general/button/TextButton.svelte +3 -0
  80. package/dist/general/button/TextButton.svelte.d.ts +1 -0
  81. package/dist/general/icon/Icon.svelte +18 -1
  82. package/dist/general/split/Split.svelte +24 -4
  83. package/dist/general/tag/Tag.svelte +9 -1
  84. package/dist/navigation/nav_menu/MenuItem.d.ts +4 -2
  85. package/dist/navigation/nav_menu/NavigatorMenu.svelte +157 -34
  86. package/dist/navigation/nav_menu/NavigatorMenu.svelte.d.ts +2 -3
  87. package/dist/navigation/nav_menu/NavigatorMenuItem.svelte +25 -13
  88. package/dist/navigation/nav_menu/NavigatorMenuItem.svelte.d.ts +3 -3
  89. package/dist/navigation/navigator/Navigator.svelte +17 -17
  90. package/dist/navigation/page-stack/PageStack.svelte +160 -0
  91. package/dist/navigation/page-stack/PageStack.svelte.d.ts +24 -0
  92. package/dist/navigation/page-stack/PageStackContext.d.ts +25 -0
  93. package/dist/navigation/page-stack/PageStackContext.js +12 -0
  94. package/dist/navigation/page-stack/PageStackFrame.svelte +24 -0
  95. package/dist/navigation/page-stack/PageStackFrame.svelte.d.ts +11 -0
  96. package/dist/navigation/page-stack/README.md +154 -0
  97. package/dist/navigation/page-stack/README_CN.md +154 -0
  98. package/dist/navigation/page-stack/index.d.ts +6 -0
  99. package/dist/navigation/page-stack/index.js +4 -0
  100. package/dist/navigation/pagination/Pagination.svelte +114 -14
  101. package/dist/navigation/progress_step_bar/ProgressStepBlock.svelte +1 -5
  102. package/dist/navigation/tabs/Tabs.svelte +42 -10
  103. package/dist/navigation/tabs/Tabs.svelte.d.ts +2 -2
  104. package/dist/overlay/common/FloatingPanel.svelte +3 -17
  105. package/dist/overlay/common/FullscreenOverlay.svelte +27 -3
  106. package/dist/overlay/common/FullscreenOverlay.svelte.d.ts +6 -27
  107. package/dist/overlay/common/ModalPopover.svelte +18 -2
  108. package/dist/overlay/common/Popover.svelte +61 -57
  109. package/dist/overlay/common/Popover.svelte.d.ts +1 -5
  110. package/dist/overlay/common/escapeClose.d.ts +28 -0
  111. package/dist/overlay/common/escapeClose.js +47 -0
  112. package/dist/overlay/common/focusTrap.d.ts +8 -0
  113. package/dist/overlay/common/focusTrap.js +83 -0
  114. package/dist/overlay/common/mountSingleton.d.ts +27 -0
  115. package/dist/overlay/common/mountSingleton.js +45 -0
  116. package/dist/overlay/common/outsideClick.d.ts +28 -0
  117. package/dist/overlay/common/outsideClick.js +20 -0
  118. package/dist/overlay/common/scrollLock.d.ts +8 -0
  119. package/dist/overlay/common/scrollLock.js +49 -0
  120. package/dist/overlay/common/uniface-utils.js +0 -1
  121. package/dist/overlay/context-menu/ContextMenu.svelte.js +2 -15
  122. package/dist/overlay/context-menu/ContextMenuPanel.svelte +27 -3
  123. package/dist/overlay/dialog/Dialog.svelte +48 -32
  124. package/dist/overlay/dialog/DialogManager.svelte.js +7 -9
  125. package/dist/overlay/dialog/DialogWrapper.svelte +5 -2
  126. package/dist/overlay/drawer/Drawer.svelte +30 -3
  127. package/dist/overlay/indicator/Indicator.svelte.js +2 -15
  128. package/dist/overlay/indicator/IndicatorPanel.svelte +4 -1
  129. package/dist/overlay/message-box/MessageBox.svelte.js +2 -15
  130. package/dist/overlay/message-box/MessageBoxPanel.svelte +19 -3
  131. package/dist/overlay/toast/Toast.svelte.js +2 -15
  132. package/dist/overlay/toast/ToastPanel.svelte +1 -1
  133. package/dist/overlay/tooltip/Tooltip.svelte +1 -1
  134. package/dist/ticatec-uniface-web.css +128 -13
  135. package/dist/types.d.ts +4 -3
  136. package/dist/utils/prefixFilter.d.ts +3 -3
  137. package/dist/utils/prefixFilter.js +3 -5
  138. package/docs/components/README.md +133 -0
  139. package/docs/components/accordion/README.md +60 -0
  140. package/docs/components/accordion/example.svelte +7 -0
  141. package/docs/components/action-bar/README.md +114 -0
  142. package/docs/components/action-bar/example.svelte +7 -0
  143. package/docs/components/app-top-bar/README.md +60 -0
  144. package/docs/components/app-top-bar/example.svelte +9 -0
  145. package/docs/components/attachment-files/README.md +332 -0
  146. package/docs/components/attachment-files/example.svelte +16 -0
  147. package/docs/components/box/README.md +63 -0
  148. package/docs/components/box/example.svelte +10 -0
  149. package/docs/components/breadcrumbs/README.md +86 -0
  150. package/docs/components/breadcrumbs/example.svelte +11 -0
  151. package/docs/components/button/README.md +103 -0
  152. package/docs/components/button/example.svelte +10 -0
  153. package/docs/components/card/README.md +113 -0
  154. package/docs/components/card/example.svelte +10 -0
  155. package/docs/components/cascade-options-select/README.md +162 -0
  156. package/docs/components/cascade-options-select/example.svelte +18 -0
  157. package/docs/components/checkbox/README.md +103 -0
  158. package/docs/components/checkbox/example.svelte +15 -0
  159. package/docs/components/classic-layout/README.md +76 -0
  160. package/docs/components/classic-layout/example.svelte +16 -0
  161. package/docs/components/color-picker/README.md +132 -0
  162. package/docs/components/color-picker/example.svelte +16 -0
  163. package/docs/components/columns-layout/README.md +70 -0
  164. package/docs/components/columns-layout/example.svelte +12 -0
  165. package/docs/components/common-dialog/README.md +109 -0
  166. package/docs/components/common-dialog/example.svelte +12 -0
  167. package/docs/components/concise-data-table/README.md +307 -0
  168. package/docs/components/concise-data-table/example.svelte +16 -0
  169. package/docs/components/context-menu/README.md +99 -0
  170. package/docs/components/context-menu/example.svelte +10 -0
  171. package/docs/components/criteria-field/README.md +69 -0
  172. package/docs/components/criteria-field/example.svelte +9 -0
  173. package/docs/components/data-table/README.md +365 -0
  174. package/docs/components/data-table/example.svelte +23 -0
  175. package/docs/components/date-picker/README.md +136 -0
  176. package/docs/components/date-picker/example.svelte +16 -0
  177. package/docs/components/date-range/README.md +122 -0
  178. package/docs/components/date-range/example.svelte +15 -0
  179. package/docs/components/date-time-picker/README.md +142 -0
  180. package/docs/components/date-time-picker/example.svelte +16 -0
  181. package/docs/components/dialog/README.md +174 -0
  182. package/docs/components/dialog/example.svelte +13 -0
  183. package/docs/components/dialog-board/README.md +41 -0
  184. package/docs/components/dialog-board/example.svelte +5 -0
  185. package/docs/components/drawer/README.md +70 -0
  186. package/docs/components/drawer/example.svelte +12 -0
  187. package/docs/components/flex-form/README.md +55 -0
  188. package/docs/components/flex-form/example.svelte +7 -0
  189. package/docs/components/flex-row-form/README.md +56 -0
  190. package/docs/components/flex-row-form/example.svelte +7 -0
  191. package/docs/components/form-field/README.md +84 -0
  192. package/docs/components/form-field/example.svelte +10 -0
  193. package/docs/components/full-screen-overlay/README.md +66 -0
  194. package/docs/components/full-screen-overlay/example.svelte +11 -0
  195. package/docs/components/grid-form/README.md +75 -0
  196. package/docs/components/grid-form/example.svelte +9 -0
  197. package/docs/components/group-check-box/README.md +147 -0
  198. package/docs/components/group-check-box/example.svelte +21 -0
  199. package/docs/components/group-radio-box/README.md +144 -0
  200. package/docs/components/group-radio-box/example.svelte +21 -0
  201. package/docs/components/header-layout/README.md +65 -0
  202. package/docs/components/header-layout/example.svelte +12 -0
  203. package/docs/components/icon/README.md +87 -0
  204. package/docs/components/icon/example.svelte +9 -0
  205. package/docs/components/icon-button/README.md +91 -0
  206. package/docs/components/icon-button/example.svelte +10 -0
  207. package/docs/components/image-files/README.md +174 -0
  208. package/docs/components/image-files/example.svelte +19 -0
  209. package/docs/components/indicator/README.md +53 -0
  210. package/docs/components/indicator/example.svelte +14 -0
  211. package/docs/components/input-options-select/README.md +163 -0
  212. package/docs/components/input-options-select/example.svelte +16 -0
  213. package/docs/components/list-box/ListItem.svelte +14 -0
  214. package/docs/components/list-box/README.md +221 -0
  215. package/docs/components/list-box/example.svelte +61 -0
  216. package/docs/components/lookup-editor/README.md +137 -0
  217. package/docs/components/lookup-editor/example.svelte +16 -0
  218. package/docs/components/memo-editor/README.md +143 -0
  219. package/docs/components/memo-editor/example.svelte +16 -0
  220. package/docs/components/message-box/README.md +68 -0
  221. package/docs/components/message-box/example.svelte +10 -0
  222. package/docs/components/navigator/README.md +75 -0
  223. package/docs/components/navigator/example.svelte +13 -0
  224. package/docs/components/navigator-menu/README.md +113 -0
  225. package/docs/components/navigator-menu/example.svelte +10 -0
  226. package/docs/components/number-editor/README.md +144 -0
  227. package/docs/components/number-editor/example.svelte +16 -0
  228. package/docs/components/number-range/README.md +116 -0
  229. package/docs/components/number-range/example.svelte +15 -0
  230. package/docs/components/options-multi-select/README.md +161 -0
  231. package/docs/components/options-multi-select/example.svelte +20 -0
  232. package/docs/components/options-select/README.md +160 -0
  233. package/docs/components/options-select/example.svelte +21 -0
  234. package/docs/components/page/README.md +77 -0
  235. package/docs/components/page/example.svelte +10 -0
  236. package/docs/components/pagination/README.md +88 -0
  237. package/docs/components/pagination/example.svelte +10 -0
  238. package/docs/components/pagination-panel/README.md +120 -0
  239. package/docs/components/pagination-panel/example.svelte +16 -0
  240. package/docs/components/popup-hint/README.md +44 -0
  241. package/docs/components/popup-hint/example.svelte +5 -0
  242. package/docs/components/progress-bar/README.md +64 -0
  243. package/docs/components/progress-bar/example.svelte +8 -0
  244. package/docs/components/progress-step-bar/README.md +93 -0
  245. package/docs/components/progress-step-bar/example.svelte +7 -0
  246. package/docs/components/prompts-text-editor/README.md +146 -0
  247. package/docs/components/prompts-text-editor/example.svelte +17 -0
  248. package/docs/components/radio-button/README.md +121 -0
  249. package/docs/components/radio-button/example.svelte +18 -0
  250. package/docs/components/rows-layout/README.md +70 -0
  251. package/docs/components/rows-layout/example.svelte +12 -0
  252. package/docs/components/search-box/README.md +136 -0
  253. package/docs/components/search-box/example.svelte +16 -0
  254. package/docs/components/separator/README.md +44 -0
  255. package/docs/components/separator/example.svelte +7 -0
  256. package/docs/components/sidebar-layout/README.md +65 -0
  257. package/docs/components/sidebar-layout/example.svelte +12 -0
  258. package/docs/components/split/README.md +48 -0
  259. package/docs/components/split/example.svelte +8 -0
  260. package/docs/components/switch/README.md +77 -0
  261. package/docs/components/switch/example.svelte +11 -0
  262. package/docs/components/tabs/README.md +127 -0
  263. package/docs/components/tabs/example.svelte +22 -0
  264. package/docs/components/tag/README.md +63 -0
  265. package/docs/components/tag/example.svelte +8 -0
  266. package/docs/components/text-button/README.md +90 -0
  267. package/docs/components/text-button/example.svelte +9 -0
  268. package/docs/components/text-editor/README.md +147 -0
  269. package/docs/components/text-editor/example.svelte +16 -0
  270. package/docs/components/time-editor/README.md +130 -0
  271. package/docs/components/time-editor/example.svelte +16 -0
  272. package/docs/components/toast/README.md +52 -0
  273. package/docs/components/toast/example.svelte +9 -0
  274. package/docs/components/tooltip/README.md +44 -0
  275. package/docs/components/tooltip/example.svelte +5 -0
  276. package/docs/components/transfer/README.md +109 -0
  277. package/docs/components/transfer/example.svelte +13 -0
  278. package/docs/components/tree-view/README.md +163 -0
  279. package/docs/components/tree-view/example.svelte +26 -0
  280. package/docs/components/unit-number-editor/README.md +192 -0
  281. package/docs/components/unit-number-editor/example.svelte +18 -0
  282. package/package.json +15 -7
@@ -23,13 +23,59 @@
23
23
  selectedItem = item;
24
24
  onSelect(item);
25
25
  }
26
+
27
+ const handleItemKeyDown = (item: any) => (e: KeyboardEvent) => {
28
+ if (e.key === 'Enter' || e.key === ' ') {
29
+ e.preventDefault();
30
+ handleItemClick(item)(e as unknown as MouseEvent);
31
+ }
32
+ }
26
33
  </script>
27
34
 
28
- <div class="cascade-column" style={box$style}>
35
+ <div class="cascade-column" style={box$style} role="listbox">
29
36
  {#each options as op (op[keyField])}
30
37
  <div class="option-item" class:selected={op[keyField] === (selectedItem ?? {})[keyField]}
31
- aria-hidden="true" onclick={handleItemClick(op)}>
38
+ role="option" tabindex="0" aria-selected={op[keyField] === (selectedItem ?? {})[keyField]}
39
+ onclick={handleItemClick(op)} onkeydown={handleItemKeyDown(op)}>
32
40
  <span>{op[textField]}</span>
33
41
  </div>
34
42
  {/each}
35
43
  </div>
44
+
45
+ <style>
46
+ /* Base sizing so a column looks correct even when the consumer doesn't
47
+ pass box$style — previously there was no default at all, so columns
48
+ had no width/overflow handling and collapsed unpredictably inside the
49
+ popover's flex row. box$style (inline style, applied after these
50
+ rules) can still override any of this per column. */
51
+ .cascade-column {
52
+ flex: 0 0 auto;
53
+ width: 140px;
54
+ height: 100%;
55
+ box-sizing: border-box;
56
+ overflow-y: auto;
57
+ overflow-x: hidden;
58
+ border-right: 1px solid var(--uniface-color-border-secondary);
59
+ }
60
+
61
+ .cascade-column:last-child {
62
+ border-right: none;
63
+ }
64
+
65
+ .option-item {
66
+ padding: 6px 12px;
67
+ cursor: pointer;
68
+ overflow: hidden;
69
+ text-overflow: ellipsis;
70
+ white-space: nowrap;
71
+ }
72
+
73
+ .option-item:hover {
74
+ background-color: var(--uniface-popover-item-color-surface-hover);
75
+ }
76
+
77
+ .option-item.selected {
78
+ background-color: var(--uniface-popover-item-color-surface-active);
79
+ color: var(--uniface-popover-item-color-active);
80
+ }
81
+ </style>
@@ -123,6 +123,7 @@
123
123
  --disabled-inner: var(--uniface-checkbox-checkmark-color-disabled);
124
124
  -webkit-appearance: none;
125
125
  -moz-appearance: none;
126
+ appearance: none;
126
127
  height: var(--uniface-checkbox-size);
127
128
  outline: none;
128
129
  display: inline-block;
@@ -191,12 +192,6 @@
191
192
  --o: 1;
192
193
  --r: var(--uniface-checkbox-check-rotation);
193
194
  }
194
- .uniface-checkbox input[type=checkbox] + label {
195
- display: inline-block;
196
- vertical-align: middle;
197
- cursor: pointer;
198
- margin-left: 4px;
199
- }
200
195
  .uniface-checkbox.compact {
201
196
  padding: 0;
202
197
  color: var(--uniface-checkbox-text-color);
@@ -224,4 +219,4 @@
224
219
  .uniface-checkbox *::before,
225
220
  .uniface-checkbox *::after {
226
221
  box-sizing: inherit;
227
- }</style>
222
+ }</style>
@@ -1,58 +1,173 @@
1
1
  <script lang="ts">
2
2
 
3
-
4
- import type {OnChangeHandler} from "../../types";
5
3
  import CommonPicker from "../common_editor/CommonPicker.svelte";
4
+ import type CommonFieldProps from "../common_editor/CommonFieldProps";
6
5
 
7
- //type OnActionHandler = () => void;
6
+ interface Props extends CommonFieldProps {
7
+ value?: string;
8
+ }
8
9
 
9
- //export let showHex = true;
10
- //export let showRgb = false;
10
+ // The manually-typed value must resolve to exactly #RRGGBB. An 8-digit #RRGGBBAA
11
+ // form was supported briefly, but <input type="color"> has no concept of alpha —
12
+ // the swatch always showed just the RGB portion and nothing in the UI ever
13
+ // rendered the transparency, so the last 2 digits had no visible effect. Dropped
14
+ // rather than half-implemented; revisit with a real alpha-aware swatch (checkered
15
+ // background + rgba preview) if this is needed later.
16
+ const HEX_PATTERN = /^#[0-9a-fA-F]{6}$/;
17
+ const DEFAULT_SWATCH = '#000000';
18
+
19
+ // CommonPicker always renders its own internal <input> in the non-readonly branch,
20
+ // even when a `children` snippet is also supplied — it doesn't apply the same
21
+ // "children replaces the default input" rule that its readonly branch does. Every
22
+ // other consumer works around this by never passing `children` in the first place
23
+ // (DatePicker, OptionsSelect...) except OptionsMultiSelect, which needs custom
24
+ // content (its tag list) the same way ColorPicker needs its swatch+hex layout —
25
+ // it hides CommonPicker's own input via `input$style` (shrink to 1x1, transparent,
26
+ // click-through) so only the custom children are visible. Same fix here: without
27
+ // this, edit mode showed CommonPicker's own empty input fighting for space with our
28
+ // swatch, while readonly mode (which only renders `children`) looked correct.
29
+ const HIDDEN_INTERNAL_INPUT_STYLE = 'position: absolute; left: 0; top: 0; width: 1px; height: 1px; opacity: 0; pointer-events: none';
30
+
31
+ let {
32
+ variant,
33
+ theme,
34
+ disabled = false,
35
+ readonly = false,
36
+ compact = false,
37
+ value = $bindable<string | undefined>("#000000"),
38
+ style,
39
+ placeholder,
40
+ error,
41
+ tooltip,
42
+ onchange,
43
+ onfocus,
44
+ onblur
45
+ }: Props = $props();
46
+
47
+ let editor: HTMLInputElement | undefined = $state();
48
+ let textInput: HTMLInputElement | undefined = $state();
49
+
50
+ // Editing buffer for the manual hex field, kept separate from `value` so the user
51
+ // can type through transient/incomplete states (e.g. "#3b8") without every
52
+ // keystroke being validated against the full #RRGGBB format — validation only
53
+ // happens on commit (blur / Enter).
54
+ let textValue = $state(value ?? '');
55
+
56
+ $effect(() => {
57
+ textValue = value ?? '';
58
+ });
59
+
60
+ let swatchValue = $derived(value && HEX_PATTERN.test(value) ? value : DEFAULT_SWATCH);
61
+
62
+ export const focus = () => {
63
+ editor?.focus();
64
+ }
11
65
 
12
- export let variant: '' | 'plain' | 'outlined' | 'filled' = '';
13
- export let disabled: boolean = false;
14
- export let readonly: boolean = false;
15
- export let compact: boolean = false;
16
- export let value: any = "#ff3e00";
17
- export let style: string = '';
18
- export let onChange: OnChangeHandler<any> = null as unknown as OnChangeHandler<any>;
66
+ const clean = () => {
67
+ value = undefined;
68
+ onchange?.(undefined);
69
+ }
19
70
 
71
+ const handleActionIconClick = () => {
72
+ if (!readonly && !disabled) {
73
+ editor?.click();
74
+ }
75
+ }
20
76
 
21
- let oldValue = value;
77
+ const handleSwatchChange = (event: Event) => {
78
+ const newValue = (event.target as HTMLInputElement).value;
79
+ value = newValue;
80
+ onchange?.(newValue);
81
+ }
22
82
 
23
- $: if (oldValue != value) {
24
- oldValue = value;
83
+ // The native color picker dialog opens on `click`, and preventing the default
84
+ // action here blocks it — this is the only reliable way to make the swatch
85
+ // non-interactive for `readonly`, since <input type="color"> ignores the
86
+ // `readonly` attribute entirely. Using the native `disabled` attribute instead
87
+ // would work too, but it also greys the swatch out, which conflates readonly
88
+ // with disabled (see the component README: readonly stays fully visible,
89
+ // disabled is dimmed).
90
+ const handleColorInputClick = (event: MouseEvent) => {
91
+ if (readonly) {
92
+ event.preventDefault();
93
+ }
25
94
  }
26
95
 
27
- let editor: any;
96
+ const handleColorInputKeydown = (event: KeyboardEvent) => {
97
+ if (readonly && (event.key === 'Enter' || event.key === ' ')) {
98
+ event.preventDefault();
99
+ }
100
+ }
28
101
 
29
- const clean = () => {
30
- onChange(null)
102
+ const commitTextValue = () => {
103
+ let candidate = textValue.trim();
104
+ if (candidate && !candidate.startsWith('#')) {
105
+ candidate = `#${candidate}`;
106
+ }
107
+ if (HEX_PATTERN.test(candidate)) {
108
+ textValue = candidate;
109
+ if (candidate !== value) {
110
+ value = candidate;
111
+ onchange?.(candidate);
112
+ }
113
+ } else {
114
+ // Doesn't resolve to a valid #RRGGBB value on commit — revert to the last
115
+ // known-good value rather than accepting a malformed color.
116
+ textValue = value ?? '';
117
+ }
118
+ }
119
+
120
+ // Only '#' and hex digits are allowed while typing; the exact length requirement
121
+ // is enforced separately on commit (see commitTextValue), since rejecting
122
+ // incomplete input on every keystroke would make it impossible to type a value
123
+ // character by character.
124
+ const handleTextKeydown = (event: KeyboardEvent) => {
125
+ const allowKeys = ['Backspace', 'Delete', 'Tab', 'ArrowLeft', 'ArrowRight', 'Home', 'End'];
126
+ if (allowKeys.includes(event.key) || event.metaKey || event.ctrlKey) return;
127
+ if (event.key === 'Enter') {
128
+ event.preventDefault();
129
+ textInput?.blur();
130
+ return;
131
+ }
132
+ if (event.key === 'Escape') {
133
+ textValue = value ?? '';
134
+ textInput?.blur();
135
+ return;
136
+ }
137
+ if (!/^[0-9a-fA-F#]$/.test(event.key)) {
138
+ event.preventDefault();
139
+ }
31
140
  }
32
141
 
33
- const handleActionIconClick = async () => {
34
- editor.click();
142
+ const handleTextPaste = (event: ClipboardEvent) => {
143
+ const pasted = event.clipboardData?.getData('text') ?? '';
144
+ if (/[^0-9a-fA-F#]/.test(pasted)) {
145
+ event.preventDefault();
146
+ }
35
147
  }
36
148
 
37
- function handleColorChange(event: Event) {
38
- // selectedColor = (event.target as HTMLElement).value;
39
- // rgbValues = hexToRgb(selectedColor);
40
- // console.log(selectedColor, rgbValues);
41
- // dispatch('change', {
42
- // hex: selectedColor,
43
- // rgb: rgbValues
44
- // });
149
+ // Defense in depth against anything that bypasses handleTextKeydown (IME, mobile
150
+ // virtual keyboards, drag-and-drop text) — strip disallowed characters and cap at
151
+ // one leading '#' plus 6 hex digits.
152
+ const handleTextInput = () => {
153
+ const hasHash = textValue.startsWith('#');
154
+ const hexOnly = textValue.replace(/#/g, '').replace(/[^0-9a-fA-F]/g, '').slice(0, 6);
155
+ const sanitized = (hasHash ? '#' : '') + hexOnly;
156
+ if (sanitized !== textValue) {
157
+ textValue = sanitized;
158
+ }
45
159
  }
46
160
 
47
161
  </script>
48
162
 
49
- <CommonPicker {variant} {style} {compact} className="multiple" iconName="icon_google_color_lens"
50
- autoFit clear={clean} textValue={value} {readonly} {disabled} iconClickHandler={handleActionIconClick}>
51
- <!-- <div style="flex: 1 1 auto; width: 50%; height: 60%; border-radius: 4px; background-color: {value}"></div>-->
163
+ <CommonPicker {variant} {style} {theme} {error} {tooltip} {compact} {placeholder} className="multiple" iconName="icon_google_color_lens"
164
+ autoFit clear={clean} textValue={value} {readonly} {disabled} {onfocus} {onblur} iconClickHandler={handleActionIconClick}
165
+ input$style={HIDDEN_INTERNAL_INPUT_STYLE}>
52
166
  <div style="width: 100%; display: flex; overflow: hidden; flex-direction: row; align-items: center; height: 100%; gap: 8px">
53
- <input bind:this={editor} style="flex: 0 0 auto; width: 40%; height: 80%; border-radius: 8px;" type="color" bind:value on:change={handleColorChange}/>
54
- <input style="flex: 0 0 auto; width: 50%; text-align: center" class="text-editor" readonly value={value??''} {disabled}/>
167
+ <input bind:this={editor} style="flex: 0 0 auto; width: 40%; height: 80%; border-radius: 8px;" type="color" value={swatchValue} {disabled}
168
+ tabindex={readonly ? -1 : 0} onclick={handleColorInputClick} onkeydown={handleColorInputKeydown} onchange={handleSwatchChange}/>
169
+ <input bind:this={textInput} style="flex: 0 0 auto; width: 50%; text-align: center" class="text-editor" bind:value={textValue}
170
+ readonly={readonly} {disabled} {placeholder} maxlength="7" spellcheck="false" autocomplete="off"
171
+ onblur={commitTextValue} onkeydown={handleTextKeydown} onpaste={handleTextPaste} oninput={handleTextInput}/>
55
172
  </div>
56
-
57
-
58
173
  </CommonPicker>
@@ -1,27 +1,9 @@
1
- import type { OnChangeHandler } from "../../types";
2
- interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
3
- new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
4
- $$bindings?: Bindings;
5
- } & Exports;
6
- (internal: unknown, props: Props & {
7
- $$events?: Events;
8
- $$slots?: Slots;
9
- }): Exports & {
10
- $set?: any;
11
- $on?: any;
12
- };
13
- z_$$bindings?: Bindings;
1
+ import type CommonFieldProps from "../common_editor/CommonFieldProps";
2
+ interface Props extends CommonFieldProps {
3
+ value?: string;
14
4
  }
15
- declare const ColorPicker: $$__sveltets_2_IsomorphicComponent<{
16
- variant?: "" | "plain" | "outlined" | "filled";
17
- disabled?: boolean;
18
- readonly?: boolean;
19
- compact?: boolean;
20
- value?: any;
21
- style?: string;
22
- onChange?: OnChangeHandler<any>;
23
- }, {
24
- [evt: string]: CustomEvent<any>;
25
- }, {}, {}, string>;
26
- type ColorPicker = InstanceType<typeof ColorPicker>;
5
+ declare const ColorPicker: import("svelte").Component<Props, {
6
+ focus: () => void;
7
+ }, "value">;
8
+ type ColorPicker = ReturnType<typeof ColorPicker>;
27
9
  export default ColorPicker;
@@ -3,14 +3,25 @@
3
3
 
4
4
  import i18nRes from "../../i18nRes";
5
5
 
6
- export let initialColor = "#ffffff";
7
- export let showHex = true;
8
- export let showRgb = false;
9
- export let swatches: Array<any> = [];
6
+ interface Props {
7
+ initialColor?: string;
8
+ showHex?: boolean;
9
+ showRgb?: boolean;
10
+ swatches?: Array<any>;
11
+ onchange?: (color: string) => void;
12
+ }
13
+
14
+ let {initialColor = "#ffffff", showHex = true, showRgb = false, swatches = [], onchange}: Props = $props();
10
15
 
11
- let selectedColor = initialColor;
12
- let rgbValues = hexToRgb(initialColor);
16
+ // Neutral initial value + effect-driven sync (rather than $state(initialColor))
17
+ // avoids implying initialColor is captured only once; the effect keeps
18
+ // selectedColor in sync whenever the prop changes later.
19
+ let selectedColor = $state('#ffffff');
20
+ let rgbValues = $derived(hexToRgb(selectedColor));
13
21
 
22
+ $effect(() => {
23
+ selectedColor = initialColor;
24
+ });
14
25
 
15
26
  // Convert hex to RGB
16
27
  function hexToRgb(hex: string) {
@@ -22,24 +33,15 @@
22
33
  } : { r: 0, g: 0, b: 0 };
23
34
  }
24
35
 
25
- // Emit change event when color changes
36
+ // selectedColor is already updated by bind:value before this fires
26
37
  function handleColorChange(event: Event) {
27
- selectedColor = "0x000000"; //event.target?.value;
28
- rgbValues = hexToRgb(selectedColor);
29
- // dispatch('change', {
30
- // hex: selectedColor,
31
- // rgb: rgbValues
32
- // });
38
+ onchange?.(selectedColor);
33
39
  }
34
40
 
35
41
  // Select a swatch
36
42
  function selectSwatch(color: any) {
37
43
  selectedColor = color;
38
- rgbValues = hexToRgb(selectedColor);
39
- // dispatch('change', {
40
- // hex: selectedColor,
41
- // rgb: rgbValues
42
- // });
44
+ onchange?.(selectedColor);
43
45
  }
44
46
  </script>
45
47
 
@@ -53,7 +55,7 @@
53
55
  id="color-input"
54
56
  type="color"
55
57
  bind:value={selectedColor}
56
- on:change={handleColorChange}
58
+ onchange={handleColorChange}
57
59
  />
58
60
  <div
59
61
  class="color-preview"
@@ -77,9 +79,10 @@
77
79
  <div class="swatches">
78
80
  {#each swatches as swatch}
79
81
  <button
82
+ type="button"
80
83
  class="swatch"
81
84
  style="background-color: {swatch};"
82
- on:click={() => selectSwatch(swatch)}
85
+ onclick={() => selectSwatch(swatch)}
83
86
  aria-label={`Select color ${swatch}`}
84
87
  class:selected={selectedColor === swatch}
85
88
  ></button>
@@ -1,23 +1,10 @@
1
- interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
2
- new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
3
- $$bindings?: Bindings;
4
- } & Exports;
5
- (internal: unknown, props: Props & {
6
- $$events?: Events;
7
- $$slots?: Slots;
8
- }): Exports & {
9
- $set?: any;
10
- $on?: any;
11
- };
12
- z_$$bindings?: Bindings;
13
- }
14
- declare const ColorPickerPanel: $$__sveltets_2_IsomorphicComponent<{
1
+ interface Props {
15
2
  initialColor?: string;
16
3
  showHex?: boolean;
17
4
  showRgb?: boolean;
18
5
  swatches?: Array<any>;
19
- }, {
20
- [evt: string]: CustomEvent<any>;
21
- }, {}, {}, string>;
22
- type ColorPickerPanel = InstanceType<typeof ColorPickerPanel>;
6
+ onchange?: (color: string) => void;
7
+ }
8
+ declare const ColorPickerPanel: import("svelte").Component<Props, {}, "">;
9
+ type ColorPickerPanel = ReturnType<typeof ColorPickerPanel>;
23
10
  export default ColorPickerPanel;
@@ -40,7 +40,7 @@ import ColorPicker from "@ticatec/uniface-element/ColorPicker";
40
40
 
41
41
  | Prop | Type | Default | Description |
42
42
  |------|------|---------|-------------|
43
- | `value` | `string` (bindable) | `"#000000"` | The selected color in hex format (e.g., `#3b82f6`). `bind:value={...}` for two-way binding. |
43
+ | `value` | `string` (bindable) | `"#000000"` | The selected color in `#RRGGBB` hex format (e.g., `#3b82f6`). `bind:value={...}` for two-way binding. |
44
44
  | `variant` | `'' \| 'plain' \| 'outlined' \| 'filled'` | `''` | Visual style variant. |
45
45
  | `compact` | `boolean` | `false` | Dense layout for table cells and inline forms. |
46
46
  | `disabled` | `boolean` | `false` | Fully disabled — the color picker is non-interactive. |
@@ -263,18 +263,25 @@ import ColorPicker from "@ticatec/uniface-element/ColorPicker";
263
263
 
264
264
  ## Behavior Notes
265
265
 
266
- ### Native color input
266
+ ### Native color input + editable hex field
267
267
 
268
- The component uses the browser's native `<input type="color">` element, which provides a consistent color picker across all platforms. The visual swatch is a custom preview layer on top of the native input.
268
+ The component shows two parts side by side: a native `<input type="color">` swatch (opens the browser's built-in color picker) and a text field showing the hex value. The text field is directly editable — typing a value and committing it (blur or Enter) updates `value` the same as picking a color from the swatch.
269
269
 
270
- ### Hex format
270
+ There's no alpha/transparency support — `<input type="color">` has no concept of it, so `value` is always a plain 6-digit `#rrggbb`.
271
271
 
272
- The `value` prop expects and returns colors in hex format (e.g., `#3b82f6`). The native color input always returns hex format.
272
+ ### Hex format and validation
273
+
274
+ `value` must be exactly `#RRGGBB` (6 hex digits after `#`, case-insensitive). When typing in the text field:
275
+
276
+ - Only `#` and hex digits (`0-9a-fA-F`) can be typed, capped at 6 digits — anything else is blocked or truncated as you type.
277
+ - The value is validated on commit (blur or Enter), not on every keystroke, so you can type through incomplete intermediate states.
278
+ - A missing leading `#` is added automatically on commit (e.g. typing `ff0000` commits as `#ff0000`).
279
+ - If the committed text isn't a valid `#RRGGBB` string, the field reverts to the last valid value — `onchange` is not called.
273
280
 
274
281
  ### Disabled and readonly states
275
282
 
276
- - **Disabled**: The color picker is non-interactive. The visual swatch is dimmed.
277
- - **Readonly**: The color picker is non-interactive but the visual swatch is fully visible.
283
+ - **Disabled**: The color picker is non-interactive. The visual swatch is dimmed, and the hex field is disabled.
284
+ - **Readonly**: The color picker is non-interactive but fully visible — the swatch blocks clicks (it can't open the native picker) and the hex field becomes a non-editable (but not dimmed) readonly text field.
278
285
 
279
286
  ## Accessibility
280
287
 
@@ -40,7 +40,7 @@ import ColorPicker from "@ticatec/uniface-element/ColorPicker";
40
40
 
41
41
  | 属性 | 类型 | 默认值 | 说明 |
42
42
  |------|------|--------|------|
43
- | `value` | `string`(可绑定) | `"#000000"` | hex 格式选择的颜色(例如 `#3b82f6`)。`bind:value={...}` 用于双向绑定。 |
43
+ | `value` | `string`(可绑定) | `"#000000"` | `#RRGGBB` 格式的颜色(例如 `#3b82f6`)。`bind:value={...}` 用于双向绑定。 |
44
44
  | `variant` | `'' \| 'plain' \| 'outlined' \| 'filled'` | `''` | 视觉风格变体。 |
45
45
  | `compact` | `boolean` | `false` | 紧凑布局,适用于表格单元格和行内表单。 |
46
46
  | `disabled` | `boolean` | `false` | 完全禁用 —— 颜色选择器不可交互。 |
@@ -263,18 +263,25 @@ import ColorPicker from "@ticatec/uniface-element/ColorPicker";
263
263
 
264
264
  ## 行为说明
265
265
 
266
- ### 原生颜色输入
266
+ ### 原生颜色输入 + 可编辑的 hex 文本框
267
267
 
268
- 该组件使用浏览器的原生 `<input type="color">` 元素,在所有平台上提供一致的颜色选择器。视觉色板是原生输入上方的自定义预览层。
268
+ 组件并排展示两部分:原生 `<input type="color">` 色板(点击打开浏览器内置的颜色选择器)和展示 hex 值的文本框。文本框可以直接编辑——输入一个值并提交(失焦或回车)后,`value` 会像通过色板选色一样更新。
269
269
 
270
- ### Hex 格式
270
+ 不支持透明通道——`<input type="color">` 本身没有这个概念,因此 `value` 始终是纯 6 位 `#rrggbb`。
271
271
 
272
- `value` 属性期望并返回 hex 格式的颜色(例如 `#3b82f6`)。原生颜色输入始终返回 hex 格式。
272
+ ### Hex 格式与校验
273
+
274
+ `value` 必须是合法的 `#RRGGBB`(`#` 后跟 6 位十六进制数字,大小写不敏感)。在文本框中输入时:
275
+
276
+ - 只能输入 `#` 和十六进制字符(`0-9a-fA-F`),最多 6 位——其他字符在输入时会被拦截或截断。
277
+ - 校验只在提交时(失焦或回车)进行,不是每次按键都校验,因此可以正常输入中间的不完整状态。
278
+ - 提交时如果缺少开头的 `#` 会自动补上(例如输入 `ff0000` 会被提交为 `#ff0000`)。
279
+ - 如果提交的文本不是合法的 `#RRGGBB`,文本框会回退到上一个合法值——不会触发 `onchange`。
273
280
 
274
281
  ### 禁用和只读状态
275
282
 
276
- - **禁用**:颜色选择器不可交互。视觉色板变暗。
277
- - **只读**:颜色选择器不可交互,但视觉色板完全可见。
283
+ - **禁用**:颜色选择器不可交互。视觉色板变暗,hex 文本框也被禁用。
284
+ - **只读**:颜色选择器不可交互,但完全可见——色板会拦截点击(无法打开原生选择器),hex 文本框变为不可编辑(但不变暗)的只读文本框。
278
285
 
279
286
  ## 无障碍
280
287
 
@@ -53,7 +53,14 @@
53
53
  </script>
54
54
 
55
55
  <FormEditor {theme} {tooltip} {error} {style} {readonly} {disabled} {compact} {variant}>
56
- <div class="uniface-common-editor" role="textbox" bind:this={container} aria-hidden="true" tabindex="-1" {style}
56
+ <!-- Decorative flex wrapper only: the real interactive control is the
57
+ <input>/children snippet rendered inside, which carries its own
58
+ semantics — this div must not steal or hide them via role/aria-hidden.
59
+ onclick here only forwards focus to that inner control, it's not an
60
+ independent action, so no interactive role/keyboard handler applies. -->
61
+ <!-- svelte-ignore a11y_click_events_have_key_events -->
62
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
63
+ <div class="uniface-common-editor" bind:this={container} {style}
57
64
  onmousedown={(e: MouseEvent)=>{e.preventDefault()}} onclick={handleContainerClick}>
58
65
  {#if leadingIcon}
59
66
  <div class="editor-embed-icon" style="flex: 0 0 auto;">
@@ -69,9 +76,16 @@
69
76
  {:else}
70
77
  <div class={input$class} style="position: relative; flex: 1 1 auto">
71
78
  {@render children()}
72
- {#if textValue && canClean}
73
- <div class="clean-icon">
74
- <i class="icon_google_clear" aria-hidden="true" onmousedown={(e: MouseEvent)=>{e.preventDefault()}} onclick={clear}></i>
79
+ {#if canClean}
80
+ <!-- Reserved unconditionally (not gated on textValue) so
81
+ .uniface-common-editor:has(.clean-icon)'s input
82
+ padding-right in uniface-form-editor.scss stays
83
+ constant — otherwise the editor visibly grows/shifts
84
+ the moment text is first entered. -->
85
+ <div class="clean-icon" class:has-value={textValue}>
86
+ <i class="icon_google_clear" role="button" tabindex={textValue ? 0 : -1} aria-label="Clear"
87
+ onmousedown={(e: MouseEvent)=>{e.preventDefault()}} onclick={textValue ? clear : undefined}
88
+ onkeydown={(e: KeyboardEvent) => { if (textValue && (e.key === 'Enter' || e.key === ' ')) { e.preventDefault(); clear?.(); } }}></i>
75
89
  </div>
76
90
  {/if}
77
91
  </div>
@@ -70,7 +70,6 @@
70
70
  let editorElement: HTMLElement | undefined = $state();
71
71
  let editor: HTMLDivElement | undefined = $state();
72
72
  let hasFocus = $state(false);
73
- let canClean = $state(!mandatory && value != null);
74
73
 
75
74
 
76
75
  const handleFocus = (event: FocusEvent) => {
@@ -111,7 +110,6 @@
111
110
  }
112
111
 
113
112
  $effect(() => {
114
- canClean = !mandatory && value != null;
115
113
  if (!isOpen && hasFocus) {
116
114
  setTimeout(() => {
117
115
  if (document.activeElement !== editor) {
@@ -148,20 +146,28 @@
148
146
  <input style={input$style} tabindex="-1" readonly {disabled} value={textValue}/>
149
147
  {/if}
150
148
  {:else}
151
- <div style="flex: 1 1 auto; position: relative; overflow: hidden; height: 100%" onclick={openPopup}>
149
+ <div style="flex: 1 1 auto; position: relative; overflow: hidden; height: 100%">
152
150
  {#if input$readonly}
153
- <input bind:this={editor} style={input$style} value={textValue} readonly {placeholder} onkeydown={handleKeyDown}
151
+ <input bind:this={editor} style={input$style} value={textValue} readonly {placeholder} onclick={openPopup} onkeydown={handleKeyDown}
154
152
  tabindex="0"/>
155
153
  {:else}
156
- <input bind:this={editor} style={input$style} value={textValue} {oninput} {oncompositionstart} {oncompositionend}
154
+ <input bind:this={editor} style={input$style} value={textValue} {oninput} {oncompositionstart} {oncompositionend} onclick={openPopup}
157
155
  {placeholder} tabindex="0" onkeydown={handleKeyDown}/>
158
156
  {/if}
159
157
  {#if children}
160
158
  {@render children()}
161
159
  {/if}
162
- {#if textValue && canClean}
163
- <div class="clean-icon">
164
- <i class="icon_google_clear" role="none" onclick={doClean}></i>
160
+ {#if !mandatory}
161
+ <!-- Space is reserved (and input padding-right adjusted via
162
+ .uniface-common-picker:has(.clean-icon) in
163
+ uniface-form-editor.scss) whenever clearing is allowed
164
+ at all, not only once a value is picked — otherwise the
165
+ whole picker visibly grows/shifts by that padding the
166
+ moment a value is first selected. Visibility of the
167
+ glyph itself still depends on there being something to
168
+ clear. -->
169
+ <div class="clean-icon" class:has-value={textValue}>
170
+ <i class="icon_google_clear" role="none" onclick={textValue ? doClean : undefined}></i>
165
171
  </div>
166
172
  {/if}
167
173
  </div>
@@ -181,5 +187,3 @@
181
187
  {/if}
182
188
  </Popover>
183
189
  {/if}
184
-
185
-
@@ -57,6 +57,12 @@
57
57
  showPopup = false;
58
58
  }
59
59
 
60
+ const handleTriggerKeyDown = (e: KeyboardEvent) => {
61
+ if (e.key === 'Enter' || e.key === ' ') {
62
+ e.preventDefault();
63
+ openPopup();
64
+ }
65
+ };
60
66
  </script>
61
67
 
62
68
  <CommonEditor
@@ -76,7 +82,8 @@
76
82
 
77
83
  {#snippet trailingIcon()}
78
84
  {#if !disabled && !readonly}
79
- <i class="icon_google_arrow_drop_down" aria-hidden="true" onclick={openPopup}></i>
85
+ <i class="icon_google_arrow_drop_down" role="button" tabindex="0" aria-label="Open" aria-expanded={showPopup}
86
+ onclick={openPopup} onkeydown={handleTriggerKeyDown}></i>
80
87
  {/if}
81
88
  {/snippet}
82
89