@ticatec/uniface-element 5.0.1 → 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 (255) hide show
  1. package/README.md +29 -54
  2. package/README_CN.md +37 -57
  3. package/dist/base-calendar/CalendarBase.svelte +29 -6
  4. package/dist/base-calendar/CalendarBase.svelte.d.ts +4 -0
  5. package/dist/base-calendar/MonthPickupPanel.svelte +19 -6
  6. package/dist/base-calendar/MonthPickupPanel.svelte.d.ts +1 -1
  7. package/dist/base-calendar/MonthlyCalendar.svelte +18 -2
  8. package/dist/base-calendar/YearPickupPanel.svelte +24 -6
  9. package/dist/base-calendar/YearPickupPanel.svelte.d.ts +1 -1
  10. package/dist/composite/pagination_panel/PaginationPanel.svelte +2 -0
  11. package/dist/data_display/card/CommonCardActionBar.svelte +34 -14
  12. package/dist/data_display/concise_data_table/Column.d.ts +6 -0
  13. package/dist/data_display/concise_data_table/ConciseListTable.svelte +4 -3
  14. package/dist/data_display/concise_data_table/DataCell.svelte +3 -3
  15. package/dist/data_display/list_box/ListBox.svelte +90 -15
  16. package/dist/data_display/list_box/ListBox.svelte.d.ts +4 -0
  17. package/dist/data_display/tree_view/TreeNodeView.svelte +25 -4
  18. package/dist/data_table/DataTable.svelte +5 -5
  19. package/dist/data_table/DataTable.svelte.d.ts +36 -16
  20. package/dist/data_table/lib/ActionsColumn.d.ts +2 -2
  21. package/dist/data_table/lib/DataColumn.d.ts +7 -1
  22. package/dist/data_table/lib/RowAction.d.ts +5 -5
  23. package/dist/data_table/parts/FixedHeaderPanel.svelte +18 -3
  24. package/dist/data_table/parts/FixedRow.svelte +10 -1
  25. package/dist/data_table/parts/HrefCell.svelte +1 -1
  26. package/dist/data_table/parts/TableHeaderPanel.svelte +18 -2
  27. package/dist/data_table/parts/TextCell.svelte +2 -2
  28. package/dist/form/attachment_files/AttachmentEditor.svelte +9 -9
  29. package/dist/form/attachment_files/FileRender.svelte +1 -1
  30. package/dist/form/attachment_files/FileUploadPanel.svelte +5 -1
  31. package/dist/form/cascade_options_select/CascadeOptionSelect.svelte +10 -2
  32. package/dist/form/cascade_options_select/OptionsBox.svelte +48 -2
  33. package/dist/form/color_picker/ColorPickerPanel.svelte +23 -20
  34. package/dist/form/color_picker/ColorPickerPanel.svelte.d.ts +5 -18
  35. package/dist/form/common_editor/CommonEditor.svelte +18 -4
  36. package/dist/form/common_editor/CommonPicker.svelte +11 -4
  37. package/dist/form/common_editor/CommonPopupEditor.svelte +8 -1
  38. package/dist/form/common_range_editor/CommonRangeEditor.svelte +11 -2
  39. package/dist/form/date_picker/DatePicker.svelte +1 -1
  40. package/dist/form/date_picker/DateTimePicker.svelte +1 -1
  41. package/dist/form/date_picker/ScrollBar.svelte +20 -12
  42. package/dist/form/date_picker/ScrollBar.svelte.d.ts +4 -18
  43. package/dist/form/date_range/DateRangeEditor.svelte +11 -3
  44. package/dist/form/group_box/group-check-box/GroupCheckBox.svelte +6 -1
  45. package/dist/form/group_box/group-radio-box/GroupRadioBox.svelte +6 -1
  46. package/dist/form/image-files/ImageFilesField.svelte +51 -7
  47. package/dist/form/image-files/ImagePreview.svelte +8 -2
  48. package/dist/form/image-files/ImageRender.svelte +69 -11
  49. package/dist/form/input_options_select/InputOptionsSelect.svelte +10 -2
  50. package/dist/form/lookup_editor/LookupEditor.svelte +9 -2
  51. package/dist/form/options_multi_select/OptionsMultiSelect.svelte +19 -3
  52. package/dist/form/options_select/OptionsSelect.svelte +15 -6
  53. package/dist/form/options_select/OptionsSelect.svelte.d.ts +39 -17
  54. package/dist/form/prompts_text_editor/PromptsTextEditor.svelte +10 -2
  55. package/dist/form/switch/Switch.svelte +4 -4
  56. package/dist/form/unit-number-editor/UnitNumberEditor.svelte +10 -2
  57. package/dist/general/button/Button.svelte +3 -0
  58. package/dist/general/button/Button.svelte.d.ts +1 -0
  59. package/dist/general/button/IconButton.svelte +3 -1
  60. package/dist/general/button/IconButton.svelte.d.ts +1 -0
  61. package/dist/general/button/TextButton.svelte +3 -0
  62. package/dist/general/button/TextButton.svelte.d.ts +1 -0
  63. package/dist/general/icon/Icon.svelte +18 -1
  64. package/dist/general/tag/Tag.svelte +9 -1
  65. package/dist/navigation/nav_menu/NavigatorMenu.svelte +2 -2
  66. package/dist/navigation/nav_menu/NavigatorMenuItem.svelte +10 -1
  67. package/dist/navigation/navigator/Navigator.svelte +17 -17
  68. package/dist/navigation/page-stack/PageStack.svelte +160 -0
  69. package/dist/navigation/page-stack/PageStack.svelte.d.ts +24 -0
  70. package/dist/navigation/page-stack/PageStackContext.d.ts +25 -0
  71. package/dist/navigation/page-stack/PageStackContext.js +12 -0
  72. package/dist/navigation/page-stack/PageStackFrame.svelte +24 -0
  73. package/dist/navigation/page-stack/PageStackFrame.svelte.d.ts +11 -0
  74. package/dist/navigation/page-stack/README.md +154 -0
  75. package/dist/navigation/page-stack/README_CN.md +154 -0
  76. package/dist/navigation/page-stack/index.d.ts +6 -0
  77. package/dist/navigation/page-stack/index.js +4 -0
  78. package/dist/navigation/pagination/Pagination.svelte +114 -14
  79. package/dist/navigation/tabs/Tabs.svelte +42 -10
  80. package/dist/navigation/tabs/Tabs.svelte.d.ts +2 -2
  81. package/dist/overlay/common/FloatingPanel.svelte +3 -11
  82. package/dist/overlay/common/FullscreenOverlay.svelte +27 -3
  83. package/dist/overlay/common/FullscreenOverlay.svelte.d.ts +6 -27
  84. package/dist/overlay/common/ModalPopover.svelte +18 -2
  85. package/dist/overlay/common/Popover.svelte +56 -57
  86. package/dist/overlay/common/Popover.svelte.d.ts +1 -5
  87. package/dist/overlay/common/escapeClose.d.ts +28 -0
  88. package/dist/overlay/common/escapeClose.js +47 -0
  89. package/dist/overlay/common/focusTrap.d.ts +8 -0
  90. package/dist/overlay/common/focusTrap.js +83 -0
  91. package/dist/overlay/common/mountSingleton.d.ts +27 -0
  92. package/dist/overlay/common/mountSingleton.js +45 -0
  93. package/dist/overlay/common/outsideClick.d.ts +28 -0
  94. package/dist/overlay/common/outsideClick.js +20 -0
  95. package/dist/overlay/common/scrollLock.d.ts +8 -0
  96. package/dist/overlay/common/scrollLock.js +49 -0
  97. package/dist/overlay/context-menu/ContextMenu.svelte.js +2 -15
  98. package/dist/overlay/context-menu/ContextMenuPanel.svelte +13 -1
  99. package/dist/overlay/dialog/Dialog.svelte +46 -29
  100. package/dist/overlay/dialog/DialogManager.svelte.js +7 -9
  101. package/dist/overlay/drawer/Drawer.svelte +30 -3
  102. package/dist/overlay/indicator/Indicator.svelte.js +2 -15
  103. package/dist/overlay/indicator/IndicatorPanel.svelte +4 -1
  104. package/dist/overlay/message-box/MessageBox.svelte.js +2 -15
  105. package/dist/overlay/message-box/MessageBoxPanel.svelte +19 -3
  106. package/dist/overlay/toast/Toast.svelte.js +2 -15
  107. package/dist/overlay/toast/ToastPanel.svelte +1 -1
  108. package/dist/overlay/tooltip/Tooltip.svelte +1 -1
  109. package/dist/ticatec-uniface-web.css +102 -7
  110. package/dist/types.d.ts +4 -3
  111. package/docs/components/README.md +133 -0
  112. package/docs/components/accordion/README.md +60 -0
  113. package/docs/components/accordion/example.svelte +7 -0
  114. package/docs/components/action-bar/README.md +114 -0
  115. package/docs/components/action-bar/example.svelte +7 -0
  116. package/docs/components/app-top-bar/README.md +60 -0
  117. package/docs/components/app-top-bar/example.svelte +9 -0
  118. package/docs/components/attachment-files/README.md +332 -0
  119. package/docs/components/attachment-files/example.svelte +16 -0
  120. package/docs/components/box/README.md +63 -0
  121. package/docs/components/box/example.svelte +10 -0
  122. package/docs/components/breadcrumbs/README.md +86 -0
  123. package/docs/components/breadcrumbs/example.svelte +11 -0
  124. package/docs/components/button/README.md +103 -0
  125. package/docs/components/button/example.svelte +10 -0
  126. package/docs/components/card/README.md +113 -0
  127. package/docs/components/card/example.svelte +10 -0
  128. package/docs/components/cascade-options-select/README.md +162 -0
  129. package/docs/components/cascade-options-select/example.svelte +18 -0
  130. package/docs/components/checkbox/README.md +103 -0
  131. package/docs/components/checkbox/example.svelte +15 -0
  132. package/docs/components/classic-layout/README.md +76 -0
  133. package/docs/components/classic-layout/example.svelte +16 -0
  134. package/docs/components/color-picker/README.md +132 -0
  135. package/docs/components/color-picker/example.svelte +16 -0
  136. package/docs/components/columns-layout/README.md +70 -0
  137. package/docs/components/columns-layout/example.svelte +12 -0
  138. package/docs/components/common-dialog/README.md +109 -0
  139. package/docs/components/common-dialog/example.svelte +12 -0
  140. package/docs/components/concise-data-table/README.md +307 -0
  141. package/docs/components/concise-data-table/example.svelte +16 -0
  142. package/docs/components/context-menu/README.md +99 -0
  143. package/docs/components/context-menu/example.svelte +10 -0
  144. package/docs/components/criteria-field/README.md +69 -0
  145. package/docs/components/criteria-field/example.svelte +9 -0
  146. package/docs/components/data-table/README.md +365 -0
  147. package/docs/components/data-table/example.svelte +23 -0
  148. package/docs/components/date-picker/README.md +136 -0
  149. package/docs/components/date-picker/example.svelte +16 -0
  150. package/docs/components/date-range/README.md +122 -0
  151. package/docs/components/date-range/example.svelte +15 -0
  152. package/docs/components/date-time-picker/README.md +142 -0
  153. package/docs/components/date-time-picker/example.svelte +16 -0
  154. package/docs/components/dialog/README.md +174 -0
  155. package/docs/components/dialog/example.svelte +13 -0
  156. package/docs/components/dialog-board/README.md +41 -0
  157. package/docs/components/dialog-board/example.svelte +5 -0
  158. package/docs/components/drawer/README.md +70 -0
  159. package/docs/components/drawer/example.svelte +12 -0
  160. package/docs/components/flex-form/README.md +55 -0
  161. package/docs/components/flex-form/example.svelte +7 -0
  162. package/docs/components/flex-row-form/README.md +56 -0
  163. package/docs/components/flex-row-form/example.svelte +7 -0
  164. package/docs/components/form-field/README.md +84 -0
  165. package/docs/components/form-field/example.svelte +10 -0
  166. package/docs/components/full-screen-overlay/README.md +66 -0
  167. package/docs/components/full-screen-overlay/example.svelte +11 -0
  168. package/docs/components/grid-form/README.md +75 -0
  169. package/docs/components/grid-form/example.svelte +9 -0
  170. package/docs/components/group-check-box/README.md +147 -0
  171. package/docs/components/group-check-box/example.svelte +21 -0
  172. package/docs/components/group-radio-box/README.md +144 -0
  173. package/docs/components/group-radio-box/example.svelte +21 -0
  174. package/docs/components/header-layout/README.md +65 -0
  175. package/docs/components/header-layout/example.svelte +12 -0
  176. package/docs/components/icon/README.md +87 -0
  177. package/docs/components/icon/example.svelte +9 -0
  178. package/docs/components/icon-button/README.md +91 -0
  179. package/docs/components/icon-button/example.svelte +10 -0
  180. package/docs/components/image-files/README.md +174 -0
  181. package/docs/components/image-files/example.svelte +19 -0
  182. package/docs/components/indicator/README.md +53 -0
  183. package/docs/components/indicator/example.svelte +14 -0
  184. package/docs/components/input-options-select/README.md +163 -0
  185. package/docs/components/input-options-select/example.svelte +16 -0
  186. package/docs/components/list-box/ListItem.svelte +14 -0
  187. package/docs/components/list-box/README.md +221 -0
  188. package/docs/components/list-box/example.svelte +61 -0
  189. package/docs/components/lookup-editor/README.md +137 -0
  190. package/docs/components/lookup-editor/example.svelte +16 -0
  191. package/docs/components/memo-editor/README.md +143 -0
  192. package/docs/components/memo-editor/example.svelte +16 -0
  193. package/docs/components/message-box/README.md +68 -0
  194. package/docs/components/message-box/example.svelte +10 -0
  195. package/docs/components/navigator/README.md +75 -0
  196. package/docs/components/navigator/example.svelte +13 -0
  197. package/docs/components/navigator-menu/README.md +113 -0
  198. package/docs/components/navigator-menu/example.svelte +10 -0
  199. package/docs/components/number-editor/README.md +144 -0
  200. package/docs/components/number-editor/example.svelte +16 -0
  201. package/docs/components/number-range/README.md +116 -0
  202. package/docs/components/number-range/example.svelte +15 -0
  203. package/docs/components/options-multi-select/README.md +161 -0
  204. package/docs/components/options-multi-select/example.svelte +20 -0
  205. package/docs/components/options-select/README.md +160 -0
  206. package/docs/components/options-select/example.svelte +21 -0
  207. package/docs/components/page/README.md +77 -0
  208. package/docs/components/page/example.svelte +10 -0
  209. package/docs/components/pagination/README.md +88 -0
  210. package/docs/components/pagination/example.svelte +10 -0
  211. package/docs/components/pagination-panel/README.md +120 -0
  212. package/docs/components/pagination-panel/example.svelte +16 -0
  213. package/docs/components/popup-hint/README.md +44 -0
  214. package/docs/components/popup-hint/example.svelte +5 -0
  215. package/docs/components/progress-bar/README.md +64 -0
  216. package/docs/components/progress-bar/example.svelte +8 -0
  217. package/docs/components/progress-step-bar/README.md +93 -0
  218. package/docs/components/progress-step-bar/example.svelte +7 -0
  219. package/docs/components/prompts-text-editor/README.md +146 -0
  220. package/docs/components/prompts-text-editor/example.svelte +17 -0
  221. package/docs/components/radio-button/README.md +121 -0
  222. package/docs/components/radio-button/example.svelte +18 -0
  223. package/docs/components/rows-layout/README.md +70 -0
  224. package/docs/components/rows-layout/example.svelte +12 -0
  225. package/docs/components/search-box/README.md +136 -0
  226. package/docs/components/search-box/example.svelte +16 -0
  227. package/docs/components/separator/README.md +44 -0
  228. package/docs/components/separator/example.svelte +7 -0
  229. package/docs/components/sidebar-layout/README.md +65 -0
  230. package/docs/components/sidebar-layout/example.svelte +12 -0
  231. package/docs/components/split/README.md +48 -0
  232. package/docs/components/split/example.svelte +8 -0
  233. package/docs/components/switch/README.md +77 -0
  234. package/docs/components/switch/example.svelte +11 -0
  235. package/docs/components/tabs/README.md +127 -0
  236. package/docs/components/tabs/example.svelte +22 -0
  237. package/docs/components/tag/README.md +63 -0
  238. package/docs/components/tag/example.svelte +8 -0
  239. package/docs/components/text-button/README.md +90 -0
  240. package/docs/components/text-button/example.svelte +9 -0
  241. package/docs/components/text-editor/README.md +147 -0
  242. package/docs/components/text-editor/example.svelte +16 -0
  243. package/docs/components/time-editor/README.md +130 -0
  244. package/docs/components/time-editor/example.svelte +16 -0
  245. package/docs/components/toast/README.md +52 -0
  246. package/docs/components/toast/example.svelte +9 -0
  247. package/docs/components/tooltip/README.md +44 -0
  248. package/docs/components/tooltip/example.svelte +5 -0
  249. package/docs/components/transfer/README.md +109 -0
  250. package/docs/components/transfer/example.svelte +13 -0
  251. package/docs/components/tree-view/README.md +163 -0
  252. package/docs/components/tree-view/example.svelte +26 -0
  253. package/docs/components/unit-number-editor/README.md +192 -0
  254. package/docs/components/unit-number-editor/example.svelte +18 -0
  255. package/package.json +15 -7
