@ticatec/uniface-element 5.0.1 → 5.0.3
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.
- package/README.md +29 -54
- package/README_CN.md +37 -57
- package/dist/base-calendar/CalendarBase.svelte +29 -6
- package/dist/base-calendar/CalendarBase.svelte.d.ts +4 -0
- package/dist/base-calendar/MonthPickupPanel.svelte +19 -6
- package/dist/base-calendar/MonthPickupPanel.svelte.d.ts +1 -1
- package/dist/base-calendar/MonthlyCalendar.svelte +18 -2
- package/dist/base-calendar/YearPickupPanel.svelte +24 -6
- package/dist/base-calendar/YearPickupPanel.svelte.d.ts +1 -1
- package/dist/composite/pagination_panel/PaginationPanel.svelte +2 -0
- package/dist/data_display/card/CommonCardActionBar.svelte +34 -14
- package/dist/data_display/concise_data_table/Column.d.ts +6 -0
- package/dist/data_display/concise_data_table/ConciseListTable.svelte +4 -3
- package/dist/data_display/concise_data_table/DataCell.svelte +3 -3
- package/dist/data_display/list_box/ListBox.svelte +90 -15
- package/dist/data_display/list_box/ListBox.svelte.d.ts +4 -0
- package/dist/data_display/tree_view/TreeNodeView.svelte +25 -4
- package/dist/data_table/DataTable.svelte +5 -5
- package/dist/data_table/DataTable.svelte.d.ts +36 -16
- package/dist/data_table/lib/ActionsColumn.d.ts +2 -2
- package/dist/data_table/lib/DataColumn.d.ts +7 -1
- package/dist/data_table/lib/RowAction.d.ts +5 -5
- package/dist/data_table/parts/FixedHeaderPanel.svelte +18 -3
- package/dist/data_table/parts/FixedRow.svelte +10 -1
- package/dist/data_table/parts/HrefCell.svelte +1 -1
- package/dist/data_table/parts/TableHeaderPanel.svelte +18 -2
- package/dist/data_table/parts/TextCell.svelte +2 -2
- package/dist/form/attachment_files/AttachmentEditor.svelte +9 -9
- package/dist/form/attachment_files/FileRender.svelte +1 -1
- package/dist/form/attachment_files/FileUploadPanel.svelte +5 -1
- package/dist/form/cascade_options_select/CascadeOptionSelect.svelte +10 -2
- package/dist/form/cascade_options_select/OptionsBox.svelte +48 -2
- package/dist/form/color_picker/ColorPickerPanel.svelte +23 -20
- package/dist/form/color_picker/ColorPickerPanel.svelte.d.ts +5 -18
- package/dist/form/common_editor/CommonEditor.svelte +36 -5
- package/dist/form/common_editor/CommonPicker.svelte +11 -4
- package/dist/form/common_editor/CommonPopupEditor.svelte +11 -2
- package/dist/form/common_range_editor/CommonRangeEditor.svelte +11 -2
- package/dist/form/date_picker/DatePicker.svelte +1 -1
- package/dist/form/date_picker/DateTimePicker.svelte +1 -1
- package/dist/form/date_picker/ScrollBar.svelte +20 -12
- package/dist/form/date_picker/ScrollBar.svelte.d.ts +4 -18
- package/dist/form/date_range/DateRangeEditor.svelte +11 -3
- package/dist/form/group_box/group-check-box/GroupCheckBox.svelte +6 -1
- package/dist/form/group_box/group-radio-box/GroupRadioBox.svelte +6 -1
- package/dist/form/image-files/ImageFilesField.svelte +51 -7
- package/dist/form/image-files/ImagePreview.svelte +8 -2
- package/dist/form/image-files/ImageRender.svelte +69 -11
- package/dist/form/input_options_select/InputOptionsSelect.svelte +10 -2
- package/dist/form/lookup_editor/LookupEditor.svelte +9 -2
- package/dist/form/number_editor/NumberEditor.svelte +2 -0
- package/dist/form/options_multi_select/OptionsMultiSelect.svelte +19 -3
- package/dist/form/options_select/OptionsSelect.svelte +15 -6
- package/dist/form/options_select/OptionsSelect.svelte.d.ts +39 -17
- package/dist/form/prompts_text_editor/PromptsTextEditor.svelte +12 -3
- package/dist/form/search_box/SearchBox.svelte +2 -1
- package/dist/form/switch/Switch.svelte +4 -4
- package/dist/form/text_editor/PasswordEditor.svelte +2 -1
- package/dist/form/text_editor/TextEditor.svelte +2 -1
- package/dist/form/time_editor/TimeEditor.svelte +2 -0
- package/dist/form/unit-number-editor/UnitNumberEditor.svelte +13 -2
- package/dist/form/unit-number-editor/UnitNumberEditor.svelte.d.ts +1 -0
- package/dist/general/button/Button.svelte +3 -0
- package/dist/general/button/Button.svelte.d.ts +1 -0
- package/dist/general/button/IconButton.svelte +3 -1
- package/dist/general/button/IconButton.svelte.d.ts +1 -0
- package/dist/general/button/TextButton.svelte +3 -0
- package/dist/general/button/TextButton.svelte.d.ts +1 -0
- package/dist/general/icon/Icon.svelte +18 -1
- package/dist/general/tag/Tag.svelte +9 -1
- package/dist/navigation/nav_menu/NavigatorMenu.svelte +2 -2
- package/dist/navigation/nav_menu/NavigatorMenuItem.svelte +10 -1
- package/dist/navigation/navigator/Navigator.svelte +17 -17
- package/dist/navigation/page-stack/PageStack.svelte +160 -0
- package/dist/navigation/page-stack/PageStack.svelte.d.ts +24 -0
- package/dist/navigation/page-stack/PageStackContext.d.ts +25 -0
- package/dist/navigation/page-stack/PageStackContext.js +12 -0
- package/dist/navigation/page-stack/PageStackFrame.svelte +24 -0
- package/dist/navigation/page-stack/PageStackFrame.svelte.d.ts +11 -0
- package/dist/navigation/page-stack/README.md +154 -0
- package/dist/navigation/page-stack/README_CN.md +154 -0
- package/dist/navigation/page-stack/index.d.ts +6 -0
- package/dist/navigation/page-stack/index.js +4 -0
- package/dist/navigation/pagination/Pagination.svelte +114 -14
- package/dist/navigation/tabs/Tabs.svelte +42 -10
- package/dist/navigation/tabs/Tabs.svelte.d.ts +2 -2
- package/dist/overlay/common/FloatingPanel.svelte +3 -11
- package/dist/overlay/common/FullscreenOverlay.svelte +27 -3
- package/dist/overlay/common/FullscreenOverlay.svelte.d.ts +6 -27
- package/dist/overlay/common/ModalPopover.svelte +18 -2
- package/dist/overlay/common/Popover.svelte +56 -57
- package/dist/overlay/common/Popover.svelte.d.ts +1 -5
- package/dist/overlay/common/escapeClose.d.ts +28 -0
- package/dist/overlay/common/escapeClose.js +47 -0
- package/dist/overlay/common/focusTrap.d.ts +8 -0
- package/dist/overlay/common/focusTrap.js +83 -0
- package/dist/overlay/common/mountSingleton.d.ts +27 -0
- package/dist/overlay/common/mountSingleton.js +45 -0
- package/dist/overlay/common/outsideClick.d.ts +28 -0
- package/dist/overlay/common/outsideClick.js +20 -0
- package/dist/overlay/common/scrollLock.d.ts +8 -0
- package/dist/overlay/common/scrollLock.js +49 -0
- package/dist/overlay/context-menu/ContextMenu.svelte.js +2 -15
- package/dist/overlay/context-menu/ContextMenuPanel.svelte +13 -1
- package/dist/overlay/dialog/Dialog.svelte +46 -29
- package/dist/overlay/dialog/DialogManager.svelte.js +7 -9
- package/dist/overlay/drawer/Drawer.svelte +30 -3
- package/dist/overlay/indicator/Indicator.svelte.js +2 -15
- package/dist/overlay/indicator/IndicatorPanel.svelte +4 -1
- package/dist/overlay/message-box/MessageBox.svelte.js +2 -15
- package/dist/overlay/message-box/MessageBoxPanel.svelte +19 -3
- package/dist/overlay/toast/Toast.svelte.js +2 -15
- package/dist/overlay/toast/ToastPanel.svelte +1 -1
- package/dist/overlay/tooltip/Tooltip.svelte +1 -1
- package/dist/ticatec-uniface-web.css +102 -7
- package/dist/types.d.ts +4 -3
- package/docs/components/README.md +133 -0
- package/docs/components/accordion/README.md +60 -0
- package/docs/components/accordion/example.svelte +7 -0
- package/docs/components/action-bar/README.md +114 -0
- package/docs/components/action-bar/example.svelte +7 -0
- package/docs/components/app-top-bar/README.md +60 -0
- package/docs/components/app-top-bar/example.svelte +9 -0
- package/docs/components/attachment-files/README.md +332 -0
- package/docs/components/attachment-files/example.svelte +16 -0
- package/docs/components/box/README.md +63 -0
- package/docs/components/box/example.svelte +10 -0
- package/docs/components/breadcrumbs/README.md +86 -0
- package/docs/components/breadcrumbs/example.svelte +11 -0
- package/docs/components/button/README.md +103 -0
- package/docs/components/button/example.svelte +10 -0
- package/docs/components/card/README.md +113 -0
- package/docs/components/card/example.svelte +10 -0
- package/docs/components/cascade-options-select/README.md +162 -0
- package/docs/components/cascade-options-select/example.svelte +18 -0
- package/docs/components/checkbox/README.md +103 -0
- package/docs/components/checkbox/example.svelte +15 -0
- package/docs/components/classic-layout/README.md +76 -0
- package/docs/components/classic-layout/example.svelte +16 -0
- package/docs/components/color-picker/README.md +132 -0
- package/docs/components/color-picker/example.svelte +16 -0
- package/docs/components/columns-layout/README.md +70 -0
- package/docs/components/columns-layout/example.svelte +12 -0
- package/docs/components/common-dialog/README.md +109 -0
- package/docs/components/common-dialog/example.svelte +12 -0
- package/docs/components/concise-data-table/README.md +307 -0
- package/docs/components/concise-data-table/example.svelte +16 -0
- package/docs/components/context-menu/README.md +99 -0
- package/docs/components/context-menu/example.svelte +10 -0
- package/docs/components/criteria-field/README.md +69 -0
- package/docs/components/criteria-field/example.svelte +9 -0
- package/docs/components/data-table/README.md +365 -0
- package/docs/components/data-table/example.svelte +23 -0
- package/docs/components/date-picker/README.md +136 -0
- package/docs/components/date-picker/example.svelte +16 -0
- package/docs/components/date-range/README.md +122 -0
- package/docs/components/date-range/example.svelte +15 -0
- package/docs/components/date-time-picker/README.md +142 -0
- package/docs/components/date-time-picker/example.svelte +16 -0
- package/docs/components/dialog/README.md +174 -0
- package/docs/components/dialog/example.svelte +13 -0
- package/docs/components/dialog-board/README.md +41 -0
- package/docs/components/dialog-board/example.svelte +5 -0
- package/docs/components/drawer/README.md +70 -0
- package/docs/components/drawer/example.svelte +12 -0
- package/docs/components/flex-form/README.md +55 -0
- package/docs/components/flex-form/example.svelte +7 -0
- package/docs/components/flex-row-form/README.md +56 -0
- package/docs/components/flex-row-form/example.svelte +7 -0
- package/docs/components/form-field/README.md +84 -0
- package/docs/components/form-field/example.svelte +10 -0
- package/docs/components/full-screen-overlay/README.md +66 -0
- package/docs/components/full-screen-overlay/example.svelte +11 -0
- package/docs/components/grid-form/README.md +75 -0
- package/docs/components/grid-form/example.svelte +9 -0
- package/docs/components/group-check-box/README.md +147 -0
- package/docs/components/group-check-box/example.svelte +21 -0
- package/docs/components/group-radio-box/README.md +144 -0
- package/docs/components/group-radio-box/example.svelte +21 -0
- package/docs/components/header-layout/README.md +65 -0
- package/docs/components/header-layout/example.svelte +12 -0
- package/docs/components/icon/README.md +87 -0
- package/docs/components/icon/example.svelte +9 -0
- package/docs/components/icon-button/README.md +91 -0
- package/docs/components/icon-button/example.svelte +10 -0
- package/docs/components/image-files/README.md +174 -0
- package/docs/components/image-files/example.svelte +19 -0
- package/docs/components/indicator/README.md +53 -0
- package/docs/components/indicator/example.svelte +14 -0
- package/docs/components/input-options-select/README.md +163 -0
- package/docs/components/input-options-select/example.svelte +16 -0
- package/docs/components/list-box/ListItem.svelte +14 -0
- package/docs/components/list-box/README.md +221 -0
- package/docs/components/list-box/example.svelte +61 -0
- package/docs/components/lookup-editor/README.md +137 -0
- package/docs/components/lookup-editor/example.svelte +16 -0
- package/docs/components/memo-editor/README.md +143 -0
- package/docs/components/memo-editor/example.svelte +16 -0
- package/docs/components/message-box/README.md +68 -0
- package/docs/components/message-box/example.svelte +10 -0
- package/docs/components/navigator/README.md +75 -0
- package/docs/components/navigator/example.svelte +13 -0
- package/docs/components/navigator-menu/README.md +113 -0
- package/docs/components/navigator-menu/example.svelte +10 -0
- package/docs/components/number-editor/README.md +144 -0
- package/docs/components/number-editor/example.svelte +16 -0
- package/docs/components/number-range/README.md +116 -0
- package/docs/components/number-range/example.svelte +15 -0
- package/docs/components/options-multi-select/README.md +161 -0
- package/docs/components/options-multi-select/example.svelte +20 -0
- package/docs/components/options-select/README.md +160 -0
- package/docs/components/options-select/example.svelte +21 -0
- package/docs/components/page/README.md +77 -0
- package/docs/components/page/example.svelte +10 -0
- package/docs/components/pagination/README.md +88 -0
- package/docs/components/pagination/example.svelte +10 -0
- package/docs/components/pagination-panel/README.md +120 -0
- package/docs/components/pagination-panel/example.svelte +16 -0
- package/docs/components/popup-hint/README.md +44 -0
- package/docs/components/popup-hint/example.svelte +5 -0
- package/docs/components/progress-bar/README.md +64 -0
- package/docs/components/progress-bar/example.svelte +8 -0
- package/docs/components/progress-step-bar/README.md +93 -0
- package/docs/components/progress-step-bar/example.svelte +7 -0
- package/docs/components/prompts-text-editor/README.md +146 -0
- package/docs/components/prompts-text-editor/example.svelte +17 -0
- package/docs/components/radio-button/README.md +121 -0
- package/docs/components/radio-button/example.svelte +18 -0
- package/docs/components/rows-layout/README.md +70 -0
- package/docs/components/rows-layout/example.svelte +12 -0
- package/docs/components/search-box/README.md +136 -0
- package/docs/components/search-box/example.svelte +16 -0
- package/docs/components/separator/README.md +44 -0
- package/docs/components/separator/example.svelte +7 -0
- package/docs/components/sidebar-layout/README.md +65 -0
- package/docs/components/sidebar-layout/example.svelte +12 -0
- package/docs/components/split/README.md +48 -0
- package/docs/components/split/example.svelte +8 -0
- package/docs/components/switch/README.md +77 -0
- package/docs/components/switch/example.svelte +11 -0
- package/docs/components/tabs/README.md +127 -0
- package/docs/components/tabs/example.svelte +22 -0
- package/docs/components/tag/README.md +63 -0
- package/docs/components/tag/example.svelte +8 -0
- package/docs/components/text-button/README.md +90 -0
- package/docs/components/text-button/example.svelte +9 -0
- package/docs/components/text-editor/README.md +147 -0
- package/docs/components/text-editor/example.svelte +16 -0
- package/docs/components/time-editor/README.md +130 -0
- package/docs/components/time-editor/example.svelte +16 -0
- package/docs/components/toast/README.md +52 -0
- package/docs/components/toast/example.svelte +9 -0
- package/docs/components/tooltip/README.md +44 -0
- package/docs/components/tooltip/example.svelte +5 -0
- package/docs/components/transfer/README.md +109 -0
- package/docs/components/transfer/example.svelte +13 -0
- package/docs/components/tree-view/README.md +163 -0
- package/docs/components/tree-view/example.svelte +26 -0
- package/docs/components/unit-number-editor/README.md +192 -0
- package/docs/components/unit-number-editor/example.svelte +18 -0
- package/package.json +15 -7
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared, reference-counted body scroll-lock.
|
|
3
|
+
*
|
|
4
|
+
* Any number of overlays (Popover, Dialog, Drawer, ModalPopover,
|
|
5
|
+
* FullscreenOverlay, ...) can call lockBodyScroll()/unlockBodyScroll()
|
|
6
|
+
* independently, in any open/close order, without one overlay's close
|
|
7
|
+
* clobbering another still-open overlay's lock. Only the very first lock
|
|
8
|
+
* captures the pre-lock overflowY value, and only the transition back to
|
|
9
|
+
* zero open locks restores it — this is what makes stacking safe.
|
|
10
|
+
*
|
|
11
|
+
* Originally lived inline in Popover.svelte; generalized here so every
|
|
12
|
+
* modal-ish overlay in the library shares one lock instead of each
|
|
13
|
+
* reinventing (or omitting) its own save/restore.
|
|
14
|
+
*/
|
|
15
|
+
let lockCount = 0;
|
|
16
|
+
let savedOverflowY = null;
|
|
17
|
+
export function lockBodyScroll() {
|
|
18
|
+
if (typeof document === 'undefined')
|
|
19
|
+
return;
|
|
20
|
+
if (lockCount === 0) {
|
|
21
|
+
savedOverflowY = window.getComputedStyle(document.body).overflowY;
|
|
22
|
+
document.body.style.overflowY = 'hidden';
|
|
23
|
+
}
|
|
24
|
+
lockCount++;
|
|
25
|
+
}
|
|
26
|
+
export function unlockBodyScroll() {
|
|
27
|
+
if (typeof document === 'undefined')
|
|
28
|
+
return;
|
|
29
|
+
lockCount = Math.max(0, lockCount - 1);
|
|
30
|
+
if (lockCount === 0 && savedOverflowY != null) {
|
|
31
|
+
document.body.style.overflowY = savedOverflowY;
|
|
32
|
+
savedOverflowY = null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Convenience wrapper for the common "lock on mount, unlock on cleanup"
|
|
37
|
+
* pattern, idempotent against being released more than once (e.g. both a
|
|
38
|
+
* timer and an unmount cleanup racing to call it).
|
|
39
|
+
*/
|
|
40
|
+
export function acquireScrollLock() {
|
|
41
|
+
lockBodyScroll();
|
|
42
|
+
let released = false;
|
|
43
|
+
return () => {
|
|
44
|
+
if (released)
|
|
45
|
+
return;
|
|
46
|
+
released = true;
|
|
47
|
+
unlockBodyScroll();
|
|
48
|
+
};
|
|
49
|
+
}
|
|
@@ -1,24 +1,11 @@
|
|
|
1
1
|
// src/lib/overlay/context-menu/ContextMenu.svelte.ts
|
|
2
|
-
import { mount } from 'svelte';
|
|
3
2
|
import ContextMenuPanel from "./ContextMenuPanel.svelte";
|
|
4
|
-
|
|
3
|
+
import { createSingletonMounter } from '../common/mountSingleton';
|
|
5
4
|
/**
|
|
6
5
|
* 懒挂载:第一次 show() 时把面板挂到 document.body,后续复用同一个实例。
|
|
7
6
|
* 不需要用户在 app 根节点手动挂 <ContextMenu />。
|
|
8
7
|
*/
|
|
9
|
-
|
|
10
|
-
if (panelMounted)
|
|
11
|
-
return;
|
|
12
|
-
if (typeof window !== 'undefined' && typeof document !== 'undefined' && document.body) {
|
|
13
|
-
try {
|
|
14
|
-
mount(ContextMenuPanel, { target: document.body });
|
|
15
|
-
panelMounted = true;
|
|
16
|
-
}
|
|
17
|
-
catch {
|
|
18
|
-
// Silent fallback
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
}
|
|
8
|
+
const ensureMounted = createSingletonMounter(ContextMenuPanel);
|
|
22
9
|
class ContextMenuController {
|
|
23
10
|
visible = $state(false);
|
|
24
11
|
position = $state({ x: 0, y: 0 });
|
|
@@ -5,14 +5,22 @@
|
|
|
5
5
|
|
|
6
6
|
let focusedIndex = $state(-1);
|
|
7
7
|
let menuPanel = $state<HTMLDivElement | null>(null);
|
|
8
|
+
let previouslyFocused: HTMLElement | null = null;
|
|
8
9
|
|
|
9
10
|
// 状态响应器:一旦菜单被激活,立即强制捕获焦点
|
|
10
11
|
$effect(() => {
|
|
11
12
|
if (contextMenuSvelte.visible) {
|
|
13
|
+
previouslyFocused = document.activeElement as HTMLElement | null;
|
|
12
14
|
focusedIndex = -1; // 初始化键盘高亮位置
|
|
13
15
|
tick().then(() => {
|
|
14
16
|
menuPanel?.focus();
|
|
15
17
|
});
|
|
18
|
+
} else if (previouslyFocused) {
|
|
19
|
+
// Regression: closing the menu previously just dropped focus back
|
|
20
|
+
// to the document body instead of wherever the user was before
|
|
21
|
+
// opening it (right-clicking a table row, say).
|
|
22
|
+
previouslyFocused.focus();
|
|
23
|
+
previouslyFocused = null;
|
|
16
24
|
}
|
|
17
25
|
});
|
|
18
26
|
|
|
@@ -71,6 +79,10 @@
|
|
|
71
79
|
break;
|
|
72
80
|
case 'Escape':
|
|
73
81
|
event.preventDefault();
|
|
82
|
+
// Stop this from also reaching the shared window-level Escape
|
|
83
|
+
// stack (escapeClose.ts) — otherwise a context menu opened on
|
|
84
|
+
// top of a Dialog/Popover would close both on one Escape press.
|
|
85
|
+
event.stopPropagation();
|
|
74
86
|
contextMenuSvelte.hide();
|
|
75
87
|
break;
|
|
76
88
|
case 'Tab':
|
|
@@ -137,7 +149,7 @@
|
|
|
137
149
|
so the menu drifted away from the cursor by the page's scroll offset. `fixed`
|
|
138
150
|
keeps the same coordinate frame as the values driving left/top. */
|
|
139
151
|
position: fixed;
|
|
140
|
-
z-index: 1000;
|
|
152
|
+
z-index: var(--uniface-z-context-menu, 1000);
|
|
141
153
|
outline: none;
|
|
142
154
|
border-radius: var(--uniface-radius);
|
|
143
155
|
/* 1. 深度整合:基础环境与阴影规范 */
|
|
@@ -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
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
<
|
|
142
|
-
|
|
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
|
|
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-
|
|
173
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2
|
+
import { createSingletonMounter } from '../common/mountSingleton';
|
|
4
3
|
/**
|
|
5
4
|
* 懒挂载:第一次 show() 时把面板挂到 document.body,后续复用同一个实例。
|
|
6
5
|
* 不需要用户在 app 根节点手动挂 <IndicatorBoard />。
|
|
7
6
|
*/
|
|
8
|
-
|
|
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
|
|
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
|
-
|
|
7
|
-
|
|
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
|
-
|
|
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
|
-
<
|
|
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
|
-
|
|
2
|
+
import { createSingletonMounter } from '../common/mountSingleton';
|
|
4
3
|
/**
|
|
5
4
|
* 懒挂载:第一次 show() 时把面板挂到 document.body,后续复用同一个实例。
|
|
6
5
|
* 不需要用户在 app 根节点手动挂 <ToastBoard />。
|
|
7
6
|
*/
|
|
8
|
-
|
|
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');
|
|
@@ -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>
|