cabloy 5.1.166 → 5.1.167
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/.cabloy-version +1 -1
- package/.claude/scheduled_tasks.lock +1 -1
- package/.claude/skills/cabloy-frontend-scaffold/SKILL.md +1 -0
- package/CHANGELOG.md +13 -0
- package/package.json +1 -1
- package/repo-docs/frontend/page-params-guide.md +1 -1
- package/repo-docs/frontend/page-query-guide.md +6 -0
- package/repo-docs/frontend/zod-guide.md +9 -7
- package/repo-e2e/specs/routed-dialog.spec.ts +368 -0
- package/vona/pnpm-lock.yaml +6 -0
- package/zova/package.original.json +1 -1
- package/zova/packages-cli/cli/package.json +2 -2
- package/zova/packages-cli/cli-set-front/package.json +1 -1
- package/zova/packages-cli/cli-set-front/src/lib/bean/toolsMetadata/generateBeanGenerals.ts +1 -1
- package/zova/packages-cli/cli-set-front/src/lib/bean/toolsMetadata/generateScopeResources.ts +1 -1
- package/zova/packages-zova/zova/package.json +2 -2
- package/zova/pnpm-lock.yaml +2014 -2053
- package/zova/pnpm-workspace.yaml +1 -1
- package/zova/src/suite/a-demo/modules/demo-basic/src/.metadata/index.ts +66 -18
- package/zova/src/suite/a-demo/modules/demo-basic/src/.metadata/page/routedDialog.ts +9 -0
- package/zova/src/suite/a-demo/modules/demo-basic/src/.metadata/page/routedDialogDetail.ts +23 -0
- package/zova/src/suite/a-demo/modules/demo-basic/src/.metadata/page/routedDialogEntry.ts +23 -0
- package/zova/src/suite/a-demo/modules/demo-basic/src/page/routedDialog/controller.tsx +301 -0
- package/zova/src/suite/a-demo/modules/demo-basic/src/page/routedDialogDetail/controller.tsx +135 -0
- package/zova/src/suite/a-demo/modules/demo-basic/src/page/routedDialogEntry/controller.tsx +108 -0
- package/zova/src/suite/a-demo/modules/demo-basic/src/routes.ts +19 -0
- package/zova/src/suite/cabloy-basic/modules/basic-app/src/.metadata/component/routedDialog.ts +63 -0
- package/zova/src/suite/cabloy-basic/modules/basic-app/src/.metadata/index.ts +44 -2
- package/zova/src/suite/cabloy-basic/modules/basic-app/src/bean/behavior.appModal.tsx +306 -18
- package/zova/src/suite/cabloy-basic/modules/basic-app/src/component/routedDialog/controller.tsx +51 -0
- package/zova/src/suite/cabloy-basic/modules/basic-app/src/config/config.ts +19 -0
- package/zova/src/suite/cabloy-basic/modules/basic-app/src/config/locale/en-us.ts +1 -0
- package/zova/src/suite/cabloy-basic/modules/basic-app/src/config/locale/zh-cn.ts +1 -0
- package/zova/src/suite/cabloy-basic/modules/basic-app/src/lib/routedDialogHistory.ts +76 -0
- package/zova/src/suite/cabloy-basic/modules/basic-app/src/service/appModal.ts +156 -1
- package/zova/src/suite/cabloy-basic/modules/basic-app/src/types/appModal.ts +92 -13
- package/zova/src/suite/cabloy-basic/modules/basic-app/test/lib/routedDialogHistory.test.ts +92 -0
- package/zova/src/suite/cabloy-basic/modules/basic-app/test/lib/routedDialogRouter.test.ts +35 -0
- package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/package.json +1 -1
- package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/model/resource.ts +1 -1
- package/zova/src/suite-vendor/a-cabloy/package.json +2 -2
- package/zova/src/suite-vendor/a-zova/modules/a-router/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-router/src/.metadata/index.ts +6 -6
- package/zova/src/suite-vendor/a-zova/modules/a-router/src/bean/bean.router.ts +60 -3
- package/zova/src/suite-vendor/a-zova/modules/a-router/src/bean/sys.router.ts +12 -0
- package/zova/src/suite-vendor/a-zova/modules/a-router/src/component/routerViewEmpty/controller.tsx +4 -1
- package/zova/src/suite-vendor/a-zova/modules/a-router/src/lib/routerViewBase.tsx +5 -1
- package/zova/src/suite-vendor/a-zova/package.json +2 -2
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { BeanControllerPageBase } from 'zova';
|
|
3
|
+
import { Controller } from 'zova-module-a-bean';
|
|
4
|
+
import { ZPage } from 'zova-module-home-base';
|
|
5
|
+
|
|
6
|
+
export const ControllerPageRoutedDialogEntrySchemaParams = z.object({});
|
|
7
|
+
|
|
8
|
+
export const ControllerPageRoutedDialogEntrySchemaQuery = z.object({
|
|
9
|
+
dialog: z.enum(['A', 'B']).optional().default('A'),
|
|
10
|
+
token: z.string().optional().default('entry'),
|
|
11
|
+
via: z.string().optional().default('open'),
|
|
12
|
+
step: z.number().optional().default(0),
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
@Controller()
|
|
16
|
+
export class ControllerPageRoutedDialogEntry extends BeanControllerPageBase {
|
|
17
|
+
pushDetail() {
|
|
18
|
+
const route = this.$router.getPagePath('/demo/basic/routedDialogDetail/:id', {
|
|
19
|
+
params: { id: this.$query.step + 1 },
|
|
20
|
+
query: {
|
|
21
|
+
...this.$query,
|
|
22
|
+
token: `${this.$query.token}-push`,
|
|
23
|
+
via: 'page-push',
|
|
24
|
+
step: this.$query.step + 1,
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
this.$router.push(route);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
replaceDetail() {
|
|
31
|
+
const route = this.$router.getPagePath('/demo/basic/routedDialogDetail/:id', {
|
|
32
|
+
params: { id: this.$query.step + 10 },
|
|
33
|
+
query: {
|
|
34
|
+
...this.$query,
|
|
35
|
+
token: `${this.$query.token}-replace`,
|
|
36
|
+
via: 'page-replace',
|
|
37
|
+
step: this.$query.step + 10,
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
this.$router.replace(route);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
protected render() {
|
|
44
|
+
return (
|
|
45
|
+
<ZPage>
|
|
46
|
+
<div class="flex flex-col gap-4 p-1">
|
|
47
|
+
<div>
|
|
48
|
+
<h1 class="text-xl font-bold">Routed Dialog Entry</h1>
|
|
49
|
+
<p class="text-base-content/70">This page is rendered by the dialog-local router.</p>
|
|
50
|
+
</div>
|
|
51
|
+
|
|
52
|
+
<div class="overflow-x-auto">
|
|
53
|
+
<table class="table">
|
|
54
|
+
<thead>
|
|
55
|
+
<tr>
|
|
56
|
+
<th>Name</th>
|
|
57
|
+
<th>Value</th>
|
|
58
|
+
<th>Type</th>
|
|
59
|
+
</tr>
|
|
60
|
+
</thead>
|
|
61
|
+
<tbody>
|
|
62
|
+
<tr>
|
|
63
|
+
<td>$pageRoute.fullPath</td>
|
|
64
|
+
<td class="break-all">{this.$pageRoute?.fullPath}</td>
|
|
65
|
+
<td>string</td>
|
|
66
|
+
</tr>
|
|
67
|
+
<tr>
|
|
68
|
+
<td>$route.fullPath</td>
|
|
69
|
+
<td class="break-all">{this.$route.fullPath}</td>
|
|
70
|
+
<td>string</td>
|
|
71
|
+
</tr>
|
|
72
|
+
<tr>
|
|
73
|
+
<td>$query.dialog</td>
|
|
74
|
+
<td>{this.$query.dialog}</td>
|
|
75
|
+
<td>{typeof this.$query.dialog}</td>
|
|
76
|
+
</tr>
|
|
77
|
+
<tr>
|
|
78
|
+
<td>$query.token</td>
|
|
79
|
+
<td>{this.$query.token}</td>
|
|
80
|
+
<td>{typeof this.$query.token}</td>
|
|
81
|
+
</tr>
|
|
82
|
+
<tr>
|
|
83
|
+
<td>$query.via</td>
|
|
84
|
+
<td>{this.$query.via}</td>
|
|
85
|
+
<td>{typeof this.$query.via}</td>
|
|
86
|
+
</tr>
|
|
87
|
+
<tr>
|
|
88
|
+
<td>$query.step</td>
|
|
89
|
+
<td>{this.$query.step}</td>
|
|
90
|
+
<td>{typeof this.$query.step}</td>
|
|
91
|
+
</tr>
|
|
92
|
+
</tbody>
|
|
93
|
+
</table>
|
|
94
|
+
</div>
|
|
95
|
+
|
|
96
|
+
<div class="flex flex-wrap gap-2">
|
|
97
|
+
<button class="btn btn-primary" onClick={() => this.pushDetail()}>
|
|
98
|
+
Local push to detail
|
|
99
|
+
</button>
|
|
100
|
+
<button class="btn btn-secondary" onClick={() => this.replaceDetail()}>
|
|
101
|
+
Local replace to detail
|
|
102
|
+
</button>
|
|
103
|
+
</div>
|
|
104
|
+
</div>
|
|
105
|
+
</ZPage>
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -2,6 +2,9 @@ import type { IModuleRoute } from 'zova-module-a-router';
|
|
|
2
2
|
|
|
3
3
|
import { ZPageComponent } from './.metadata/page/component.js';
|
|
4
4
|
import { ZPageLocale } from './.metadata/page/locale.js';
|
|
5
|
+
import { ZPageRoutedDialog } from './.metadata/page/routedDialog.js';
|
|
6
|
+
import { ZPageRoutedDialogDetail } from './.metadata/page/routedDialogDetail.js';
|
|
7
|
+
import { ZPageRoutedDialogEntry } from './.metadata/page/routedDialogEntry.js';
|
|
5
8
|
import { ZPageRouteParams } from './.metadata/page/routeParams.js';
|
|
6
9
|
import { ZPageRouteQuery } from './.metadata/page/routeQuery.js';
|
|
7
10
|
import { ZPageRouteQueryB } from './.metadata/page/routeQueryB.js';
|
|
@@ -70,4 +73,20 @@ export const routes: IModuleRoute[] = [
|
|
|
70
73
|
ssrProfile: 'public',
|
|
71
74
|
},
|
|
72
75
|
},
|
|
76
|
+
{
|
|
77
|
+
path: 'routedDialog',
|
|
78
|
+
component: ZPageRoutedDialog,
|
|
79
|
+
meta: { requiresAuth: false },
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
path: 'routedDialogEntry',
|
|
83
|
+
component: ZPageRoutedDialogEntry,
|
|
84
|
+
meta: { requiresAuth: false },
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
name: 'routedDialogDetail',
|
|
88
|
+
path: 'routedDialogDetail/:id',
|
|
89
|
+
component: ZPageRoutedDialogDetail,
|
|
90
|
+
meta: { requiresAuth: false },
|
|
91
|
+
},
|
|
73
92
|
];
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { TypeControllerInnerProps } from 'zova';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
routeLocationKey,
|
|
5
|
+
routerKey,
|
|
6
|
+
routerViewLocationKey,
|
|
7
|
+
viewDepthKey,
|
|
8
|
+
} from '@cabloy/vue-router';
|
|
9
|
+
import { computed, defineComponent, provide, shallowReactive } from 'vue';
|
|
10
|
+
import { prepareComponentOptions, useController } from 'zova';
|
|
11
|
+
|
|
12
|
+
import type { ControllerRoutedDialogProps } from '../../component/routedDialog/controller.jsx';
|
|
13
|
+
|
|
14
|
+
import { ControllerRoutedDialog } from '../../component/routedDialog/controller.jsx';
|
|
15
|
+
export type ZRoutedDialogProps = {
|
|
16
|
+
controllerRef?: (ref: ControllerRoutedDialog) => void;
|
|
17
|
+
} & ControllerRoutedDialogProps;
|
|
18
|
+
|
|
19
|
+
type ControllerInnerProps = TypeControllerInnerProps<
|
|
20
|
+
ControllerRoutedDialogProps,
|
|
21
|
+
keyof typeof ControllerRoutedDialog.$propsDefault
|
|
22
|
+
>;
|
|
23
|
+
declare module 'zova-module-basic-app' {
|
|
24
|
+
export interface ControllerRoutedDialog {
|
|
25
|
+
$props: ControllerInnerProps;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const ZRoutedDialog = defineComponent(
|
|
30
|
+
(props: ZRoutedDialogProps) => {
|
|
31
|
+
const router = props.item.state.router;
|
|
32
|
+
if (router) {
|
|
33
|
+
const currentRoute = router.currentRoute;
|
|
34
|
+
const reactiveRoute = {} as Record<string, unknown>;
|
|
35
|
+
for (const key in currentRoute.value) {
|
|
36
|
+
Object.defineProperty(reactiveRoute, key, {
|
|
37
|
+
get: () => currentRoute.value[key],
|
|
38
|
+
enumerable: true,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
provide(routerKey, router.router);
|
|
42
|
+
provide(routeLocationKey, shallowReactive(reactiveRoute) as any);
|
|
43
|
+
provide(routerViewLocationKey, currentRoute);
|
|
44
|
+
provide(
|
|
45
|
+
viewDepthKey,
|
|
46
|
+
computed(() => router.getEmbeddedRouterViewDepth(currentRoute.value)),
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
useController(ControllerRoutedDialog, undefined, undefined);
|
|
50
|
+
return () => {};
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
...prepareComponentOptions(ControllerRoutedDialog.$componentOptions),
|
|
54
|
+
props: {
|
|
55
|
+
item: { type: Object, required: true },
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
);
|
|
59
|
+
declare module 'zova-module-a-bean' {
|
|
60
|
+
export interface IVonaComponentRecord {
|
|
61
|
+
'basic-app:routedDialog': ControllerRoutedDialogProps;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -26,7 +26,7 @@ declare module 'zova-module-basic-app' {
|
|
|
26
26
|
}
|
|
27
27
|
/** service: end */
|
|
28
28
|
/** service: begin */
|
|
29
|
-
import { ServiceAppModal } from '../service/appModal.js';
|
|
29
|
+
import type { ServiceAppModal } from '../service/appModal.js';
|
|
30
30
|
import 'zova';
|
|
31
31
|
declare module 'zova' {
|
|
32
32
|
export interface IBeanRecordGeneral {
|
|
@@ -34,6 +34,48 @@ declare module 'zova' {
|
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
/** service: end */
|
|
37
|
+
/** controller: begin */
|
|
38
|
+
export * from '../component/routedDialog/controller.jsx';
|
|
39
|
+
|
|
40
|
+
import 'zova';
|
|
41
|
+
declare module 'zova' {
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
}
|
|
45
|
+
declare module 'zova-module-basic-app' {
|
|
46
|
+
|
|
47
|
+
export interface ControllerRoutedDialog {
|
|
48
|
+
/** @internal */
|
|
49
|
+
get scope(): ScopeModuleBasicApp;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/** controller: end */
|
|
53
|
+
/** controller: begin */
|
|
54
|
+
import type { ControllerRoutedDialog } from '../component/routedDialog/controller.jsx';
|
|
55
|
+
import 'zova';
|
|
56
|
+
declare module 'zova' {
|
|
57
|
+
export interface IBeanRecordLocal {
|
|
58
|
+
'basic-app.controller.routedDialog': ControllerRoutedDialog;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/** controller: end */
|
|
62
|
+
|
|
63
|
+
/** components: begin */
|
|
64
|
+
export * from './component/routedDialog.js';
|
|
65
|
+
import { ZRoutedDialog } from './component/routedDialog.js';
|
|
66
|
+
export const components = {
|
|
67
|
+
'routedDialog': ZRoutedDialog,
|
|
68
|
+
};
|
|
69
|
+
import 'zova';
|
|
70
|
+
declare module 'zova' {
|
|
71
|
+
export interface IComponentRecord {
|
|
72
|
+
'basic-app:routedDialog': ControllerRoutedDialog;
|
|
73
|
+
}
|
|
74
|
+
export interface IZovaComponentRecord {
|
|
75
|
+
'basic-app:routedDialog': typeof ZRoutedDialog;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/** components: end */
|
|
37
79
|
/** behavior: begin */
|
|
38
80
|
export * from '../bean/behavior.appModal.jsx';
|
|
39
81
|
import { IBehaviorOptionsAppModal } from '../bean/behavior.appModal.jsx';
|
|
@@ -61,7 +103,7 @@ declare module 'zova-module-basic-app' {
|
|
|
61
103
|
}
|
|
62
104
|
/** behavior: end */
|
|
63
105
|
/** behavior: begin */
|
|
64
|
-
import { BehaviorAppModal } from '../bean/behavior.appModal.jsx';
|
|
106
|
+
import type { BehaviorAppModal } from '../bean/behavior.appModal.jsx';
|
|
65
107
|
import 'zova';
|
|
66
108
|
declare module 'zova' {
|
|
67
109
|
export interface IBeanRecordLocal {
|
|
@@ -15,7 +15,14 @@ import {
|
|
|
15
15
|
IModalItem,
|
|
16
16
|
IModalMessageOptions,
|
|
17
17
|
IModalPromptOptionsInner,
|
|
18
|
+
IModalResponsiveMaxWidth,
|
|
19
|
+
IModalResponsiveTopGutter,
|
|
20
|
+
IModalRoutedDialogItem,
|
|
21
|
+
IModalRoutedDialogPresentationOptions,
|
|
22
|
+
ModalRoutedDialogMaxWidth,
|
|
23
|
+
ModalRoutedDialogTopGutter,
|
|
18
24
|
ModalType,
|
|
25
|
+
ModalWidth,
|
|
19
26
|
} from '../types/appModal.js';
|
|
20
27
|
|
|
21
28
|
export interface IBehaviorPropsInputAppModal {}
|
|
@@ -24,17 +31,92 @@ export interface IBehaviorPropsOutputAppModal extends IBehaviorPropsInputAppModa
|
|
|
24
31
|
|
|
25
32
|
export interface IBehaviorOptionsAppModal extends IDecoratorBehaviorOptions {}
|
|
26
33
|
|
|
34
|
+
interface IPreparedDialogOptions extends Omit<IModalDialogOptions, 'maxWidth'> {
|
|
35
|
+
maxWidth?: ModalRoutedDialogMaxWidth;
|
|
36
|
+
topGutter?: ModalRoutedDialogTopGutter;
|
|
37
|
+
showBackButton?: boolean;
|
|
38
|
+
}
|
|
39
|
+
|
|
27
40
|
interface IRenderDialogBaseOptions {
|
|
28
41
|
modalItem: IModalItem;
|
|
29
|
-
dialogOptions:
|
|
42
|
+
dialogOptions: IPreparedDialogOptions;
|
|
30
43
|
iconName?: keyof IIconRecord;
|
|
31
44
|
title: string;
|
|
32
45
|
body?: VNode;
|
|
33
46
|
actions?: VNode;
|
|
47
|
+
showBackButton?: boolean;
|
|
34
48
|
showCloseButton?: boolean;
|
|
49
|
+
onBack?: () => void;
|
|
35
50
|
onClose: () => void;
|
|
36
51
|
}
|
|
37
52
|
|
|
53
|
+
interface IModalCardPresentation {
|
|
54
|
+
className?: string;
|
|
55
|
+
style?: Record<string, string>;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
interface IModalOuterPresentation {
|
|
59
|
+
className?: string;
|
|
60
|
+
style?: Record<string, string>;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const responsiveMaxWidthClass = {
|
|
64
|
+
maxWidth: 'var(--zova-routed-dialog-max-width-default)',
|
|
65
|
+
$nest: {
|
|
66
|
+
'@media (min-width: 48rem)': {
|
|
67
|
+
maxWidth:
|
|
68
|
+
'var(--zova-routed-dialog-max-width-md, var(--zova-routed-dialog-max-width-default))',
|
|
69
|
+
},
|
|
70
|
+
'@media (min-width: 64rem)': {
|
|
71
|
+
maxWidth:
|
|
72
|
+
'var(--zova-routed-dialog-max-width-lg, var(--zova-routed-dialog-max-width-md, var(--zova-routed-dialog-max-width-default)))',
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const responsiveTopGutterClass = {
|
|
78
|
+
paddingTop: 'var(--zova-routed-dialog-top-gutter-default)',
|
|
79
|
+
$nest: {
|
|
80
|
+
'@media (min-width: 48rem)': {
|
|
81
|
+
paddingTop:
|
|
82
|
+
'var(--zova-routed-dialog-top-gutter-md, var(--zova-routed-dialog-top-gutter-default))',
|
|
83
|
+
},
|
|
84
|
+
'@media (min-width: 64rem)': {
|
|
85
|
+
paddingTop:
|
|
86
|
+
'var(--zova-routed-dialog-top-gutter-lg, var(--zova-routed-dialog-top-gutter-md, var(--zova-routed-dialog-top-gutter-default)))',
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const routedDialogMaxHeightClass = {
|
|
92
|
+
maxHeight:
|
|
93
|
+
'min(var(--zova-routed-dialog-max-height, 100%), calc(100vh - var(--zova-routed-dialog-top-gutter-default) - 1rem))',
|
|
94
|
+
$nest: {
|
|
95
|
+
'@media (min-width: 48rem)': {
|
|
96
|
+
maxHeight:
|
|
97
|
+
'min(var(--zova-routed-dialog-max-height, 100%), calc(100vh - var(--zova-routed-dialog-top-gutter-md, var(--zova-routed-dialog-top-gutter-default)) - 1rem))',
|
|
98
|
+
},
|
|
99
|
+
'@media (min-width: 64rem)': {
|
|
100
|
+
maxHeight:
|
|
101
|
+
'min(var(--zova-routed-dialog-max-height, 100%), calc(100vh - var(--zova-routed-dialog-top-gutter-lg, var(--zova-routed-dialog-top-gutter-md, var(--zova-routed-dialog-top-gutter-default))) - 1rem))',
|
|
102
|
+
},
|
|
103
|
+
'@supports (height: 100dvh)': {
|
|
104
|
+
maxHeight:
|
|
105
|
+
'min(var(--zova-routed-dialog-max-height, 100%), calc(100dvh - var(--zova-routed-dialog-top-gutter-default) - 1rem))',
|
|
106
|
+
$nest: {
|
|
107
|
+
'@media (min-width: 48rem)': {
|
|
108
|
+
maxHeight:
|
|
109
|
+
'min(var(--zova-routed-dialog-max-height, 100%), calc(100dvh - var(--zova-routed-dialog-top-gutter-md, var(--zova-routed-dialog-top-gutter-default)) - 1rem))',
|
|
110
|
+
},
|
|
111
|
+
'@media (min-width: 64rem)': {
|
|
112
|
+
maxHeight:
|
|
113
|
+
'min(var(--zova-routed-dialog-max-height, 100%), calc(100dvh - var(--zova-routed-dialog-top-gutter-lg, var(--zova-routed-dialog-top-gutter-md, var(--zova-routed-dialog-top-gutter-default))) - 1rem))',
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
|
|
38
120
|
@Behavior<IBehaviorOptionsAppModal>()
|
|
39
121
|
export class BehaviorAppModal extends BeanBehaviorBase<
|
|
40
122
|
IBehaviorOptionsAppModal,
|
|
@@ -108,6 +190,7 @@ export class BehaviorAppModal extends BeanBehaviorBase<
|
|
|
108
190
|
if (modalItem.type === 'alert') return this._renderAppModalAlert(modalItem);
|
|
109
191
|
if (modalItem.type === 'confirm') return this._renderAppModalConfirm(modalItem);
|
|
110
192
|
if (modalItem.type === 'prompt') return this._renderAppModalPrompt(modalItem);
|
|
193
|
+
if (modalItem.type === 'routedDialog') return this._renderAppModalRoutedDialog(modalItem);
|
|
111
194
|
return this._renderAppModalDialog(modalItem);
|
|
112
195
|
}
|
|
113
196
|
|
|
@@ -249,6 +332,41 @@ export class BehaviorAppModal extends BeanBehaviorBase<
|
|
|
249
332
|
});
|
|
250
333
|
}
|
|
251
334
|
|
|
335
|
+
private _renderAppModalRoutedDialog(modalItem: IModalRoutedDialogItem) {
|
|
336
|
+
const options = modalItem.options;
|
|
337
|
+
const dialogOptions = this._prepareDialogOptions(
|
|
338
|
+
modalItem.type,
|
|
339
|
+
modalItem.dialogOptions,
|
|
340
|
+
options,
|
|
341
|
+
);
|
|
342
|
+
const title = options.title ?? this.sys.env.APP_TITLE ?? '';
|
|
343
|
+
const state = modalItem.state;
|
|
344
|
+
let body: VNode;
|
|
345
|
+
if (state.status === 'error') {
|
|
346
|
+
body = <div class="text-error whitespace-pre-wrap">{String(state.error)}</div>;
|
|
347
|
+
} else if (state.status === 'ready' && state.router) {
|
|
348
|
+
const RoutedDialog = this.$zovaComponent('basic-app:routedDialog');
|
|
349
|
+
body = <RoutedDialog item={modalItem}></RoutedDialog>;
|
|
350
|
+
} else {
|
|
351
|
+
body = <div class="loading loading-spinner"></div>;
|
|
352
|
+
}
|
|
353
|
+
return this._renderDialogBase({
|
|
354
|
+
modalItem,
|
|
355
|
+
dialogOptions,
|
|
356
|
+
iconName: options.icon,
|
|
357
|
+
title,
|
|
358
|
+
body,
|
|
359
|
+
showBackButton: dialogOptions.showBackButton && state.status === 'ready' && state.canGoBack,
|
|
360
|
+
showCloseButton: dialogOptions.showCloseButton,
|
|
361
|
+
onBack: () => {
|
|
362
|
+
state.router?.back();
|
|
363
|
+
},
|
|
364
|
+
onClose: () => {
|
|
365
|
+
this.$appModal.close(modalItem.id, 'button');
|
|
366
|
+
},
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
|
|
252
370
|
private _renderAppModalDialog(modalItem: IModalItem) {
|
|
253
371
|
const options = modalItem.options as IModalDialogRenderOptions | undefined;
|
|
254
372
|
const dialogOptions = this._prepareDialogOptions(modalItem.type, modalItem.dialogOptions);
|
|
@@ -275,12 +393,27 @@ export class BehaviorAppModal extends BeanBehaviorBase<
|
|
|
275
393
|
title,
|
|
276
394
|
body,
|
|
277
395
|
actions,
|
|
396
|
+
showBackButton,
|
|
278
397
|
showCloseButton,
|
|
398
|
+
onBack,
|
|
279
399
|
onClose,
|
|
280
400
|
}: IRenderDialogBaseOptions) {
|
|
281
|
-
const
|
|
401
|
+
const routedDialog = modalItem.type === 'routedDialog';
|
|
402
|
+
const presentation = this._dialogPresentation(dialogOptions, routedDialog);
|
|
403
|
+
const outerPresentation = routedDialog
|
|
404
|
+
? this._routedDialogOuterPresentation(dialogOptions)
|
|
405
|
+
: undefined;
|
|
282
406
|
return (
|
|
283
|
-
<div
|
|
407
|
+
<div
|
|
408
|
+
key={modalItem.id}
|
|
409
|
+
class={this.$cssMerge(
|
|
410
|
+
routedDialog
|
|
411
|
+
? 'fixed inset-0 z-50 flex items-start justify-center overflow-y-auto px-4 pb-4'
|
|
412
|
+
: 'fixed inset-0 z-50 flex items-center justify-center p-4',
|
|
413
|
+
outerPresentation?.className,
|
|
414
|
+
)}
|
|
415
|
+
style={outerPresentation?.style}
|
|
416
|
+
>
|
|
284
417
|
<div
|
|
285
418
|
class="absolute inset-0 bg-base-content/30"
|
|
286
419
|
onClick={() => {
|
|
@@ -292,11 +425,29 @@ export class BehaviorAppModal extends BeanBehaviorBase<
|
|
|
292
425
|
<div
|
|
293
426
|
role="dialog"
|
|
294
427
|
aria-modal="true"
|
|
295
|
-
class=
|
|
296
|
-
|
|
428
|
+
class={this.$cssMerge(
|
|
429
|
+
routedDialog
|
|
430
|
+
? 'card bg-base-100 shadow-2xl relative w-full'
|
|
431
|
+
: 'card bg-base-100 shadow-2xl relative w-full max-h-[calc(100vh-2rem)]',
|
|
432
|
+
presentation.className,
|
|
433
|
+
)}
|
|
434
|
+
style={presentation.style}
|
|
297
435
|
>
|
|
298
436
|
<div class="card-body flex max-h-full min-h-0 flex-col gap-4">
|
|
299
|
-
<div class="flex items-
|
|
437
|
+
<div class="flex items-center gap-3 shrink-0">
|
|
438
|
+
{!!showBackButton && (
|
|
439
|
+
<button
|
|
440
|
+
type="button"
|
|
441
|
+
class="btn btn-ghost btn-sm btn-circle shrink-0 transition-colors hover:bg-base-300"
|
|
442
|
+
aria-label={this.scope.locale.Back()}
|
|
443
|
+
title={this.scope.locale.Back()}
|
|
444
|
+
onClick={() => {
|
|
445
|
+
onBack?.();
|
|
446
|
+
}}
|
|
447
|
+
>
|
|
448
|
+
<ZIcon name="::arrow-back" width={18} height={18}></ZIcon>
|
|
449
|
+
</button>
|
|
450
|
+
)}
|
|
300
451
|
{!!iconName && (
|
|
301
452
|
<ZIcon class="text-primary mt-1 shrink-0" name={iconName} width={24}></ZIcon>
|
|
302
453
|
)}
|
|
@@ -334,19 +485,44 @@ export class BehaviorAppModal extends BeanBehaviorBase<
|
|
|
334
485
|
|
|
335
486
|
private _prepareDialogOptions(
|
|
336
487
|
type: ModalType,
|
|
337
|
-
dialogOptions?:
|
|
338
|
-
|
|
488
|
+
dialogOptions?:
|
|
489
|
+
| IModalDialogOptions
|
|
490
|
+
| IModalMessageOptions
|
|
491
|
+
| IModalRoutedDialogPresentationOptions,
|
|
492
|
+
routedDialogOptions?: IModalRoutedDialogPresentationOptions,
|
|
493
|
+
): IPreparedDialogOptions {
|
|
339
494
|
const defaults = this.scope.config.model[type].default as
|
|
340
495
|
| IModalDialogOptions
|
|
341
|
-
| IModalMessageOptions
|
|
342
|
-
|
|
343
|
-
|
|
496
|
+
| IModalMessageOptions
|
|
497
|
+
| IModalRoutedDialogPresentationOptions;
|
|
498
|
+
const maxWidth =
|
|
499
|
+
type === 'routedDialog'
|
|
500
|
+
? this._resolveRoutedDialogMaxWidth(
|
|
501
|
+
defaults.maxWidth as ModalRoutedDialogMaxWidth | undefined,
|
|
502
|
+
routedDialogOptions?.maxWidth,
|
|
503
|
+
(dialogOptions as IModalRoutedDialogPresentationOptions | undefined)?.maxWidth,
|
|
504
|
+
)
|
|
505
|
+
: ((dialogOptions as IModalDialogOptions | IModalMessageOptions | undefined)?.maxWidth ??
|
|
506
|
+
(defaults.maxWidth as ModalWidth | undefined));
|
|
507
|
+
const routedPresentation = dialogOptions as IModalRoutedDialogPresentationOptions | undefined;
|
|
508
|
+
const routedDefaults = defaults as IModalRoutedDialogPresentationOptions;
|
|
509
|
+
const topGutter =
|
|
510
|
+
type === 'routedDialog'
|
|
511
|
+
? this._resolveRoutedDialogTopGutter(
|
|
512
|
+
routedDefaults.topGutter,
|
|
513
|
+
routedDialogOptions?.topGutter,
|
|
514
|
+
routedPresentation?.topGutter,
|
|
515
|
+
)
|
|
516
|
+
: undefined;
|
|
517
|
+
const options: IPreparedDialogOptions = {
|
|
518
|
+
maxWidth,
|
|
344
519
|
maxHeight: dialogOptions?.maxHeight ?? defaults.maxHeight,
|
|
520
|
+
topGutter,
|
|
345
521
|
closeOnBackdrop: dialogOptions?.closeOnBackdrop ?? defaults.closeOnBackdrop,
|
|
346
522
|
closeOnEscape: dialogOptions?.closeOnEscape ?? defaults.closeOnEscape,
|
|
347
523
|
showCloseButton: false,
|
|
348
524
|
};
|
|
349
|
-
if (type !== 'dialog') return options;
|
|
525
|
+
if (type !== 'dialog' && type !== 'routedDialog') return options;
|
|
350
526
|
const defaultsDialog = defaults as IModalDialogOptions;
|
|
351
527
|
return {
|
|
352
528
|
...options,
|
|
@@ -354,20 +530,132 @@ export class BehaviorAppModal extends BeanBehaviorBase<
|
|
|
354
530
|
(dialogOptions as IModalDialogOptions | undefined)?.showCloseButton ??
|
|
355
531
|
defaultsDialog.showCloseButton ??
|
|
356
532
|
false,
|
|
533
|
+
showBackButton:
|
|
534
|
+
type === 'routedDialog'
|
|
535
|
+
? (routedPresentation?.showBackButton ??
|
|
536
|
+
routedDialogOptions?.showBackButton ??
|
|
537
|
+
routedDefaults.showBackButton ??
|
|
538
|
+
false)
|
|
539
|
+
: undefined,
|
|
357
540
|
};
|
|
358
541
|
}
|
|
359
542
|
|
|
360
|
-
private
|
|
543
|
+
private _resolveRoutedDialogMaxWidth(
|
|
544
|
+
...values: (ModalRoutedDialogMaxWidth | undefined)[]
|
|
545
|
+
): ModalRoutedDialogMaxWidth | undefined {
|
|
546
|
+
return this._resolveRoutedDialogLength(values) as ModalRoutedDialogMaxWidth | undefined;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
private _resolveRoutedDialogTopGutter(
|
|
550
|
+
...values: (ModalRoutedDialogTopGutter | undefined)[]
|
|
551
|
+
): ModalRoutedDialogTopGutter | undefined {
|
|
552
|
+
return this._resolveRoutedDialogLength(values) as ModalRoutedDialogTopGutter | undefined;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
private _resolveRoutedDialogLength(
|
|
556
|
+
values: (ModalRoutedDialogMaxWidth | ModalRoutedDialogTopGutter | undefined)[],
|
|
557
|
+
): ModalRoutedDialogMaxWidth | ModalRoutedDialogTopGutter | undefined {
|
|
558
|
+
let resolved: ModalRoutedDialogMaxWidth | ModalRoutedDialogTopGutter | undefined;
|
|
559
|
+
for (const value of values) {
|
|
560
|
+
if (value === undefined) continue;
|
|
561
|
+
if (!this._isResponsiveLength(value)) {
|
|
562
|
+
resolved = value;
|
|
563
|
+
continue;
|
|
564
|
+
}
|
|
565
|
+
resolved = {
|
|
566
|
+
...(this._isResponsiveLength(resolved) ? resolved : { default: resolved }),
|
|
567
|
+
...value,
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
return resolved;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
private _isResponsiveMaxWidth(
|
|
574
|
+
value: ModalRoutedDialogMaxWidth | undefined,
|
|
575
|
+
): value is IModalResponsiveMaxWidth {
|
|
576
|
+
return this._isResponsiveLength(value);
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
private _isResponsiveTopGutter(
|
|
580
|
+
value: ModalRoutedDialogTopGutter | undefined,
|
|
581
|
+
): value is IModalResponsiveTopGutter {
|
|
582
|
+
return this._isResponsiveLength(value);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
private _isResponsiveLength(
|
|
586
|
+
value: ModalRoutedDialogMaxWidth | ModalRoutedDialogTopGutter | undefined,
|
|
587
|
+
): value is IModalResponsiveMaxWidth | IModalResponsiveTopGutter {
|
|
588
|
+
return !!value && typeof value === 'object';
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
private _routedDialogOuterPresentation(
|
|
592
|
+
dialogOptions: IPreparedDialogOptions,
|
|
593
|
+
): IModalOuterPresentation {
|
|
594
|
+
const style = {} as Record<string, string>;
|
|
595
|
+
const topGutter = dialogOptions.topGutter;
|
|
596
|
+
if (this._isResponsiveTopGutter(topGutter)) {
|
|
597
|
+
this._setResponsiveTopGutter(style, 'default', topGutter.default);
|
|
598
|
+
this._setResponsiveTopGutter(style, 'md', topGutter.md);
|
|
599
|
+
this._setResponsiveTopGutter(style, 'lg', topGutter.lg);
|
|
600
|
+
} else if (topGutter !== undefined) {
|
|
601
|
+
this._setResponsiveTopGutter(style, 'default', topGutter);
|
|
602
|
+
}
|
|
603
|
+
return {
|
|
604
|
+
className: this.$style(responsiveTopGutterClass),
|
|
605
|
+
style: Object.keys(style).length > 0 ? style : undefined,
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
private _dialogPresentation(
|
|
610
|
+
dialogOptions: IPreparedDialogOptions,
|
|
611
|
+
routedDialog: boolean,
|
|
612
|
+
): IModalCardPresentation {
|
|
361
613
|
const style = {} as Record<string, string>;
|
|
362
614
|
const maxWidth = dialogOptions.maxWidth;
|
|
363
615
|
const maxHeight = dialogOptions.maxHeight;
|
|
364
|
-
|
|
365
|
-
|
|
616
|
+
const classNames: string[] = [];
|
|
617
|
+
if (this._isResponsiveMaxWidth(maxWidth)) {
|
|
618
|
+
classNames.push(this.$style(responsiveMaxWidthClass));
|
|
619
|
+
this._setResponsiveMaxWidth(style, 'default', maxWidth.default);
|
|
620
|
+
this._setResponsiveMaxWidth(style, 'md', maxWidth.md);
|
|
621
|
+
this._setResponsiveMaxWidth(style, 'lg', maxWidth.lg);
|
|
622
|
+
} else if (maxWidth !== undefined) {
|
|
623
|
+
style.maxWidth = this._normalizeWidth(maxWidth);
|
|
366
624
|
}
|
|
367
|
-
if (
|
|
368
|
-
style
|
|
625
|
+
if (routedDialog) {
|
|
626
|
+
classNames.push(this.$style(routedDialogMaxHeightClass));
|
|
627
|
+
if (maxHeight !== undefined) {
|
|
628
|
+
style['--zova-routed-dialog-max-height'] = this._normalizeWidth(maxHeight);
|
|
629
|
+
}
|
|
630
|
+
} else if (maxHeight !== undefined) {
|
|
631
|
+
style.maxHeight = this._normalizeWidth(maxHeight);
|
|
369
632
|
}
|
|
370
|
-
return
|
|
633
|
+
return {
|
|
634
|
+
className: classNames.length > 0 ? this.$cssMerge(...classNames) : undefined,
|
|
635
|
+
style: Object.keys(style).length > 0 ? style : undefined,
|
|
636
|
+
};
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
private _setResponsiveMaxWidth(
|
|
640
|
+
style: Record<string, string>,
|
|
641
|
+
breakpoint: keyof IModalResponsiveMaxWidth,
|
|
642
|
+
value: ModalWidth | undefined,
|
|
643
|
+
) {
|
|
644
|
+
if (value === undefined) return;
|
|
645
|
+
style[`--zova-routed-dialog-max-width-${breakpoint}`] = this._normalizeWidth(value);
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
private _setResponsiveTopGutter(
|
|
649
|
+
style: Record<string, string>,
|
|
650
|
+
breakpoint: keyof IModalResponsiveTopGutter,
|
|
651
|
+
value: ModalWidth | undefined,
|
|
652
|
+
) {
|
|
653
|
+
if (value === undefined) return;
|
|
654
|
+
style[`--zova-routed-dialog-top-gutter-${breakpoint}`] = this._normalizeWidth(value);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
private _normalizeWidth(value: ModalWidth) {
|
|
658
|
+
return typeof value === 'number' ? `${value}px` : value;
|
|
371
659
|
}
|
|
372
660
|
|
|
373
661
|
private _getButtonClass(type: AlertType, primary?: boolean) {
|