@@ -26,6 +26,13 @@
26
26
  clickEnable = true;
27
27
  }
28
28
  };
29
+
30
+ const handleActionKeyDown = (action: CardAction) => (e: KeyboardEvent) => {
31
+ if (e.key === 'Enter' || e.key === ' ') {
32
+ e.preventDefault();
33
+ handleActionClick(action)(e as unknown as MouseEvent);
34
+ }
35
+ };
29
36
  </script>
30
37
 
31
38
  <div class="card-action-bar">
@@ -34,18 +41,21 @@
34
41
  <i class="action-icon {action.icon}"
35
42
  class:disabled={action.disabled}
36
43
  data-tip={action.disabled ? undefined : action.tooltip}
37
- aria-hidden="true"
44
+ role="button" tabindex={action.disabled ? -1 : 0}
45
+ aria-disabled={action.disabled}
46
+ aria-label={action.tooltip}
38
47
  onclick={handleActionClick(action)}
48
+ onkeydown={handleActionKeyDown(action)}
39
49
  ></i>
40
50
  {:else if action.img}
41
- <img class="card-action-img"
42
- class:disabled={action.disabled}
43
- alt=""
44
- src={action.img}
45
- data-tip={action.disabled ? undefined : action.tooltip}
46
- aria-hidden="true"
47
- onclick={handleActionClick(action)}
48
- />
51
+ <button type="button" class="card-action-img-btn"
52
+ class:disabled={action.disabled}
53
+ disabled={action.disabled}
54
+ data-tip={action.disabled ? undefined : action.tooltip}
55
+ aria-label={action.tooltip}
56
+ onclick={handleActionClick(action)}>
57
+ <img class="card-action-img" alt="" src={action.img}/>
58
+ </button>
49
59
  {/if}
