@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
@@ -1,5 +1,5 @@
1
1
  <script lang="ts">
2
- import {getContext, hasContext} from "svelte";
2
+ import {getContext, hasContext, onMount} from "svelte";
3
3
  import type {DialogCloseConfirm} from "./DialogCloseConfirm";
4
4
  import ActionBar from "../../layouts/action_bar";
5
5
  import type {ButtonAction, ButtonActions} from "../../types";
@@ -7,6 +7,9 @@
7
7
  import {ModalResult, type OnClose} from "./IDialog";
8
8
  import i18nRes from "../../i18nRes";
9
9
  import type {Snippet} from "svelte";
10
+ import {acquireScrollLock} from "../common/scrollLock";
11
+ import {registerEscapeHandler} from "../common/escapeClose";
12
+ import {focusTrap} from "../common/focusTrap";
10
13
 
11
14
  interface Props {
12
15
  title: string;
@@ -42,16 +45,6 @@
42
45
  try { return hasContext('dialogId') ? getContext<string | undefined>('dialogId') : undefined; } catch { return undefined; }
43
46
  })();
44
47
 
45
- // 自动聚焦第一个按钮(次轮 tick,确保子组件已挂载)
46
- $effect(() => {
47
- if (!dialogId) return; // 直接渲染模式由调用方控制
48
- setTimeout(() => {
49
- const root = document.querySelector('.uniface-dialog[data-dialog-id="' + dialogId + '"]');
50
- const firstButton = root?.querySelector('button');
51
- firstButton?.focus();
52
- }, 300);
53
- });
54
-
55
48
  export const close = async (result: ModalResult) => {
56
49
  const isCancelLike = result === ModalResult.MR_CANCEL || result === ModalResult.MR_CLOSE;
57
50
  const confirm = closeConfirm && isCancelLike ? await closeConfirm() : true;
@@ -65,15 +58,22 @@
65
58
  }
66
59
  };
67
60
 
68
- const handleWindowKeyDown = (e: KeyboardEvent) => {
69
- if (e.code !== 'Escape') return;
70
- // 如果有 picker popover 打开,先让 CommonPicker ESC 处理(关闭 popover)。
71
- // 此时 DOM 里的 popover 还没被 Svelte 移除(响应式更新在微任务里),能检测到。
72
- const openPopover = document.querySelector('.uniface-popover-wrapper:not(.hidden)');
73
- if (openPopover) return;
74
- e.preventDefault();
75
- void close(ModalResult.MR_CANCEL);
76
- };
61
+ // Dialog is modal the page behind it should not scroll (regression fix:
62
+ // this was previously entirely missing, unlike Popover). Escape now goes
63
+ // through the shared, stack-aware registry instead of a hand-rolled
64
+ // ".uniface-popover-wrapper:not(.hidden)" DOM query a Popover opened
65
+ // from within this dialog (e.g. a picker dropdown) registers later and so
66
+ // is topmost, closing first, with no special-casing needed here.
67
+ onMount(() => {
68
+ const releaseScrollLock = acquireScrollLock();
69
+ const unregisterEscape = registerEscapeHandler(() => {
70
+ void close(ModalResult.MR_CANCEL);
71
+ });
72
+ return () => {
73
+ releaseScrollLock();
74
+ unregisterEscape();
75
+ };
76
+ });
77
77
 
