zkit-ui 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +267 -0
- package/dist/ComponentLibProvider.d.ts +15 -0
- package/dist/ComponentLibProvider.d.ts.map +1 -0
- package/dist/ComponentLibProvider.js +18 -0
- package/dist/assets/animations/spinner.json +1 -0
- package/dist/assets/icons/check.svg +4 -0
- package/dist/assets/icons/toast/error.webp +0 -0
- package/dist/assets/icons/toast/success.webp +0 -0
- package/dist/assets/icons/toast/warning.webp +0 -0
- package/dist/config.d.ts +16 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +42 -0
- package/dist/i18n/I18nProvider.d.ts +11 -0
- package/dist/i18n/I18nProvider.d.ts.map +1 -0
- package/dist/i18n/I18nProvider.js +84 -0
- package/dist/i18n/locale.d.ts +7 -0
- package/dist/i18n/locale.d.ts.map +1 -0
- package/dist/i18n/locale.js +87 -0
- package/dist/i18n/locales/de.d.ts +51 -0
- package/dist/i18n/locales/de.d.ts.map +1 -0
- package/dist/i18n/locales/de.js +62 -0
- package/dist/i18n/locales/en-US.d.ts +51 -0
- package/dist/i18n/locales/en-US.d.ts.map +1 -0
- package/dist/i18n/locales/en-US.js +62 -0
- package/dist/i18n/locales/index.d.ts +9 -0
- package/dist/i18n/locales/index.d.ts.map +1 -0
- package/dist/i18n/locales/index.js +23 -0
- package/dist/i18n/locales/ja.d.ts +51 -0
- package/dist/i18n/locales/ja.d.ts.map +1 -0
- package/dist/i18n/locales/ja.js +62 -0
- package/dist/i18n/locales/zh-CN.d.ts +51 -0
- package/dist/i18n/locales/zh-CN.d.ts.map +1 -0
- package/dist/i18n/locales/zh-CN.js +62 -0
- package/dist/i18n/locales/zh-TW.d.ts +51 -0
- package/dist/i18n/locales/zh-TW.d.ts.map +1 -0
- package/dist/i18n/locales/zh-TW.js +62 -0
- package/dist/i18n/types.d.ts +11 -0
- package/dist/i18n/types.d.ts.map +1 -0
- package/dist/i18n/types.js +2 -0
- package/dist/i18n/useI18n.d.ts +2 -0
- package/dist/i18n/useI18n.d.ts.map +1 -0
- package/dist/i18n/useI18n.js +41 -0
- package/dist/index.d.ts +57 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +90 -0
- package/dist/services/ActionDialogService/ActionDialog.d.ts +4 -0
- package/dist/services/ActionDialogService/ActionDialog.d.ts.map +1 -0
- package/dist/services/ActionDialogService/ActionDialog.js +531 -0
- package/dist/services/ActionDialogService/index.d.ts +4 -0
- package/dist/services/ActionDialogService/index.d.ts.map +1 -0
- package/dist/services/ActionDialogService/index.js +8 -0
- package/dist/services/ActionDialogService/service.d.ts +7 -0
- package/dist/services/ActionDialogService/service.d.ts.map +1 -0
- package/dist/services/ActionDialogService/service.js +309 -0
- package/dist/services/ActionDialogService/shared.d.ts +17 -0
- package/dist/services/ActionDialogService/shared.d.ts.map +1 -0
- package/dist/services/ActionDialogService/shared.js +167 -0
- package/dist/services/ActionDialogService/types.d.ts +160 -0
- package/dist/services/ActionDialogService/types.d.ts.map +1 -0
- package/dist/services/ActionDialogService/types.js +2 -0
- package/dist/services/CardToastService/index.d.ts +115 -0
- package/dist/services/CardToastService/index.d.ts.map +1 -0
- package/dist/services/CardToastService/index.js +899 -0
- package/dist/services/ImagePreviewService/index.d.ts +179 -0
- package/dist/services/ImagePreviewService/index.d.ts.map +1 -0
- package/dist/services/ImagePreviewService/index.js +1383 -0
- package/dist/services/LoadingService/index.d.ts +146 -0
- package/dist/services/LoadingService/index.d.ts.map +1 -0
- package/dist/services/LoadingService/index.js +827 -0
- package/dist/services/PermissionPurposeDialogService/index.d.ts +99 -0
- package/dist/services/PermissionPurposeDialogService/index.d.ts.map +1 -0
- package/dist/services/PermissionPurposeDialogService/index.js +360 -0
- package/dist/services/PickerService/index.d.ts +128 -0
- package/dist/services/PickerService/index.d.ts.map +1 -0
- package/dist/services/PickerService/index.js +250 -0
- package/dist/theme/ThemeProvider.d.ts +15 -0
- package/dist/theme/ThemeProvider.d.ts.map +1 -0
- package/dist/theme/ThemeProvider.js +61 -0
- package/dist/theme/defaultTheme.d.ts +3 -0
- package/dist/theme/defaultTheme.d.ts.map +1 -0
- package/dist/theme/defaultTheme.js +28 -0
- package/dist/theme/mergeTheme.d.ts +3 -0
- package/dist/theme/mergeTheme.d.ts.map +1 -0
- package/dist/theme/mergeTheme.js +19 -0
- package/dist/theme/types.d.ts +17 -0
- package/dist/theme/types.d.ts.map +1 -0
- package/dist/theme/types.js +2 -0
- package/dist/theme/useTheme.d.ts +2 -0
- package/dist/theme/useTheme.d.ts.map +1 -0
- package/dist/theme/useTheme.js +46 -0
- package/dist/ui/Accordion/index.d.ts +99 -0
- package/dist/ui/Accordion/index.d.ts.map +1 -0
- package/dist/ui/Accordion/index.js +777 -0
- package/dist/ui/AddressCascader/index.d.ts +58 -0
- package/dist/ui/AddressCascader/index.d.ts.map +1 -0
- package/dist/ui/AddressCascader/index.js +710 -0
- package/dist/ui/BetweenTime/index.d.ts +82 -0
- package/dist/ui/BetweenTime/index.d.ts.map +1 -0
- package/dist/ui/BetweenTime/index.js +702 -0
- package/dist/ui/Button/index.d.ts +93 -0
- package/dist/ui/Button/index.d.ts.map +1 -0
- package/dist/ui/Button/index.js +832 -0
- package/dist/ui/Checkbox/index.d.ts +103 -0
- package/dist/ui/Checkbox/index.d.ts.map +1 -0
- package/dist/ui/Checkbox/index.js +694 -0
- package/dist/ui/DatePicker/index.d.ts +5 -0
- package/dist/ui/DatePicker/index.d.ts.map +1 -0
- package/dist/ui/DatePicker/index.js +157 -0
- package/dist/ui/DatePicker/types.d.ts +99 -0
- package/dist/ui/DatePicker/types.d.ts.map +1 -0
- package/dist/ui/DatePicker/types.js +2 -0
- package/dist/ui/DatePicker/utils.d.ts +32 -0
- package/dist/ui/DatePicker/utils.d.ts.map +1 -0
- package/dist/ui/DatePicker/utils.js +316 -0
- package/dist/ui/LinkedScroll/index.d.ts +78 -0
- package/dist/ui/LinkedScroll/index.d.ts.map +1 -0
- package/dist/ui/LinkedScroll/index.js +470 -0
- package/dist/ui/LoadingSpinner/index.d.ts +8 -0
- package/dist/ui/LoadingSpinner/index.d.ts.map +1 -0
- package/dist/ui/LoadingSpinner/index.js +122 -0
- package/dist/ui/Picker/actionBar.d.ts +14 -0
- package/dist/ui/Picker/actionBar.d.ts.map +1 -0
- package/dist/ui/Picker/actionBar.js +28 -0
- package/dist/ui/Picker/index.d.ts +5 -0
- package/dist/ui/Picker/index.d.ts.map +1 -0
- package/dist/ui/Picker/index.js +412 -0
- package/dist/ui/Picker/types.d.ts +80 -0
- package/dist/ui/Picker/types.d.ts.map +1 -0
- package/dist/ui/Picker/types.js +2 -0
- package/dist/ui/Picker/utils.d.ts +27 -0
- package/dist/ui/Picker/utils.d.ts.map +1 -0
- package/dist/ui/Picker/utils.js +182 -0
- package/dist/ui/Radio/index.d.ts +95 -0
- package/dist/ui/Radio/index.d.ts.map +1 -0
- package/dist/ui/Radio/index.js +633 -0
- package/dist/ui/Sheet/index.d.ts +12 -0
- package/dist/ui/Sheet/index.d.ts.map +1 -0
- package/dist/ui/Sheet/index.js +832 -0
- package/dist/ui/Sheet/nativeBottom.d.ts +52 -0
- package/dist/ui/Sheet/nativeBottom.d.ts.map +1 -0
- package/dist/ui/Sheet/nativeBottom.js +442 -0
- package/dist/ui/Sheet/nativeBottom.web.d.ts +18 -0
- package/dist/ui/Sheet/nativeBottom.web.d.ts.map +1 -0
- package/dist/ui/Sheet/nativeBottom.web.js +46 -0
- package/dist/ui/Sheet/slots.d.ts +5 -0
- package/dist/ui/Sheet/slots.d.ts.map +1 -0
- package/dist/ui/Sheet/slots.js +40 -0
- package/dist/ui/Sheet/types.d.ts +131 -0
- package/dist/ui/Sheet/types.d.ts.map +1 -0
- package/dist/ui/Sheet/types.js +2 -0
- package/dist/ui/SliderCaptcha/index.d.ts +57 -0
- package/dist/ui/SliderCaptcha/index.d.ts.map +1 -0
- package/dist/ui/SliderCaptcha/index.js +586 -0
- package/dist/ui/Switch/index.d.ts +72 -0
- package/dist/ui/Switch/index.d.ts.map +1 -0
- package/dist/ui/Switch/index.js +671 -0
- package/dist/ui/Text/index.d.ts +41 -0
- package/dist/ui/Text/index.d.ts.map +1 -0
- package/dist/ui/Text/index.js +325 -0
- package/dist/ui/TextInput/TextInputPrimitive.d.ts +8 -0
- package/dist/ui/TextInput/TextInputPrimitive.d.ts.map +1 -0
- package/dist/ui/TextInput/TextInputPrimitive.js +54 -0
- package/dist/ui/TextInput/index.d.ts +89 -0
- package/dist/ui/TextInput/index.d.ts.map +1 -0
- package/dist/ui/TextInput/index.js +545 -0
- package/dist/ui/WheelColumn/ZKitWheelPickerNativeComponent.d.ts +39 -0
- package/dist/ui/WheelColumn/ZKitWheelPickerNativeComponent.d.ts.map +1 -0
- package/dist/ui/WheelColumn/ZKitWheelPickerNativeComponent.js +60 -0
- package/dist/ui/WheelColumn/constants.d.ts +2 -0
- package/dist/ui/WheelColumn/constants.d.ts.map +1 -0
- package/dist/ui/WheelColumn/constants.js +4 -0
- package/dist/ui/WheelColumn/index.d.ts +59 -0
- package/dist/ui/WheelColumn/index.d.ts.map +1 -0
- package/dist/ui/WheelColumn/index.js +547 -0
- package/ios/ZKitWheelPicker.h +18 -0
- package/ios/ZKitWheelPicker.mm +230 -0
- package/ios/ZKitWheelPickerLabel.h +10 -0
- package/ios/ZKitWheelPickerLabel.mm +31 -0
- package/ios/ZKitWheelPickerManager.h +5 -0
- package/ios/ZKitWheelPickerManager.mm +53 -0
- package/package.json +75 -0
- package/react-native.config.js +10 -0
- package/src/ComponentLibProvider.tsx +56 -0
- package/src/assets/animations/spinner.json +1 -0
- package/src/assets/icons/check.svg +4 -0
- package/src/assets/icons/toast/error.webp +0 -0
- package/src/assets/icons/toast/success.webp +0 -0
- package/src/assets/icons/toast/warning.webp +0 -0
- package/src/config.ts +65 -0
- package/src/i18n/I18nProvider.tsx +69 -0
- package/src/i18n/locale.ts +108 -0
- package/src/i18n/locales/de.ts +67 -0
- package/src/i18n/locales/en-US.ts +67 -0
- package/src/i18n/locales/index.ts +19 -0
- package/src/i18n/locales/ja.ts +67 -0
- package/src/i18n/locales/zh-CN.ts +67 -0
- package/src/i18n/locales/zh-TW.ts +67 -0
- package/src/i18n/types.ts +21 -0
- package/src/i18n/useI18n.ts +7 -0
- package/src/index.ts +395 -0
- package/src/services/ActionDialogService/ActionDialog.tsx +756 -0
- package/src/services/ActionDialogService/README.md +146 -0
- package/src/services/ActionDialogService/index.tsx +39 -0
- package/src/services/ActionDialogService/service.tsx +355 -0
- package/src/services/ActionDialogService/shared.ts +204 -0
- package/src/services/ActionDialogService/types.ts +197 -0
- package/src/services/CardToastService/README.md +128 -0
- package/src/services/CardToastService/index.tsx +1284 -0
- package/src/services/ImagePreviewService/README.md +67 -0
- package/src/services/ImagePreviewService/index.tsx +2148 -0
- package/src/services/LoadingService/README.md +128 -0
- package/src/services/LoadingService/index.tsx +1113 -0
- package/src/services/PermissionPurposeDialogService/README.md +80 -0
- package/src/services/PermissionPurposeDialogService/index.tsx +449 -0
- package/src/services/PickerService/README.md +154 -0
- package/src/services/PickerService/index.tsx +410 -0
- package/src/theme/ThemeProvider.tsx +40 -0
- package/src/theme/defaultTheme.ts +30 -0
- package/src/theme/mergeTheme.ts +16 -0
- package/src/theme/types.ts +36 -0
- package/src/theme/useTheme.ts +10 -0
- package/src/types/svg.d.ts +9 -0
- package/src/types/vant-area-data.d.ts +19 -0
- package/src/ui/Accordion/README.md +67 -0
- package/src/ui/Accordion/index.tsx +1221 -0
- package/src/ui/AddressCascader/README.md +144 -0
- package/src/ui/AddressCascader/index.tsx +1084 -0
- package/src/ui/BetweenTime/README.md +490 -0
- package/src/ui/BetweenTime/index.tsx +1001 -0
- package/src/ui/Button/README.md +186 -0
- package/src/ui/Button/index.tsx +1356 -0
- package/src/ui/Checkbox/README.md +211 -0
- package/src/ui/Checkbox/index.tsx +1231 -0
- package/src/ui/DatePicker/README.md +117 -0
- package/src/ui/DatePicker/index.tsx +295 -0
- package/src/ui/DatePicker/types.ts +115 -0
- package/src/ui/DatePicker/utils.ts +405 -0
- package/src/ui/LinkedScroll/README.md +55 -0
- package/src/ui/LinkedScroll/index.tsx +809 -0
- package/src/ui/LoadingSpinner/index.tsx +104 -0
- package/src/ui/Picker/README.md +190 -0
- package/src/ui/Picker/actionBar.tsx +70 -0
- package/src/ui/Picker/index.tsx +694 -0
- package/src/ui/Picker/types.ts +94 -0
- package/src/ui/Picker/utils.ts +239 -0
- package/src/ui/Radio/README.md +207 -0
- package/src/ui/Radio/index.tsx +1117 -0
- package/src/ui/Sheet/README.md +82 -0
- package/src/ui/Sheet/index.tsx +1163 -0
- package/src/ui/Sheet/nativeBottom.tsx +683 -0
- package/src/ui/Sheet/nativeBottom.web.tsx +40 -0
- package/src/ui/Sheet/slots.tsx +75 -0
- package/src/ui/Sheet/types.ts +195 -0
- package/src/ui/SliderCaptcha/README.md +61 -0
- package/src/ui/SliderCaptcha/index.tsx +816 -0
- package/src/ui/Switch/README.md +143 -0
- package/src/ui/Switch/index.tsx +1092 -0
- package/src/ui/Text/README.md +157 -0
- package/src/ui/Text/index.tsx +450 -0
- package/src/ui/TextInput/README.md +197 -0
- package/src/ui/TextInput/TextInputPrimitive.tsx +44 -0
- package/src/ui/TextInput/index.tsx +942 -0
- package/src/ui/WheelColumn/README.md +104 -0
- package/src/ui/WheelColumn/ZKitWheelPickerNativeComponent.tsx +68 -0
- package/src/ui/WheelColumn/constants.ts +1 -0
- package/src/ui/WheelColumn/index.tsx +826 -0
- package/zkit-ui.podspec +19 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# ActionDialog
|
|
2
|
+
|
|
3
|
+
顶层操作对话框,提供两种入口:
|
|
4
|
+
|
|
5
|
+
- `ActionDialog`:声明式组件,支持 `open/defaultOpen/onOpenChange`
|
|
6
|
+
- `actionDialog`:全局命令式服务,适合路由守卫、请求确认、跨组件业务流程
|
|
7
|
+
|
|
8
|
+
`ComponentLibProvider` 已内置 `ActionDialogProvider`;只有单独使用 `actionDialog` 服务时才需要确认 Provider 已挂载。
|
|
9
|
+
|
|
10
|
+
## 设计取舍
|
|
11
|
+
|
|
12
|
+
- 声明式组件拥有状态模型;service 只调度宿主实例,避免半受控状态竞争。
|
|
13
|
+
- `message` 承载简单正文,`children` 承载复杂内容;不再使用含糊的 `content`。
|
|
14
|
+
- action 的 `role` 决定结果语义,`tone/variant` 决定视觉,不把业务结果和样式绑死。
|
|
15
|
+
- ActionDialog 只确认用户意图,不承接业务异步;确认后直接关闭,异步进度与结果态交给 `loading.promise()`。
|
|
16
|
+
- `footer.layout="auto"` 默认横排;3 个短标签也保持横排,长标签、自定义节点或更多动作才自动堆叠。需要固定样式时显式传 `row/stack/bar`。
|
|
17
|
+
- 默认同一时刻只显示一个全局 dialog;新调用会替换旧调用并以 `replace` 结算。需要串行展示时传 `collisionStrategy: 'queue'`。
|
|
18
|
+
- 默认不点蒙层关闭,Android back / iOS accessibility escape 可关闭;这是确认类弹窗更稳妥的默认行为。
|
|
19
|
+
- `actionDialog` 全局服务默认使用顶层 `inline` 宿主,避开 Android 触发时创建原生 Modal 窗口的延迟;声明式组件默认仍用 `modal`,也可按场景切到 `hostMode="inline"`。
|
|
20
|
+
- 默认过渡是低成本 `motion="fade"`,只做遮罩与卡片透明度变化;需要更明显的中心弹出感时再显式使用 `motion="scale"`。
|
|
21
|
+
|
|
22
|
+
## 声明式使用
|
|
23
|
+
|
|
24
|
+
```tsx
|
|
25
|
+
import { ActionDialog } from 'zkit-ui';
|
|
26
|
+
|
|
27
|
+
<ActionDialog
|
|
28
|
+
open={open}
|
|
29
|
+
onOpenChange={setOpen}
|
|
30
|
+
title="删除确认"
|
|
31
|
+
message="确定要删除这条记录吗?"
|
|
32
|
+
actions={[
|
|
33
|
+
{ key: 'cancel', role: 'cancel' },
|
|
34
|
+
{ key: 'delete', role: 'confirm', label: '删除', tone: 'danger' },
|
|
35
|
+
]}
|
|
36
|
+
/>;
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## 命令式确认
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
import { actionDialog, loading } from 'zkit-ui';
|
|
43
|
+
|
|
44
|
+
const confirmed = await actionDialog.confirm({
|
|
45
|
+
title: '删除确认',
|
|
46
|
+
message: '确定要删除这条记录吗?',
|
|
47
|
+
tone: 'danger',
|
|
48
|
+
confirmLabel: '删除',
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
if (confirmed) {
|
|
52
|
+
await loading.promise(remove(), {
|
|
53
|
+
loading: '删除中',
|
|
54
|
+
success: '已删除',
|
|
55
|
+
error: '删除失败',
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## 自定义动作
|
|
61
|
+
|
|
62
|
+
```tsx
|
|
63
|
+
const handle = actionDialog.open({
|
|
64
|
+
title: '离开页面',
|
|
65
|
+
message: '还有未保存内容,确认离开?',
|
|
66
|
+
actions: [
|
|
67
|
+
{ key: 'cancel', role: 'cancel' },
|
|
68
|
+
{
|
|
69
|
+
key: 'stay',
|
|
70
|
+
role: 'neutral',
|
|
71
|
+
label: '继续编辑',
|
|
72
|
+
closeOnPress: false,
|
|
73
|
+
onPress: ({ dismiss }) => {
|
|
74
|
+
dismiss();
|
|
75
|
+
return false;
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
key: 'leave',
|
|
80
|
+
role: 'confirm',
|
|
81
|
+
label: '离开',
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const result = await handle.result;
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
`onPress` 是同步拦截钩子;同步返回 `false` 会阻止默认关闭。业务异步不要放进 `onPress`,应在弹窗关闭并得到确认结果后用 `loading.promise()` 展示 loading / success / error。
|
|
90
|
+
|
|
91
|
+
## 自定义 Footer
|
|
92
|
+
|
|
93
|
+
```tsx
|
|
94
|
+
actionDialog.open({
|
|
95
|
+
title: '高级操作',
|
|
96
|
+
message: '底部区域可以完全接管,但仍复用 action 语义。',
|
|
97
|
+
actions: [
|
|
98
|
+
{ key: 'cancel', role: 'cancel' },
|
|
99
|
+
{ key: 'save', role: 'confirm', label: '保存' },
|
|
100
|
+
],
|
|
101
|
+
footer: {
|
|
102
|
+
render: ({ pressAction, close }) => (
|
|
103
|
+
<MyFooter onCancel={close} onSave={() => pressAction('save')} />
|
|
104
|
+
),
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## 常用 API
|
|
110
|
+
|
|
111
|
+
### `ActionDialog`
|
|
112
|
+
|
|
113
|
+
| 参数 | 说明 |
|
|
114
|
+
| --- | --- |
|
|
115
|
+
| `open/defaultOpen/onOpenChange` | 标准受控/非受控打开状态 |
|
|
116
|
+
| `title` | 标题区域 |
|
|
117
|
+
| `message` | 简单正文 |
|
|
118
|
+
| `children` | 复杂内容 |
|
|
119
|
+
| `actions` | 底部动作列表 |
|
|
120
|
+
| `footer.layout` | `'auto' \| 'row' \| 'stack' \| 'bar'`,默认 `auto` |
|
|
121
|
+
| `dismiss.overlayPress` | 点击蒙层关闭,默认 `false` |
|
|
122
|
+
| `dismiss.backPress` | Android back / accessibility escape 关闭,默认 `true` |
|
|
123
|
+
| `keyboard.avoid` | 键盘出现时避让,默认 `true` |
|
|
124
|
+
| `hostMode` | `'modal' \| 'inline'`,声明式默认 `modal` |
|
|
125
|
+
| `motion` | `'none' \| 'fade' \| 'scale'`,默认 `fade` |
|
|
126
|
+
| `layout` | `width/maxWidth/contentPadding/radius`,设计尺寸会通过 `wp(...)` 换算 |
|
|
127
|
+
| `colors/labels` | 颜色与文案 escape hatch |
|
|
128
|
+
|
|
129
|
+
### `actionDialog`
|
|
130
|
+
|
|
131
|
+
| 方法 | 说明 |
|
|
132
|
+
| --- | --- |
|
|
133
|
+
| `open(options)` | 打开自定义对话框,返回 `ActionDialogHandle` |
|
|
134
|
+
| `confirm(options)` | 打开确认框,确认返回 `true`,其它关闭返回 `false` |
|
|
135
|
+
| `alert(options)` | 打开提示框,关闭后 resolve |
|
|
136
|
+
| `close()` | 关闭当前对话框 |
|
|
137
|
+
| `closeByScope(scopeKey)` | 关闭命中 scope 的当前或排队对话框 |
|
|
138
|
+
| `closeAll()` | 关闭当前并清空队列 |
|
|
139
|
+
| `getSnapshot()` | 读取当前打开态、active id 与队列数量 |
|
|
140
|
+
|
|
141
|
+
`ActionDialogHandle` 包含:
|
|
142
|
+
|
|
143
|
+
- `id`
|
|
144
|
+
- `result: Promise<ActionDialogResult>`
|
|
145
|
+
- `close()`
|
|
146
|
+
- `update(patch)`
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export { ActionDialog } from './ActionDialog';
|
|
2
|
+
export { ActionDialogProvider, actionDialog } from './service';
|
|
3
|
+
export type {
|
|
4
|
+
ActionDialogAction,
|
|
5
|
+
ActionDialogActionContext,
|
|
6
|
+
ActionDialogActionPressResult,
|
|
7
|
+
ActionDialogActionResult,
|
|
8
|
+
ActionDialogActionRole,
|
|
9
|
+
ActionDialogActionTone,
|
|
10
|
+
ActionDialogActionVariant,
|
|
11
|
+
ActionDialogAlertOptions,
|
|
12
|
+
ActionDialogCollisionStrategy,
|
|
13
|
+
ActionDialogColors,
|
|
14
|
+
ActionDialogConfirmOptions,
|
|
15
|
+
ActionDialogDismissOptions,
|
|
16
|
+
ActionDialogDismissReason,
|
|
17
|
+
ActionDialogDismissResult,
|
|
18
|
+
ActionDialogFooterLayout,
|
|
19
|
+
ActionDialogFooterOptions,
|
|
20
|
+
ActionDialogFooterRenderContext,
|
|
21
|
+
ActionDialogHandle,
|
|
22
|
+
ActionDialogHostMode,
|
|
23
|
+
ActionDialogKeyboardOptions,
|
|
24
|
+
ActionDialogLabels,
|
|
25
|
+
ActionDialogLayerOptions,
|
|
26
|
+
ActionDialogLayoutOptions,
|
|
27
|
+
ActionDialogMotion,
|
|
28
|
+
ActionDialogOpenChangeMeta,
|
|
29
|
+
ActionDialogOpenChangeReason,
|
|
30
|
+
ActionDialogOpenOptions,
|
|
31
|
+
ActionDialogProps,
|
|
32
|
+
ActionDialogRef,
|
|
33
|
+
ActionDialogResolvedAction,
|
|
34
|
+
ActionDialogResolvedFooterLayout,
|
|
35
|
+
ActionDialogResult,
|
|
36
|
+
ActionDialogSemanticActionOptions,
|
|
37
|
+
ActionDialogService,
|
|
38
|
+
ActionDialogSnapshot,
|
|
39
|
+
} from './types';
|
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { ActionDialog } from './ActionDialog';
|
|
3
|
+
import { createSemanticAction, mergeOpenOptions } from './shared';
|
|
4
|
+
import type {
|
|
5
|
+
ActionDialogAlertOptions,
|
|
6
|
+
ActionDialogConfirmOptions,
|
|
7
|
+
ActionDialogDismissReason,
|
|
8
|
+
ActionDialogHandle,
|
|
9
|
+
ActionDialogOpenOptions,
|
|
10
|
+
ActionDialogResult,
|
|
11
|
+
ActionDialogService,
|
|
12
|
+
ActionDialogSnapshot,
|
|
13
|
+
} from './types';
|
|
14
|
+
|
|
15
|
+
type ActionDialogRequest = ActionDialogOpenOptions & {
|
|
16
|
+
id: string;
|
|
17
|
+
resolve: (result: ActionDialogResult) => void;
|
|
18
|
+
settled: boolean;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
type ActionDialogServiceState = {
|
|
22
|
+
activeRequest: ActionDialogRequest | null;
|
|
23
|
+
queuedRequests: ActionDialogRequest[];
|
|
24
|
+
open: boolean;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
type ActionDialogServiceController = {
|
|
28
|
+
open: (request: ActionDialogRequest) => void;
|
|
29
|
+
close: (id: string | undefined, reason: ActionDialogDismissReason) => void;
|
|
30
|
+
closeByScope: (scopeKey: string, reason: ActionDialogDismissReason) => void;
|
|
31
|
+
closeAll: (reason: ActionDialogDismissReason) => void;
|
|
32
|
+
update: (id: string, patch: Partial<ActionDialogOpenOptions>) => void;
|
|
33
|
+
getSnapshot: () => ActionDialogSnapshot;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const initialState: ActionDialogServiceState = {
|
|
37
|
+
activeRequest: null,
|
|
38
|
+
queuedRequests: [],
|
|
39
|
+
open: false,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
class ActionDialogServiceClass implements ActionDialogService {
|
|
43
|
+
private controller: ActionDialogServiceController | null = null;
|
|
44
|
+
private sequence = 0;
|
|
45
|
+
|
|
46
|
+
_bind(controller: ActionDialogServiceController | null) {
|
|
47
|
+
this.controller = controller;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
open(options: ActionDialogOpenOptions = {}): ActionDialogHandle {
|
|
51
|
+
const id = `action_dialog_${Date.now()}_${++this.sequence}`;
|
|
52
|
+
let resolveResult: (result: ActionDialogResult) => void = () => {};
|
|
53
|
+
const result = new Promise<ActionDialogResult>((resolve) => {
|
|
54
|
+
resolveResult = resolve;
|
|
55
|
+
});
|
|
56
|
+
const request: ActionDialogRequest = {
|
|
57
|
+
...options,
|
|
58
|
+
id,
|
|
59
|
+
resolve: resolveResult,
|
|
60
|
+
settled: false,
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const handle: ActionDialogHandle = {
|
|
64
|
+
id,
|
|
65
|
+
result,
|
|
66
|
+
close: () => this.controller?.close(id, 'api'),
|
|
67
|
+
update: (patch) => this.controller?.update(id, patch),
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
if (!this.controller) {
|
|
71
|
+
if (typeof __DEV__ !== 'undefined' && __DEV__) {
|
|
72
|
+
console.warn('[actionDialog] ActionDialogProvider is not mounted.');
|
|
73
|
+
}
|
|
74
|
+
this.settle(request, { type: 'dismiss', reason: 'unmount' });
|
|
75
|
+
return handle;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
this.controller.open(request);
|
|
79
|
+
return handle;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async confirm({
|
|
83
|
+
cancelAction,
|
|
84
|
+
cancelLabel,
|
|
85
|
+
confirmAction,
|
|
86
|
+
confirmLabel,
|
|
87
|
+
footer,
|
|
88
|
+
tone = 'default',
|
|
89
|
+
...restOptions
|
|
90
|
+
}: ActionDialogConfirmOptions = {}) {
|
|
91
|
+
const handle = this.open({
|
|
92
|
+
...restOptions,
|
|
93
|
+
footer: { layout: footer?.layout },
|
|
94
|
+
actions: [
|
|
95
|
+
createSemanticAction(cancelAction, cancelLabel, 'cancel', 'cancel', 'neutral'),
|
|
96
|
+
createSemanticAction(
|
|
97
|
+
confirmAction,
|
|
98
|
+
confirmLabel,
|
|
99
|
+
'confirm',
|
|
100
|
+
'confirm',
|
|
101
|
+
tone === 'danger' ? 'danger' : 'primary'
|
|
102
|
+
),
|
|
103
|
+
],
|
|
104
|
+
});
|
|
105
|
+
const result = await handle.result;
|
|
106
|
+
return result.type === 'action' && result.action.role === 'confirm';
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async alert({
|
|
110
|
+
confirmAction,
|
|
111
|
+
confirmLabel,
|
|
112
|
+
footer,
|
|
113
|
+
tone = 'default',
|
|
114
|
+
...restOptions
|
|
115
|
+
}: ActionDialogAlertOptions = {}) {
|
|
116
|
+
const handle = this.open({
|
|
117
|
+
...restOptions,
|
|
118
|
+
footer: { layout: footer?.layout },
|
|
119
|
+
actions: [
|
|
120
|
+
createSemanticAction(
|
|
121
|
+
confirmAction,
|
|
122
|
+
confirmLabel,
|
|
123
|
+
'confirm',
|
|
124
|
+
'confirm',
|
|
125
|
+
tone === 'danger' ? 'danger' : 'primary'
|
|
126
|
+
),
|
|
127
|
+
],
|
|
128
|
+
});
|
|
129
|
+
await handle.result;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
close() {
|
|
133
|
+
this.controller?.close(undefined, 'api');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
closeByScope(scopeKey: string) {
|
|
137
|
+
if (!scopeKey) return;
|
|
138
|
+
this.controller?.closeByScope(scopeKey, 'api');
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
closeAll() {
|
|
142
|
+
this.controller?.closeAll('api');
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
getSnapshot(): ActionDialogSnapshot {
|
|
146
|
+
return (
|
|
147
|
+
this.controller?.getSnapshot() ?? {
|
|
148
|
+
activeId: null,
|
|
149
|
+
open: false,
|
|
150
|
+
queuedCount: 0,
|
|
151
|
+
}
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
_settle(request: ActionDialogRequest, result: ActionDialogResult) {
|
|
156
|
+
this.settle(request, result);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
private settle(request: ActionDialogRequest, result: ActionDialogResult) {
|
|
160
|
+
if (request.settled) return;
|
|
161
|
+
request.settled = true;
|
|
162
|
+
request.onClose?.(result);
|
|
163
|
+
request.resolve(result);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const actionDialogService = new ActionDialogServiceClass();
|
|
168
|
+
|
|
169
|
+
export const actionDialog: ActionDialogService = actionDialogService;
|
|
170
|
+
|
|
171
|
+
function settleRequest(request: ActionDialogRequest | null | undefined, result: ActionDialogResult) {
|
|
172
|
+
if (!request) return;
|
|
173
|
+
actionDialogService._settle(request, result);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function ActionDialogProvider({ children }: { children: React.ReactNode }) {
|
|
177
|
+
const [state, setState] = React.useState<ActionDialogServiceState>(initialState);
|
|
178
|
+
const stateRef = React.useRef(state);
|
|
179
|
+
stateRef.current = state;
|
|
180
|
+
|
|
181
|
+
const showNextRequest = React.useCallback(() => {
|
|
182
|
+
setState((prev) => {
|
|
183
|
+
const [nextRequest, ...restQueue] = prev.queuedRequests;
|
|
184
|
+
if (!nextRequest) {
|
|
185
|
+
return {
|
|
186
|
+
activeRequest: null,
|
|
187
|
+
queuedRequests: [],
|
|
188
|
+
open: false,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return {
|
|
193
|
+
activeRequest: nextRequest,
|
|
194
|
+
queuedRequests: restQueue,
|
|
195
|
+
open: true,
|
|
196
|
+
};
|
|
197
|
+
});
|
|
198
|
+
}, []);
|
|
199
|
+
|
|
200
|
+
const controller = React.useMemo<ActionDialogServiceController>(
|
|
201
|
+
() => ({
|
|
202
|
+
open: (request) => {
|
|
203
|
+
setState((prev) => {
|
|
204
|
+
if (request.collisionStrategy === 'queue' && prev.activeRequest) {
|
|
205
|
+
return {
|
|
206
|
+
...prev,
|
|
207
|
+
queuedRequests: [...prev.queuedRequests, request],
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
settleRequest(prev.activeRequest, { type: 'dismiss', reason: 'replace' });
|
|
212
|
+
for (const queuedRequest of prev.queuedRequests) {
|
|
213
|
+
settleRequest(queuedRequest, { type: 'dismiss', reason: 'replace' });
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return {
|
|
217
|
+
activeRequest: request,
|
|
218
|
+
queuedRequests: [],
|
|
219
|
+
open: true,
|
|
220
|
+
};
|
|
221
|
+
});
|
|
222
|
+
},
|
|
223
|
+
close: (id, reason) => {
|
|
224
|
+
setState((prev) => {
|
|
225
|
+
if (id && prev.activeRequest?.id !== id) {
|
|
226
|
+
let changed = false;
|
|
227
|
+
const nextQueue = prev.queuedRequests.filter((request) => {
|
|
228
|
+
if (request.id !== id) return true;
|
|
229
|
+
changed = true;
|
|
230
|
+
settleRequest(request, { type: 'dismiss', reason });
|
|
231
|
+
return false;
|
|
232
|
+
});
|
|
233
|
+
return changed ? { ...prev, queuedRequests: nextQueue } : prev;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (!prev.activeRequest) return prev;
|
|
237
|
+
settleRequest(prev.activeRequest, { type: 'dismiss', reason });
|
|
238
|
+
return { ...prev, open: false };
|
|
239
|
+
});
|
|
240
|
+
},
|
|
241
|
+
closeByScope: (scopeKey, reason) => {
|
|
242
|
+
setState((prev) => {
|
|
243
|
+
let changed = false;
|
|
244
|
+
const nextQueue = prev.queuedRequests.filter((request) => {
|
|
245
|
+
if (request.scopeKey !== scopeKey) return true;
|
|
246
|
+
changed = true;
|
|
247
|
+
settleRequest(request, { type: 'dismiss', reason });
|
|
248
|
+
return false;
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
if (prev.activeRequest?.scopeKey === scopeKey) {
|
|
252
|
+
settleRequest(prev.activeRequest, { type: 'dismiss', reason });
|
|
253
|
+
return { ...prev, queuedRequests: nextQueue, open: false };
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return changed ? { ...prev, queuedRequests: nextQueue } : prev;
|
|
257
|
+
});
|
|
258
|
+
},
|
|
259
|
+
closeAll: (reason) => {
|
|
260
|
+
setState((prev) => {
|
|
261
|
+
settleRequest(prev.activeRequest, { type: 'dismiss', reason });
|
|
262
|
+
for (const queuedRequest of prev.queuedRequests) {
|
|
263
|
+
settleRequest(queuedRequest, { type: 'dismiss', reason });
|
|
264
|
+
}
|
|
265
|
+
return {
|
|
266
|
+
activeRequest: prev.activeRequest,
|
|
267
|
+
queuedRequests: [],
|
|
268
|
+
open: prev.activeRequest ? false : prev.open,
|
|
269
|
+
};
|
|
270
|
+
});
|
|
271
|
+
},
|
|
272
|
+
update: (id, patch) => {
|
|
273
|
+
setState((prev) => {
|
|
274
|
+
if (prev.activeRequest?.id === id) {
|
|
275
|
+
return {
|
|
276
|
+
...prev,
|
|
277
|
+
activeRequest: {
|
|
278
|
+
...mergeOpenOptions(prev.activeRequest, patch),
|
|
279
|
+
id,
|
|
280
|
+
resolve: prev.activeRequest.resolve,
|
|
281
|
+
settled: prev.activeRequest.settled,
|
|
282
|
+
},
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
let changed = false;
|
|
287
|
+
const queuedRequests = prev.queuedRequests.map((request) => {
|
|
288
|
+
if (request.id !== id) return request;
|
|
289
|
+
changed = true;
|
|
290
|
+
return {
|
|
291
|
+
...mergeOpenOptions(request, patch),
|
|
292
|
+
id,
|
|
293
|
+
resolve: request.resolve,
|
|
294
|
+
settled: request.settled,
|
|
295
|
+
};
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
return changed ? { ...prev, queuedRequests } : prev;
|
|
299
|
+
});
|
|
300
|
+
},
|
|
301
|
+
getSnapshot: () => ({
|
|
302
|
+
activeId: stateRef.current.activeRequest?.id ?? null,
|
|
303
|
+
open: stateRef.current.open,
|
|
304
|
+
queuedCount: stateRef.current.queuedRequests.length,
|
|
305
|
+
}),
|
|
306
|
+
}),
|
|
307
|
+
[]
|
|
308
|
+
);
|
|
309
|
+
|
|
310
|
+
React.useEffect(() => {
|
|
311
|
+
actionDialogService._bind(controller);
|
|
312
|
+
|
|
313
|
+
return () => {
|
|
314
|
+
const currentState = stateRef.current;
|
|
315
|
+
settleRequest(currentState.activeRequest, { type: 'dismiss', reason: 'unmount' });
|
|
316
|
+
for (const queuedRequest of currentState.queuedRequests) {
|
|
317
|
+
settleRequest(queuedRequest, { type: 'dismiss', reason: 'unmount' });
|
|
318
|
+
}
|
|
319
|
+
actionDialogService._bind(null);
|
|
320
|
+
};
|
|
321
|
+
}, [controller]);
|
|
322
|
+
|
|
323
|
+
const handleClose = React.useCallback((result: ActionDialogResult) => {
|
|
324
|
+
setState((prev) => {
|
|
325
|
+
if (!prev.activeRequest) return prev;
|
|
326
|
+
settleRequest(prev.activeRequest, result);
|
|
327
|
+
return { ...prev, open: false };
|
|
328
|
+
});
|
|
329
|
+
}, []);
|
|
330
|
+
|
|
331
|
+
const activeRequest = state.activeRequest;
|
|
332
|
+
const activeDialogProps = React.useMemo(() => {
|
|
333
|
+
if (!activeRequest) return {};
|
|
334
|
+
const { collisionStrategy, id, resolve, scopeKey, settled, ...props } = activeRequest;
|
|
335
|
+
void collisionStrategy;
|
|
336
|
+
void id;
|
|
337
|
+
void resolve;
|
|
338
|
+
void scopeKey;
|
|
339
|
+
void settled;
|
|
340
|
+
return props;
|
|
341
|
+
}, [activeRequest]);
|
|
342
|
+
|
|
343
|
+
return (
|
|
344
|
+
<>
|
|
345
|
+
{children}
|
|
346
|
+
<ActionDialog
|
|
347
|
+
{...activeDialogProps}
|
|
348
|
+
hostMode={activeRequest?.hostMode ?? 'inline'}
|
|
349
|
+
open={Boolean(activeRequest && state.open)}
|
|
350
|
+
onClose={handleClose}
|
|
351
|
+
onDismissComplete={showNextRequest}
|
|
352
|
+
/>
|
|
353
|
+
</>
|
|
354
|
+
);
|
|
355
|
+
}
|