50
60
 
51
61
  {/each}
@@ -89,20 +99,30 @@
89
99
  }
90
100
 
91
101
 
92
- .card-action-img {
93
- width: var(--action-image-size, var(--uniface-action-image-size));
94
- height: var(--action-image-size, var(--uniface-action-image-size));
102
+ .card-action-img-btn {
103
+ background: none;
104
+ border: none;
105
+ padding: 0;
95
106
  cursor: pointer;
107
+ display: inline-flex;
96
108
  }
97
109
 
98
- .card-action-img:active {
110
+ .card-action-img-btn:active {
99
111
  position: relative;
100
112
  top: 1px;
101
113
  }
102
114
 
103
- .card-action-img.disabled {
115
+ .card-action-img-btn.disabled {
104
116
  cursor: not-allowed;
117
+ }
118
+
119
+ .card-action-img-btn.disabled .card-action-img {
105
120
  filter: grayscale(100%);
106
121
  }
107
122
 
123
+ .card-action-img {
124
+ width: var(--action-image-size, var(--uniface-action-image-size));
125
+ height: var(--action-image-size, var(--uniface-action-image-size));
126
+ }
127
+
108
128
  </style>
@@ -5,5 +5,11 @@ export default interface Column {
5
5
  align?: "left" | "center" | "right";
6
6
  weight?: number;
7
7
  formatter?: (value: any) => string;
8
+ renderHTML?: boolean;
9
+ /**
10
+ * @deprecated Use renderHTML instead; "escapeHTML" reads as "this escapes HTML"
11
+ * when it actually means the opposite — true renders raw, trusted HTML. Still
12
+ * honored as a fallback for existing callers.
13
+ */
8
14
  escapeHTML?: boolean;
9
15
  }
@@ -215,7 +215,8 @@
215
215
  overflow: hidden;
216
216
  display: flex;
217
217
  flex-direction: column;
218
- color: var(--uniface-concise-table-bg, unset);
218
+ background-color: var(--uniface-concise-table-color-surface);
219
+ color: var(--uniface-concise-table-color);
219
220
  }
220
221
  :global .uniface-concise-datatable .table-container {
221
222
  display: flex;
@@ -237,7 +238,7 @@
237
238
  padding: 2px 4px;
238
239
  }
239
240
  :global .uniface-concise-datatable .table-header {
240
- background-color: #f5f5f5;
241
+ background-color: var(--uniface-concise-table-header-surface);
241
242
  padding: 0;
242
243
  flex-shrink: 0;
243
244
  z-index: 10;
@@ -265,7 +266,7 @@
265
266
  color: var(--uniface-concise-table-row-alter-color, #374151);
266
267
  }
267
268
  :global .uniface-concise-datatable .table-body-container .table-row:hover {
268
- background-color: #f9f9f9;
269
+ background-color: var(--uniface-concise-table-row-hover-bg);
269
270
  }
270
271
  :global .uniface-concise-datatable .table-body-container.auto-scroll {
271
272
  overflow-y: hidden;
@@ -14,9 +14,9 @@
14
14
  }: Props = $props();
15
15
 
16
16
  let text = $derived(column.formatter ? column.formatter(data[column.field]) : (data[column.field] ?? ''));
17
- // Safe by default: HTML rendering is opt-in only (escapeHTML: true), matching the
18
- // semantics of the data_table component's DataColumn#escapeHTML flag.
19
- let renderAsHtml = $derived(column.escapeHTML === true);
17
+ // Safe by default: HTML rendering is opt-in only (renderHTML: true), matching the
18
+ // semantics of the data_table component's DataColumn#renderHTML flag.
19
+ let renderAsHtml = $derived((column.renderHTML ?? column.escapeHTML) === true);
20
20
  </script>
21
21
 
22
22
  <div class="data-cell col_{idx}" role="cell">
@@ -1,5 +1,5 @@
1
1
  <script lang="ts">
2
- import {tick} from "svelte";
2
+ import {tick, onDestroy, untrack} from "svelte";
3
3
  import SearchBox from "../../form/search_box"
4
4
  import type {FunFilter} from "../../types";
5
5
  import type {ListLazyLoader} from "../../types";
@@ -22,6 +22,10 @@
22
22
  selectedList?: Array<any>;
23
23
  class?: string;
24
24
  item$props?: any;
25
+ onchange?: OnSelectChange;
26
+ /**
27
+ * @deprecated 请使用 onchange;仍受支持以兼容旧调用。
28
+ */
25
29
  onSelectChange?: OnSelectChange;
26
30
  onItemDblClick?: OnItemDblClick;
27
31
  onItemClick?: OnItemClick;
@@ -45,6 +49,7 @@
45
49
  selectedList = $bindable([]),
46
50
  class: className,
47
51
  item$props,
52
+ onchange,
48
53
  onSelectChange,
49
54
  onItemDblClick,
50
55
  onItemClick,
@@ -83,12 +88,19 @@
83
88
  localFilterText = filterText ?? '';
84
89
  });
85
90
 
91
+ // onchange is the canonical name; onSelectChange is kept as a deprecated
92
+ // alias and fired alongside it for backward compatibility.
93
+ const notifySelectChange = (item: any) => {
94
+ onchange?.(item);
95
+ onSelectChange?.(item);
96
+ };
97
+
86
98
  const handleItemClick = (item: any) => (e: MouseEvent) => {
87
99
  if (readonly == false) {
88
100
  if (selectMode == 'single') {
89
101
  if (localSelectedItem != item) {
90
102
  localSelectedItem = item;
91
- onSelectChange?.(item);
103
+ notifySelectChange(item);
92
104
  }
93
105
  } else {
94
106
  onItemClick?.(item);
@@ -102,6 +114,13 @@
102
114
  }
103
115
  }
104
116
 
117
+ const handleItemKeyDown = (item: any) => (e: KeyboardEvent) => {
118
+ if (e.key === 'Enter' || e.key === ' ') {
119
+ e.preventDefault();
120
+ handleItemClick(item)(e as unknown as MouseEvent);
121
+ }
122
+ }
123
+
105
124
  const doFilter = () => {
106
125
  const newList: Array<any> = [];
107
126
  list?.forEach(item => {
@@ -115,14 +134,23 @@
115
134
  filteredList = newList;
116
135
  if (selectMode == 'single') {
117
136
  localSelectedItem = null;
118
- onSelectChange?.(null);
137
+ notifySelectChange(null);
119
138
  }
120
139
  }
121
140
 
141
+ // Guards against out-of-order responses: only the most recently issued
142
+ // request is allowed to write its result into component state.
143
+ let lazyLoadSeq = 0;
144
+ let destroyed = false;
145
+
122
146
  const lazyLoad = async (clean: boolean = true) => {
147
+ const seq = ++lazyLoadSeq;
123
148
  isBusy = true;
124
149
  try {
125
150
  let result = await lazyLoader?.(localFilterText, pageNo);
151
+ if (destroyed || seq !== lazyLoadSeq) {
152
+ return;
153
+ }
126
154
  if (result) {
127
155
  if (clean) {
128
156
  list = result.list;
@@ -133,8 +161,17 @@
133
161
  filteredList = list ?? [];
134
162
  hasMore = result.hasMore;
135
163
  }
164
+ } catch (e) {
165
+ if (!clean) {
166
+ // Failed page-2+ fetch: undo the optimistic pageNo increment so a
167
+ // retry (e.g. re-triggered by scroll) re-requests the same page.
168
+ pageNo--;
169
+ }
170
+ throw e;
136
171
  } finally {
137
- isBusy = false;
172
+ if (!destroyed && seq === lazyLoadSeq) {
173
+ isBusy = false;
174
+ }
138
175
  }
139
176
  }
140
177
 
@@ -148,12 +185,26 @@
148
185
  interval = null;
149
186
  pageNo = 1;
150
187
  scrollElement.scrollTop = 0;
151
- await lazyLoad();
152
- await tick();
153
- searchBox.focus();
188
+ try {
189
+ await lazyLoad();
190
+ } catch (e) {
191
+ console.error('ListBox: failed to reload items', e);
192
+ }
193
+ if (!destroyed) {
194
+ await tick();
195
+ searchBox?.focus();
196
+ }
154
197
  }
155
198
  }
156
199
 
200
+ onDestroy(() => {
201
+ destroyed = true;
202
+ if (interval != null) {
203
+ clearInterval(interval);
204
+ interval = null;
205
+ }
206
+ });
207
+
157
208
  const handleCriteriaChange = () => {
158
209
  if (lazyLoader != null) {
159
210
  if (interval == null) {
@@ -192,15 +243,27 @@
192
243
  }
193
244
  }
194
245
 
246
+ // untrack: lazyLoad() reads pageNo/isBusy/localFilterText synchronously
247
+ // before its first await. Without untrack, those reads register as
248
+ // dependencies of *this* effect, so a later change to pageNo (e.g. from
249
+ // fetchMore) would re-run this effect and fire a second, unrelated
250
+ // "initial load" — this must only re-run when the lazyLoader prop itself
251
+ // changes.
195
252
  $effect(() => {
196
253
  if (lazyLoader != null) {
197
- lazyLoad();
254
+ untrack(() => {
255
+ lazyLoad().catch((e) => console.error('ListBox: failed to load items', e));
256
+ });
198
257
  }
199
258
  });
200
259
 
201
260
  const fetchMore = async () => {
202
261
  pageNo++;
203
- await lazyLoad(false);
262
+ try {
263
+ await lazyLoad(false);
264
+ } catch (e) {
265
+ console.error('ListBox: failed to load more items', e);
266
+ }
204
267
  }
205
268
 
206
269
  const handleScroll = () => {
@@ -232,20 +295,25 @@
232
295
  onchange={handleCriteriaChange}/>
233
296
  </div>
234
297
  {/if}
235
- <div class="listbox-content" class:selectable={selectMode==='single'} bind:this={scrollElement} onscroll={handleScroll}>
298
+ <div class="listbox-content" class:selectable={selectMode==='single'} bind:this={scrollElement} onscroll={handleScroll}
299
+ role="listbox" aria-multiselectable={selectMode === 'multiple' ? true : undefined}>
236
300
  {#if filteredList && ItemRender}
237
301
  {#each filteredList as item}
238
302
  {#if selectMode === "multiple"}
239
- <div class="listview-item" style="display: flex; flex-direction: row; align-items: center; gap: 8px">
303
+ <div class="listview-item" style="display: flex; flex-direction: row; align-items: center; gap: 8px"
304
+ role="option" aria-selected={localSelectedList.indexOf(item) > -1}>
240
305
  <CheckBox style="flex: 0 0 auto; margin-left: 8px" {readonly} value={localSelectedList.indexOf(item) > -1}
241
306
  onchange={handleSelectionChanged(item)}/>
242
- <div style="flex: 1 1 auto; cursor: pointer" aria-hidden="true" onclick={handleSelectionChanged(item)}>
307
+ <div style="flex: 1 1 auto; cursor: pointer" role="button" tabindex="0"
308
+ onclick={handleSelectionChanged(item)}
309
+ onkeydown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); handleSelectionChanged(item)(); } }}>
243
310
  <ItemRender {readonly} {item} {...item$props}/>
244
311
  </div>
245
312
  </div>
246
313
  {:else }
247
- <div class="listview-item" class:selected={item===localSelectedItem} aria-hidden="true"
248
- ondblclick={handleItemDblClick(item)} onclick={handleItemClick(item)}>
314
+ <div class="listview-item" class:selected={item===localSelectedItem}
315
+ role="option" aria-selected={item===localSelectedItem} tabindex="0"
316
+ ondblclick={handleItemDblClick(item)} onclick={handleItemClick(item)} onkeydown={handleItemKeyDown(item)}>
249
317
  <ItemRender {item} {readonly} selected={item===localSelectedItem}
250
318
  {...item$props}/>
251
319
  </div>
@@ -279,6 +347,8 @@
279
347
  box-sizing: border-box;
280
348
  user-select: none;
281
349
  border: 1px solid var(--uniface-list-box-border-color);
350
+ background-color: var(--uniface-list-box-color-surface);
351
+ color: var(--uniface-list-box-color);
282
352
  }
283
353
 
284
354
  .uniface-listbox.round {
@@ -291,7 +361,7 @@
291
361
  position: absolute;
292
362
  top: 0;
293
363
  left: 0;
294
- background-color: #FFFFFF10;
364
+ background-color: var(--uniface-color-overlay-mask-surface);
295
365
  }
296
366
 
297
367
  .box-header {
@@ -350,4 +420,9 @@
350
420
  .listbox-content.selectable .listview-item.selected {
351
421
  background-color: var(--uniface-list-box-selected-surface);
352
422
  }
423
+
424
+ .listbox-content .listview-item:focus-visible {
425
+ outline: 2px solid var(--uniface-primary-border-color, #298DFF);
426
+ outline-offset: -2px;
427
+ }
353
428
  </style>
@@ -15,6 +15,10 @@ interface Props {
15
15
  selectedList?: Array<any>;
16
16
  class?: string;
17
17
  item$props?: any;
18
+ onchange?: OnSelectChange;
19
+ /**
20
+ * @deprecated 请使用 onchange;仍受支持以兼容旧调用。
21
+ */
18
22
  onSelectChange?: OnSelectChange;
19
23
  onItemDblClick?: OnItemDblClick;
20
24
  onItemClick?: OnItemClick;
@@ -68,25 +68,41 @@
68
68
  await onNodeSelectionChange(node);
69
69
  }
70
70
 
71
+ const handleRowKeyDown = async (e: KeyboardEvent) => {
72
+ if (e.key === 'Enter' || e.key === ' ') {
73
+ e.preventDefault();
74
+ await handleRowClick();
75
+ }
76
+ }
77
+
78
+ const handleIconKeyDown = async (e: KeyboardEvent) => {
79
+ if (e.key === 'Enter' || e.key === ' ') {
80
+ e.preventDefault();
81
+ e.stopPropagation();
82
+ await onToggle();
83
+ }
84
+ }
85
+
71
86
  const handleContextMenu = (event: MouseEvent) => {
72
87
  onContextMenu?.(event, node);
73
88
  }
74
89
  </script>
75
90
 
76
91
  <div class="tree-node" class:expanded style="padding-left: {node.level * 16}px">
77
- <div class="node-item" class:selected={isSelected} onclick={handleRowClick} aria-hidden="true"
78
- oncontextmenu={handleContextMenu}>
92
+ <div class="node-item" class:selected={isSelected} onclick={handleRowClick} onkeydown={handleRowKeyDown}
93
+ oncontextmenu={handleContextMenu} role="treeitem" tabindex="0" aria-selected={isSelected}
94
+ aria-expanded={isBranch ? expanded : undefined}>
79
95
 
80
96
  <div class="item-chevron">
81
97
  {#if isBranch}
82
98
  {#if loading}
83
99
  <img alt="loading" class="chevron-img" src={iconLoading}/>
84
100
  {:else if chevronIcon}
85
- <span aria-hidden="true" onclick={handleIconClick} class="chevron-btn">
101
+ <span role="button" tabindex="0" aria-label={expanded ? 'Collapse' : 'Expand'} onclick={handleIconClick} onkeydown={handleIconKeyDown} class="chevron-btn">
86
102
  {@render chevronIcon({ expanded, loading })}
87
103
  </span>
88
104
  {:else}
89
- <span aria-hidden="true" onclick={handleIconClick} class="chevron-btn default-arrow">
105
+ <span role="button" tabindex="0" aria-label={expanded ? 'Collapse' : 'Expand'} onclick={handleIconClick} onkeydown={handleIconKeyDown} class="chevron-btn default-arrow">
90
106
  {expanded ? '▾' : '▸'}
91
107
  </span>
92
108
  {/if}
@@ -202,6 +218,11 @@
202
218
  color: var(--uniface-treeview-item-selected-color, #0061FF);
203
219
  font-weight: 600;
204
220
  }
221
+
222
+ &:focus-visible {
223
+ outline: 2px solid var(--uniface-primary-border-color, #298DFF);
224
+ outline-offset: -2px;
225
+ }
205
226
  }
206
227
  }
207
228
  </style>
@@ -1,4 +1,4 @@
1
- <script lang="ts">
1
+ <script lang="ts" generics="T = any">
2
2
  import ContentPanel from "./parts/ContentPanel.svelte";
3
3
  import type DataColumn from "./lib/DataColumn";
4
4
  import type ActionsColumn from "./lib/ActionsColumn";
@@ -14,17 +14,17 @@
14
14
 
15
15
  interface Props {
16
16
  columns: Array<DataColumn>;
17
- actionsColumn?: ActionsColumn | null;
17
+ actionsColumn?: ActionsColumn<T> | null;
18
18
  indicatorColumn?: IndicatorColumn | null;
19
19
  rowHeight?: number;
20
20
  headerHeight?: number;
21
- list: Array<any>;
22
- selectedRows?: Array<any>;
21
+ list: Array<T>;
22
+ selectedRows?: Array<T>;
23
23
  inlineRowHeight?: number;
24
24
  rowBorder?: boolean;
25
25
  colBorder?: boolean;
26
26
  emptyIndicator?: string;
27
- getRowFontStyle?: GetRowFontStyle;
27
+ getRowFontStyle?: GetRowFontStyle<T>;
28
28
  style?: string;
29
29
  }
30
30
 
@@ -2,21 +2,41 @@ import type DataColumn from "./lib/DataColumn";
2
2
  import type ActionsColumn from "./lib/ActionsColumn";
3
3
  import type IndicatorColumn from "./lib/IndicatorColumn";
4
4
  import type { GetRowFontStyle } from "../types";
5
- interface Props {
6
- columns: Array<DataColumn>;
7
- actionsColumn?: ActionsColumn | null;
8
- indicatorColumn?: IndicatorColumn | null;
9
- rowHeight?: number;
10
- headerHeight?: number;
11
- list: Array<any>;
12
- selectedRows?: Array<any>;
13
- inlineRowHeight?: number;
14
- rowBorder?: boolean;
15
- colBorder?: boolean;
16
- emptyIndicator?: string;
17
- getRowFontStyle?: GetRowFontStyle;
18
- style?: string;
5
+ declare function $$render<T = any>(): {
6
+ props: {
7
+ columns: Array<DataColumn>;
8
+ actionsColumn?: ActionsColumn<T> | null;
9
+ indicatorColumn?: IndicatorColumn | null;
10
+ rowHeight?: number;
11
+ headerHeight?: number;
12
+ list: Array<T>;
13
+ selectedRows?: Array<T>;
14
+ inlineRowHeight?: number;
15
+ rowBorder?: boolean;
16
+ colBorder?: boolean;
17
+ emptyIndicator?: string;
18
+ getRowFontStyle?: GetRowFontStyle<T>;
19
+ style?: string;
20
+ };
21
+ exports: {};
22
+ bindings: "inlineRowHeight" | "selectedRows";
23
+ slots: {};
24
+ events: {};
25
+ };
26
+ declare class __sveltets_Render<T = any> {
27
+ props(): ReturnType<typeof $$render<T>>['props'];
28
+ events(): ReturnType<typeof $$render<T>>['events'];
29
+ slots(): ReturnType<typeof $$render<T>>['slots'];
30
+ bindings(): "inlineRowHeight" | "selectedRows";
31
+ exports(): {};
19
32
  }
20
- declare const DataTable: import("svelte").Component<Props, {}, "inlineRowHeight" | "selectedRows">;
21
- type DataTable = ReturnType<typeof DataTable>;
33
+ interface $$IsomorphicComponent {
34
+ new <T = any>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> & {
35
+ $$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
36
+ } & ReturnType<__sveltets_Render<T>['exports']>;
37
+ <T = any>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
38
+ z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
39
+ }
40
+ declare const DataTable: $$IsomorphicComponent;
41
+ type DataTable<T = any> = InstanceType<typeof DataTable<T>>;
22
42
  export default DataTable;
@@ -1,5 +1,5 @@
1
1
  import type { GetRowActions } from "./RowAction";
2
- export default interface ActionsColumn {
2
+ export default interface ActionsColumn<T = any> {
3
3
  /**
4
4
  * 宽度
5
5
  */
@@ -7,7 +7,7 @@ export default interface ActionsColumn {
7
7
  /**
8
8
  *
9
9
  */
10
- getActions: GetRowActions;
10
+ getActions: GetRowActions<T>;
11
11
  /**
12
12
  * 对齐方式,默认靠左对齐
13
13
  */
@@ -33,7 +33,13 @@ export default interface DataColumn {
33
33
  */
34
34
  formatter?: FormatCell;
35
35
  /**
36
- * 是否忽略html的转义符
36
+ * 是否将单元格内容作为可信 HTML 渲染({@html}),而非纯文本转义。
37
+ * 仅当 field/formatter 输出为可信标记时才应设为 true。
38
+ */
39
+ renderHTML?: boolean;
40
+ /**
41
+ * @deprecated 请使用 renderHTML;名称容易被误读为"转义 HTML",
42
+ * 实际含义相反——设为 true 会把内容作为可信 HTML 渲染。仍受支持以兼容旧调用。
37
43
  */
38
44
  escapeHTML?: boolean;
39
45
  /**
@@ -1,11 +1,11 @@
1
1
  /**
2
- *
2
+ * @template T Row data type. Defaults to `any` so existing untyped usage keeps compiling.
3
3
  */
4
- export type ActionFunction = (item: any) => void;
4
+ export type ActionFunction<T = any> = (item: T) => void;
5
5
  /**
6
6
  *
7
7
  */
8
- export default interface RowAction {
8
+ export default interface RowAction<T = any> {
9
9
  /**
10
10
  *
11
11
  */
@@ -21,9 +21,9 @@ export default interface RowAction {
21
21
  /**
22
22
  *
23
23
  */
24
- callback: ActionFunction;
24
+ callback: ActionFunction<T>;
25
25
  }
26
26
  /**
27
27
  *
28
28
  */
29
- export type GetRowActions = (item: any) => Array<RowAction>;
29
+ export type GetRowActions<T = any> = (item: T) => Array<RowAction<T>>;
@@ -60,7 +60,6 @@
60
60
  let startWidth = $state(0);
61
61
 
62
62
  const handleResizeMouseDown = (col: DataColumn) => (event: MouseEvent) => {
63
- console.log("handleResizeMouseDown", col, event);
64
63
  if (col.resizable) {
65
64
  resizeCol = col;
66
65
  startX = event.clientX;
@@ -89,6 +88,13 @@
89
88
  window.removeEventListener('mousemove', onMouseMove);
90
89
  window.removeEventListener('mouseup', onMouseUp);
91
90
  }
91
+
92
+ const handleHeaderKeyDown = (column: DataColumn) => (e: KeyboardEvent) => {
93
+ if (column.compareFunction != null && (e.key === 'Enter' || e.key === ' ')) {
94
+ e.preventDefault();
95
+ handleCellClick?.(column);
96
+ }
97
+ };
92
98
  </script>
93
99
 
94
100
  <div class="header-row" style="height: {headerHeight}px">
@@ -110,10 +116,18 @@
110
116
  {/if}
111
117
  {#each columns as column, index (index)}
112
118
  <div class="fz_col-{index} data-cell" class:sortable={column.compareFunction != null} style="text-align: center">
119
+ <!-- role/tabindex are only ever both set together (sortable columns);
120
+ svelte-check can't verify that pairing statically across the ternary. -->
121
+ <!-- svelte-ignore a11y_no_noninteractive_tabindex -->
113
122
  <div
114
123
  class:sortable={column.compareFunction != null}
115
124
  onclick={() => handleCellClick?.(column)}
116
- aria-hidden="true"
125
+ onkeydown={handleHeaderKeyDown(column)}
126
+ role={column.compareFunction != null ? 'button' : undefined}
127
+ tabindex={column.compareFunction != null ? 0 : undefined}
128
+ aria-sort={column.field === orderColumn?.field
129
+ ? (orderDirection === OrderDirection.Ascending ? 'ascending' : 'descending')
130
+ : undefined}
117
131
  class="vertical-center"
118
132
  >
119
133
  <span>{column.text}</span>
@@ -129,7 +143,8 @@
129
143
  <div
130
144
  class="header-cell-divider"
131
145
  class:resizer={column.resizable}
132
- aria-hidden="true"
146
+ role={column.resizable ? 'separator' : undefined}
147
+ aria-orientation={column.resizable ? 'vertical' : undefined}
133
148
  onmousedown={handleResizeMouseDown(column)}
134
149
  ></div>
135
150
  </div>
@@ -45,6 +45,14 @@
45
45
  e.stopPropagation();
46
46
  };
47
47
 
48
+ const handleExpandKeyDown = (e: KeyboardEvent) => {
49
+ if (e.key === 'Enter' || e.key === ' ') {
50
+ e.preventDefault();
51
+ e.stopPropagation();
52
+ handleRowExpand?.(row);
53
+ }
54
+ };
55
+
48
56
  const handleSelectChange = (e: Event) => {
49
57
  const checked = (e.target as HTMLInputElement).checked;
50
58
  handleRowSelectChange?.(rowIndex, checked);
@@ -66,8 +74,9 @@
66
74
  <div class="expand-icon" style="flex: 0 0 auto">
67
75
  <i
68
76
  class={row == expandRow ? 'icon_google_folder' : 'icon_google_folder_open'}
69
- aria-hidden="true"
77
+ role="button" tabindex="0" aria-label={row == expandRow ? 'Collapse row' : 'Expand row'}
70
78
  onclick={showInlineData}
79
+ onkeydown={handleExpandKeyDown}
71
80
  ></i>
72
81
  </div>
73
82
  {/if}
@@ -20,7 +20,7 @@
20
20
  {#if typeof item == 'string'}
21
21
  <span>{item}</span>
22
22
  {:else}
23
- <button class="link-style" onclick={(e) => { e.preventDefault(); item.action(); }}>{item.text}</button>
23
+ <button type="button" class="link-style" onclick={(e) => { e.preventDefault(); item.action(); }}>{item.text}</button>
24
24
  {/if}
25
25
  {/each}
26
26
  </div>