78
78
  const closeAction = $derived<ButtonAction>({
79
79
  label: closeButton,
@@ -128,18 +128,23 @@
128
128
  );
129
129
  </script>
130
130
 
131
- <svelte:window onkeydown={handleWindowKeyDown} onmousemove={handleMouseMove} onmouseup={handleMouseUp}/>
131
+ <svelte:window onmousemove={handleMouseMove} onmouseup={handleMouseUp}/>
132
132
 
133
133
  <div class="uniface-dialog" data-dialog-id={dialogId}
134
134
  style="{startMove ? 'cursor: move' : ''}">
135
- <div class="uniface-dialog-box"
135
+ <div class="uniface-dialog-box" use:focusTrap={{enabled: !!dialogId}}
136
136
  style="{dialog$style}; top: {top}px; left: {left}px; width: {width}; height: {height};">
137
- <div class="title-bar" onmousedown={handleMouseDown} onmouseup={handleMouseUp} aria-hidden="true">
137
+ <!-- Drag handle for repositioning the dialog, not a semantic control; the
138
+ close action inside it is its own focusable <button>. -->
139
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
140
+ <div class="title-bar" onmousedown={handleMouseDown} onmouseup={handleMouseUp}>
138
141
  <div style="flex: 1 1 auto"><span>{title ?? 'New window'}</span></div>
139
142
  {#if !hideClose}
140
143
  <div style="flex: 0 0 auto; padding-left: 12px">
141
- <i class="icon_google_clear dialog-action-button" aria-hidden="true"
142
- onclick={() => close(ModalResult.MR_CANCEL)}></i>
144
+ <button type="button" class="dialog-action-button" aria-label="Close"
145
+ onclick={() => close(ModalResult.MR_CANCEL)}>
146
+ <i class="icon_google_clear" aria-hidden="true"></i>
147
+ </button>
143
148
  </div>
144
149
  {/if}
145
150
  </div>
@@ -152,8 +157,7 @@
152
157
  </div>
153
158
  </div>
154
159
 
155
- <style>@charset "UTF-8";
156
- :global(:root) {
160
+ <style>:global(:root) {
157
161
  --uniface-dialog-border-color: #0000000D;
158
162
  --uniface-dialog-action-button-hover-background-color: #E2E8F0;
159
163
  --uniface-dialog-action-button-color: #6B7280;
@@ -169,8 +173,10 @@
169
173
  align-items: center;
170
174
  justify-content: center;
171
175
  background-color: var(--uniface-color-dialog-mask-surface, rgba(255, 255, 255, 0.1254901961));
172
- /* z-index 必须低于 Popover 的 100,picker 下拉才能浮在 dialog 之上 */
173
- z-index: 20;
176
+ /* Must stay below --uniface-z-popover so a picker dropdown opened inside
177
+ a dialog floats above the dialog itself. See uniface-root.scss for the
178
+ full stacking scale. */
179
+ z-index: var(--uniface-z-dialog, 20);
174
180
  user-select: none;
175
181
  }
176
182
  .uniface-dialog .uniface-dialog-box {
@@ -202,6 +208,13 @@
202
208
  .uniface-dialog .uniface-dialog-box .title-bar .dialog-action-button {
203
209
  cursor: pointer;
204
210
  color: var(--uniface-dialog-action-button-color, #B3BCCA);
211
+ background: none;
212
+ border: none;
213
+ padding: 4px;
214
+ display: inline-flex;
215
+ align-items: center;
216
+ justify-content: center;
217
+ font: inherit;
205
218
  }
206
219
  .uniface-dialog .uniface-dialog-box .title-bar .dialog-action-button:hover {
207
220
  background-color: var(--uniface-dialog-action-button-hover-background-color, #E2E8F0);
@@ -211,6 +224,10 @@
211
224
  position: relative;
212
225
  top: 1px;
213
226
  }
227
+ .uniface-dialog .uniface-dialog-box .title-bar .dialog-action-button:focus-visible {
228
+ outline: 2px solid var(--uniface-primary-border-color, #298DFF);
229
+ outline-offset: 1px;
230
+ }
214
231
  .uniface-dialog .uniface-dialog-box .dialog-content {
215
232
  flex: 1 1 auto;
216
233
  overflow: auto;
@@ -1,6 +1,6 @@
1
- import { mount } from 'svelte';
2
1
  import DialogBoard from './DialogBoard.svelte';
3
2
  import { ModalResult } from './IDialog';
3
+ import { mountToBody } from '../common/mountSingleton';
4
4
  let boardMounted = false;
5
5
  /**
6
6
  * 内部使用:DialogBoard 在组件初始化时检查并标记,确保全局只有一个 board 实际渲染 stack。
@@ -15,14 +15,12 @@ export function _markBoardMounted() {
15
15
  function ensureMounted() {
16
16
  if (boardMounted)
17
17
  return;
18
- if (typeof window !== 'undefined' && typeof document !== 'undefined' && document.body) {
19
- try {
20
- mount(DialogBoard, { target: document.body });
21
- }
22
- catch {
23
- // Silent fallback
24
- }
25
- }
18
+ // Note: unlike the other overlay controllers, this deliberately does not
19
+ // set boardMounted here — DialogBoard.svelte itself calls
20
+ // _markBoardMounted() on init, since a board the consumer placed manually
21
+ // in their app root also needs to mark it mounted, not just this
22
+ // auto-mount path.
23
+ mountToBody(DialogBoard);
26
24
  }
27
25
  class DialogManager {
28
26
  stack = $state([]);
@@ -1,6 +1,8 @@
1
1
  <script lang="ts">
2
2
  import {fly} from "svelte/transition";
3
3
  import type { Snippet } from "svelte";
4
+ import {acquireScrollLock} from "../common/scrollLock";
5
+ import {registerEscapeHandler} from "../common/escapeClose";
4
6
 
5
7
  interface Props {
6
8
  theme?: string;
@@ -26,6 +28,24 @@
26
28
  }
27
29
  });
28
30
 
31
+ // Drawer stays mounted and toggles `visible` repeatedly (unlike Popover/
32
+ // Dialog, which are conditionally rendered fresh each open), so the lock
33
+ // is acquired/released per visibility flip via this effect's cleanup,
34
+ // not once in onMount. Regression: this was previously entirely missing.
35
+ $effect(() => {
36
+ if (visible) {
37
+ return acquireScrollLock();
38
+ }
39
+ });
40
+
41
+ // Same per-visibility-flip lifecycle as the scroll lock above. Regression:
42
+ // previously had no Escape handling at all.
43
+ $effect(() => {
44
+ if (visible) {
45
+ return registerEscapeHandler(() => { visible = false; });
46
+ }
47
+ });
48
+
29
49
  let xPos = $derived(position == 'left' ? `${-width}` : '100%');
30
50
 
31
51
  const handleBackdropClick = () => {
@@ -37,9 +57,16 @@
37
57
  };
38
58
  </script>
39
59
 
40
- <div class="uniface-drawer" aria-hidden="true" data-theme={theme} style="display: {hidden ? 'none' : 'block'}" onclick={handleBackdropClick}>
60
+ <!-- Backdrop click-to-close convenience only; aria-hidden must not be used
61
+ here since it would hide the real, arbitrary interactive content
62
+ rendered via `children` from assistive tech. -->
63
+ <!-- svelte-ignore a11y_click_events_have_key_events -->
64
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
65
+ <div class="uniface-drawer" data-theme={theme} style="display: {hidden ? 'none' : 'block'}" onclick={handleBackdropClick}>
41
66
  {#if visible}
42
- <div class={position} style="width: {width}px" transition:fly={{ x: xPos, duration: 1000, opacity: 0.5 }} aria-hidden="true"
67
+ <!-- svelte-ignore a11y_click_events_have_key_events -->
68
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
69
+ <div class={position} style="width: {width}px" transition:fly={{ x: xPos, duration: 1000, opacity: 0.5 }}
43
70
  onoutroend={()=>{hidden = !visible}} onclick={handleDrawerClick}>
44
71
  <div>
45
72
  {#if children}
@@ -58,7 +85,7 @@
58
85
  left: 0;
59
86
  right: 0;
60
87
  background-color: transparent;
61
- z-index: 99;
88
+ z-index: var(--uniface-z-drawer, 99);
62
89
  }
63
90
 
64
91
  .uniface-drawer > div {
@@ -1,23 +1,10 @@
1
- import { mount } from 'svelte';
2
1
  import IndicatorPanel from './IndicatorPanel.svelte';
3
- let panelMounted = false;
2
+ import { createSingletonMounter } from '../common/mountSingleton';
4
3
  /**
5
4
  * 懒挂载:第一次 show() 时把面板挂到 document.body,后续复用同一个实例。
6
5
  * 不需要用户在 app 根节点手动挂 <IndicatorBoard />。
7
6
  */
8
- function ensureMounted() {
9
- if (panelMounted)
10
- return;
11
- if (typeof window !== 'undefined' && typeof document !== 'undefined' && document.body) {
12
- try {
13
- mount(IndicatorPanel, { target: document.body });
14
- panelMounted = true;
15
- }
16
- catch {
17
- // Silent fallback
18
- }
19
- }
20
- }
7
+ const ensureMounted = createSingletonMounter(IndicatorPanel);
21
8
  class IndicatorController {
22
9
  message = $state('');
23
10
  visible = $state(false);
@@ -20,7 +20,10 @@
20
20
  display: flex;
21
21
  flex-direction: column;
22
22
  justify-content: center;
23
- z-index: 100;
23
+ /* Was a bare 100, accidentally tied with Popover's z-index; centralized
24
+ scale bumps this to 110 so an Indicator shown while a Popover is
25
+ open reliably sits above it. */
26
+ z-index: var(--uniface-z-indicator, 110);
24
27
  }
25
28
  .uniface-indicator-board .indicator-content {
26
29
  margin: 0 auto;
@@ -1,22 +1,9 @@
1
- import { mount } from 'svelte';
2
1
  import MessageBoxPanel from './MessageBoxPanel.svelte';
3
2
  import { ModalResult } from './IMessageBox';
4
3
  import { getAppTitle } from '../../unifaceContext';
5
4
  import i18nRes from '../../i18nRes';
6
- let panelMounted = false;
7
- function ensureMounted() {
8
- if (panelMounted)
9
- return;
10
- if (typeof window !== 'undefined' && typeof document !== 'undefined' && document.body) {
11
- try {
12
- mount(MessageBoxPanel, { target: document.body });
13
- panelMounted = true;
14
- }
15
- catch {
16
- // Silent fallback in test environments where Svelte client mount is bypassed
17
- }
18
- }
19
- }
5
+ import { createSingletonMounter } from '../common/mountSingleton';
6
+ const ensureMounted = createSingletonMounter(MessageBoxPanel);
20
7
  class MessageBoxController {
21
8
  visible = $state(false);
22
9
  title = $state('');
@@ -61,12 +61,17 @@
61
61
  <div style={startMove ? 'cursor: move' : ''} onmousemove={handleMouseMove} role="none" class="dialog-inner">
62
62
  <div class="uniface-message-box">
63
63
  <div style="top: {top}px; left: {left}px">
64
- <div class="title-bar" aria-hidden="true" onmousedown={handleMouseDown} onmouseup={handleMouseUp}>
64
+ <!-- Drag handle for repositioning the dialog, not a semantic control; the
65
+ close action inside it is its own focusable <button>. -->
66
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
67
+ <div class="title-bar" onmousedown={handleMouseDown} onmouseup={handleMouseUp}>
65
68
  {#if !utils.isEmpty(messageBox.title)}
66
69
  <span class="title-text">{messageBox.title}</span>
67
70
  {/if}
68
71
  <div>
69
- <i class="icon_google_clear dialog-action-button" aria-hidden="true" onclick={handleCloseClick}></i>
72
+ <button type="button" class="dialog-action-button" aria-label="Close" onclick={handleCloseClick}>
73
+ <i class="icon_google_clear" aria-hidden="true"></i>
74
+ </button>
70
75
  </div>
71
76
  </div>
72
77
  <div class="box-content">
@@ -102,7 +107,7 @@
102
107
  width: 100%;
103
108
  height: 100%;
104
109
  user-select: none;
105
- z-index: 30;
110
+ z-index: var(--uniface-z-message-box, 30);
106
111
  opacity: 0;
107
112
  transition: opacity 0.3s ease, display 0.3s ease allow-discrete, overlay 0.3s ease allow-discrete;
108
113
  }
@@ -200,6 +205,13 @@
200
205
  .uniface-message-dialog .uniface-message-box .title-bar .dialog-action-button {
201
206
  cursor: pointer;
202
207
  color: var(--uniface-message-color-title-bar);
208
+ background: none;
209
+ border: none;
210
+ padding: 4px;
211
+ display: inline-flex;
212
+ align-items: center;
213
+ justify-content: center;
214
+ font: inherit;
203
215
  }
204
216
  .uniface-message-dialog .uniface-message-box .title-bar .dialog-action-button:hover {
205
217
  background-color: var(--uniface-dialog-action-button-color-hover-surface);
@@ -209,6 +221,10 @@
209
221
  position: relative;
210
222
  top: 1px;
211
223
  }
224
+ .uniface-message-dialog .uniface-message-box .title-bar .dialog-action-button:focus-visible {
225
+ outline: 2px solid var(--uniface-primary-border-color, #298DFF);
226
+ outline-offset: 1px;
227
+ }
212
228
  .uniface-message-dialog .box-content {
213
229
  flex: 1 1 auto;
214
230
  overflow: auto;
@@ -1,23 +1,10 @@
1
- import { mount } from 'svelte';
2
1
  import ToastPanel from './ToastPanel.svelte';
3
- let panelMounted = false;
2
+ import { createSingletonMounter } from '../common/mountSingleton';
4
3
  /**
5
4
  * 懒挂载:第一次 show() 时把面板挂到 document.body,后续复用同一个实例。
6
5
  * 不需要用户在 app 根节点手动挂 <ToastBoard />。
7
6
  */
8
- function ensureMounted() {
9
- if (panelMounted)
10
- return;
11
- if (typeof window !== 'undefined' && typeof document !== 'undefined' && document.body) {
12
- try {
13
- mount(ToastPanel, { target: document.body });
14
- panelMounted = true;
15
- }
16
- catch {
17
- // Silent fallback in test environments
18
- }
19
- }
20
- }
7
+ const ensureMounted = createSingletonMounter(ToastPanel);
21
8
  class ToastController {
22
9
  message = $state('');
23
10
  type = $state('info');
@@ -35,7 +35,7 @@
35
35
  background-color: var(--uniface-color-overlay-mask-surface);
36
36
  display: block;
37
37
  text-align: center;
38
- z-index: 40;
38
+ z-index: var(--uniface-z-toast, 40);
39
39
  color: var(--uniface-toast-color);
40
40
  }
41
41
  .uniface-toast-board .error {
@@ -129,7 +129,7 @@
129
129
  {#if state.visible}
130
130
  <div
131
131
  class="uniface-tooltip"
132
- style="position: fixed; left: {state.x}px; top: {state.y}px; z-index: 9999; pointer-events: none; transform: {state.transform}; max-width: 50vw;"
132
+ style="position: fixed; left: {state.x}px; top: {state.y}px; z-index: var(--uniface-z-tooltip, 9999); pointer-events: none; transform: {state.transform}; max-width: 50vw;"
133
133
  >
134
134
  {@html state.html}
135
135
  </div>
@@ -1,5 +1,23 @@
1
1
  @charset "UTF-8";
2
2
  :root {
3
+ /* === Overlay z-index scale ===
4
+ A single source of truth for stacking order across every overlay
5
+ surface. Previously each component hardcoded its own literal with no
6
+ documented relationship to the others — Popover and Indicator both used
7
+ 100, and ContextMenu/FloatingPanel both used 1000, by accident rather
8
+ than by design. Consumers can override any layer; the relative order
9
+ below is what actually matters; the absolute numbers only need enough
10
+ headroom between layers for app code to slot things in between. */
11
+ --uniface-z-dialog: 20;
12
+ --uniface-z-message-box: 30;
13
+ --uniface-z-toast: 40;
14
+ --uniface-z-drawer: 99;
15
+ --uniface-z-popover: 100;
16
+ --uniface-z-indicator: 110;
17
+ --uniface-z-context-menu: 1000;
18
+ --uniface-z-floating-panel: 1010;
19
+ --uniface-z-modal-overlay: 9998;
20
+ --uniface-z-tooltip: 9999;
3
21
  /* === Color Tokens === */
4
22
  --color-primary: #0061FF;
5
23
  --color-secondary: #22BDFF;
@@ -158,7 +176,11 @@
158
176
  --uniface-card-footer-height: 40px;
159
177
  --uniface-card-color-surface: #ffffff;
160
178
  --uniface-card-color: #111827;
161
- --uniface-card-color-muted: #4b5563;
179
+ /* Card.svelte (and the component's own README/README_CN) reference
180
+ --uniface-card-text-muted; this used to be named --uniface-card-color-muted
181
+ here instead, so the header title color silently fell back to inherited
182
+ text color everywhere, in both light and dark mode. */
183
+ --uniface-card-text-muted: #4b5563;
162
184
  --uniface-card-border-color: var(--uniface-color-border-secondary);
163
185
  --uniface-card-color-inside-border: #f3f4f6;
164
186
  --uniface-card-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.06), 0 1px 2px -1px rgba(0, 0, 0, 0.04);
@@ -417,6 +439,11 @@
417
439
  --uniface-treeview-chevron-hover-color: #111827;
418
440
  --uniface-treeview-item-bg: #EEF4FF;
419
441
  --uniface-treeview-item-selected-color: #0061FF;
442
+ /* The listbox body itself previously had no surface/text color of its own
443
+ — it just showed through the page background, which happened to look
444
+ fine only because most light-mode pages are white. */
445
+ --uniface-list-box-color-surface: var(--uniface-color-surface);
446
+ --uniface-list-box-color: var(--uniface-primary-color-font);
420
447
  --uniface-listbox-separator-color: #F0F0F0;
421
448
  --uniface-list-box-hover-surface: #FFFFE1;
422
449
  --uniface-list-box-selected-surface: #b1c8f6;
@@ -446,6 +473,32 @@
446
473
  --uniface-tab-action-background: #FFFFFF;
447
474
  --uniface-tab-hover-text-color: #298DFF;
448
475
  --uniface-tab-header-radius: 4px;
476
+ /* Concise Data Table Tokens
477
+ Previously these tokens didn't exist anywhere in this file at all —
478
+ ConciseListTable.svelte referenced --uniface-concise-table-row-alter-bg/
479
+ -color with light-only fallback values and otherwise used hardcoded
480
+ literals (#f5f5f5 header, #f9f9f9 hover) with no variable and no
481
+ dark-mode hook whatsoever. */
482
+ --uniface-concise-table-color-surface: var(--uniface-color-surface);
483
+ --uniface-concise-table-color: var(--uniface-primary-color-font);
484
+ --uniface-concise-table-header-surface: #f5f5f5;
485
+ --uniface-concise-table-row-alter-bg: #F8FAFC;
486
+ --uniface-concise-table-row-alter-color: #374151;
487
+ --uniface-concise-table-row-hover-bg: #f9f9f9;
488
+ /* Navigator Tokens
489
+ Previously the component declared its own local custom properties
490
+ (scoped to .uniface-navigator-panel) instead of reading from this file,
491
+ and one of them pointed at --uniface-application-bg, a token that was
492
+ never defined anywhere — so the panel background was always invalid/
493
+ unset (transparent) and had no light/dark distinction at all. */
494
+ --uniface-navigator-background-color: var(--uniface-color-surface);
495
+ --uniface-navigator-completed-text-color: #374151;
496
+ --uniface-navigator-not-start-text-color: #6B7280;
497
+ --uniface-navigator-warning-dot-color: #C62828;
498
+ --uniface-navigator-hover-text-color: #298DFF;
499
+ --uniface-navigator-active-text-color: #0061FF;
500
+ --uniface-navigator-highlight-text-color: #0061FF;
501
+ --uniface-navigator-connector-color: #D1D5DB;
449
502
  }
450
503
 
451
504
  /* ============================================
@@ -531,6 +584,20 @@
531
584
  --uniface-treeview-chevron-hover-color: #e5e7eb;
532
585
  --uniface-treeview-item-bg: rgba(255, 255, 255, 0.08);
533
586
  --uniface-treeview-item-selected-color: #60a5fa;
587
+ /* ListBox Tokens - Dark Theme
588
+ Previously entirely missing: every ListBox token only had a light value
589
+ in :root, with no [data-theme="dark"] override, so the box stayed
590
+ light-themed (light header/footer surfaces, near-white hover/selected
591
+ highlight colors) regardless of dark mode. */
592
+ --uniface-list-box-color-surface: var(--uniface-color-surface);
593
+ --uniface-list-box-color: #e5e7eb;
594
+ --uniface-listbox-separator-color: rgba(255, 255, 255, 0.08);
595
+ --uniface-list-box-hover-surface: rgba(255, 255, 255, 0.08);
596
+ --uniface-list-box-selected-surface: rgba(59, 130, 246, 0.24);
597
+ --uniface-list-box-fotter-top-border: 1px solid rgba(255, 255, 255, 0.08);
598
+ --uniface-list-box-border-color: #3a3a3a;
599
+ --uniface-list-box-header-surface: #262626;
600
+ --uniface-list-box-footer-surface: #262626;
534
601
  /* Menu Separator */
535
602
  --uniface-menu-separator-color: rgba(255, 255, 255, 0.08);
536
603
  /* Drawer Tokens */
@@ -543,6 +610,18 @@
543
610
  --uniface-box-color-header-title: var(--uniface-primary-color-font);
544
611
  --uniface-box-color-footer-surface: var(--uniface-color-surface-variant);
545
612
  --uniface-box-color-footer-title: var(--uniface-second-color-font);
613
+ /* Card Tokens - Dark Theme
614
+ Previously entirely missing: Card only had light-mode values in :root,
615
+ with no [data-theme="dark"] override, so cards stayed white/light
616
+ regardless of dark mode. */
617
+ --uniface-card-color-surface: #262626;
618
+ --uniface-card-color: #e5e7eb;
619
+ --uniface-card-text-muted: #9ca3af;
620
+ --uniface-card-color-inside-border: rgba(255, 255, 255, 0.08);
621
+ --uniface-card-shadow: 0 4px 12px rgba(0, 0, 0, 0.5), 0 16px 32px rgba(0, 0, 0, 0.4);
622
+ --uniface-card-color-action-bar: #9ca3af;
623
+ --uniface-card-color-action-bar-hover: #ffffff;
624
+ --uniface-card-color-action-bar-disabled: #52525b;
546
625
  --uniface-page-color-text: var(--uniface-primary-color-font);
547
626
  --uniface-page-color-surface: var(--uniface-color-surface);
548
627
  --uniface-page-border-color: rgba(255, 255, 255, 0.06);
@@ -745,6 +824,22 @@
745
824
  --uniface-tab-action-background: #262626;
746
825
  --uniface-tab-hover-text-color: #60A5FA;
747
826
  --uniface-tab-header-radius: 4px;
827
+ /* Concise Data Table Tokens - Dark Theme */
828
+ --uniface-concise-table-color-surface: var(--uniface-color-surface);
829
+ --uniface-concise-table-color: #e5e7eb;
830
+ --uniface-concise-table-header-surface: #262626;
831
+ --uniface-concise-table-row-alter-bg: #1f1f1f;
832
+ --uniface-concise-table-row-alter-color: #e5e7eb;
833
+ --uniface-concise-table-row-hover-bg: rgba(255, 255, 255, 0.08);
834
+ /* Navigator Tokens - Dark Theme */
835
+ --uniface-navigator-background-color: var(--uniface-color-surface);
836
+ --uniface-navigator-completed-text-color: #e5e7eb;
837
+ --uniface-navigator-not-start-text-color: #9ca3af;
838
+ --uniface-navigator-warning-dot-color: #f87171;
839
+ --uniface-navigator-hover-text-color: #60a5fa;
840
+ --uniface-navigator-active-text-color: #60a5fa;
841
+ --uniface-navigator-highlight-text-color: #60a5fa;
842
+ --uniface-navigator-connector-color: #3a3a3a;
748
843
  }
749
844
 
750
845
  * {
@@ -1098,21 +1193,21 @@
1098
1193
  .uniface-form-editor .uniface-common-editor:focus-within.number-input {
1099
1194
  text-align: left;
1100
1195
  }
1101
- .uniface-form-editor .uniface-common-editor:focus-within .clean-icon i {
1196
+ .uniface-form-editor .uniface-common-editor:focus-within .clean-icon.has-value i {
1102
1197
  display: block;
1103
1198
  position: relative;
1104
1199
  }
1105
- .uniface-form-editor .uniface-common-editor:focus-within .clean-icon i:active {
1200
+ .uniface-form-editor .uniface-common-editor:focus-within .clean-icon.has-value i:active {
1106
1201
  top: 1px;
1107
1202
  }
1108
1203
  .uniface-form-editor .uniface-common-editor:has(.clean-icon) input {
1109
1204
  padding-right: 14px;
1110
1205
  }
1111
- .uniface-form-editor .uniface-common-picker:hover .clean-icon i {
1206
+ .uniface-form-editor .uniface-common-picker:hover .clean-icon.has-value i {
1112
1207
  display: block;
1113
1208
  position: relative;
1114
1209
  }
1115
- .uniface-form-editor .uniface-common-picker:hover .clean-icon i:active {
1210
+ .uniface-form-editor .uniface-common-picker:hover .clean-icon.has-value i:active {
1116
1211
  top: 1px;
1117
1212
  }
1118
1213
  .uniface-form-editor .uniface-common-picker:has(.clean-icon) input {
@@ -1223,7 +1318,7 @@
1223
1318
  and FloatingPanel.svelte, which use the same fixed + viewport-relative-coords pattern. */
1224
1319
  position: fixed;
1225
1320
  display: block;
1226
- z-index: 100;
1321
+ z-index: var(--uniface-z-popover, 100);
1227
1322
  opacity: 1;
1228
1323
  visibility: visible;
1229
1324
  transition: opacity 0.3s ease-in-out;
@@ -1590,7 +1685,7 @@
1590
1685
  width: 100vw;
1591
1686
  height: 100vh;
1592
1687
  background-color: rgba(0, 0, 0, 0.45);
1593
- z-index: 9998;
1688
+ z-index: var(--uniface-z-modal-overlay, 9998);
1594
1689
  opacity: 1;
1595
1690
  transition: opacity 0.15s ease;
1596
1691
  }
package/dist/types.d.ts CHANGED
@@ -68,9 +68,9 @@ export type ButtonActions = Array<ButtonAction | null>;
68
68
  */
69
69
  export type TabActionHandler = (tab: any) => void;
70
70
  /**
71
- * 标签页关闭处理器
71
+ * 标签页关闭处理器:返回 false(含 Promise<false>)可阻止关闭
72
72
  */
73
- export type TabCloseHandler = (tab: any) => Promise<boolean>;
73
+ export type TabCloseHandler = (tab: any) => boolean | Promise<boolean>;
74
74
  /**
75
75
  * 标签页渲染器
76
76
  */
@@ -175,8 +175,9 @@ export interface RowFontStyle {
175
175
  export type GetCellStyle = (data: any) => string;
176
176
  /**
177
177
  * 获取整行的样式
178
+ * @template T Row data type. Defaults to `any` so existing untyped usage keeps compiling.
178
179
  */
179
- export type GetRowFontStyle = (data: any) => RowFontStyle;
180
+ export type GetRowFontStyle<T = any> = (data: T) => RowFontStyle;
180
181
  /**
181
182
  *
182
183
  */