Skip to content

Commit de4f28d

Browse files
committed
refactor(dialog): restructure dialog context and provider, removing legacy code and enhancing dialog management
1 parent fe58dad commit de4f28d

File tree

10 files changed

+177
-194
lines changed

10 files changed

+177
-194
lines changed

playground/examples/dialog/use-dialog.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ const openDestructive = () => {
2929
};
3030
3131
const openCustomTextDialog = () => {
32-
dialog.create({
32+
dialog({
3333
type: 'warning',
3434
title: 'Dialog Title',
3535
description: 'Dialog Description',
@@ -40,7 +40,7 @@ const openCustomTextDialog = () => {
4040
};
4141
4242
const openCustomFooterDialog = () => {
43-
dialog.create({
43+
dialog({
4444
type: 'warning',
4545
title: 'Dialog Title',
4646
description: 'Dialog Description',

src/components/config-provider/config-provider.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ import { useOmitProps } from '@soybeanjs/headless/composables';
66
import { isClient, transformPropsToContext } from '@soybeanjs/headless/shared';
77
import { generateCSSVars } from '@soybeanjs/unocss-shadcn';
88
import type { ThemeSize } from '@/theme';
9+
import DialogProvider from '../dialog/dialog-provider.vue';
910
import { provideConfigProviderContext } from './context';
1011
import { getThemeName, isIncludeByDefaultTheme } from './shared';
11-
import DialogProvider from './dialog-provider.vue';
1212
import type { ConfigProviderProps } from './types';
1313
1414
defineOptions({
Lines changed: 2 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -1,100 +1,7 @@
1-
import { computed, shallowRef, watch } from 'vue';
2-
import { useDebounceFn } from '@vueuse/core';
31
import { useContext } from '@soybeanjs/headless/composables';
4-
import type {
5-
ConfigProviderContextParams,
6-
DialogInfo,
7-
UseDialogOptions,
8-
UseDialogReturn,
9-
UseDialogType
10-
} from './types';
2+
import type { ConfigProviderContextParams } from './types';
113

124
export const [provideConfigProviderContext, useConfigProvider] = useContext(
13-
'UIConfigProvider',
5+
'UiConfigProvider',
146
(params: ConfigProviderContextParams) => params
157
);
16-
17-
export const [provideDialogProviderContext, useDialogProvider] = useContext('UIDialogProvider', () => {
18-
const dialogInfos = shallowRef<DialogInfo[]>([]);
19-
20-
const dialogIds = computed(() => dialogInfos.value.map(info => info.id));
21-
22-
const debouncedDialogInfos = shallowRef<DialogInfo[]>([]);
23-
24-
const updater = useDebounceFn(() => {
25-
debouncedDialogInfos.value = dialogInfos.value;
26-
}, 500);
27-
28-
watch(dialogInfos, (newValue, oldValue) => {
29-
if (newValue.length > oldValue.length) {
30-
debouncedDialogInfos.value = dialogInfos.value;
31-
} else {
32-
updater();
33-
}
34-
});
35-
36-
const addDialog = (info: DialogInfo) => {
37-
dialogInfos.value = [...dialogInfos.value, info];
38-
};
39-
40-
const removeDialog = (id: number) => {
41-
dialogInfos.value = dialogInfos.value.filter(info => info.id !== id);
42-
};
43-
44-
const destroyAll = () => {
45-
dialogInfos.value = [];
46-
};
47-
48-
let $id = 0;
49-
50-
const factory = (options: UseDialogOptions & { type: UseDialogType }) => {
51-
const { type, showIcon = true, ...rest } = options;
52-
53-
const id = $id;
54-
$id++;
55-
56-
const info: DialogInfo = {
57-
id,
58-
type,
59-
options: {
60-
...rest,
61-
showIcon
62-
}
63-
};
64-
65-
return info;
66-
};
67-
68-
const create = (options: UseDialogOptions & { type: UseDialogType }) => {
69-
const info = factory(options);
70-
addDialog(info);
71-
};
72-
73-
const useDialog = {
74-
create,
75-
destroyAll
76-
} as UseDialogReturn;
77-
78-
const types: UseDialogType[] = ['destructive', 'success', 'warning', 'info'];
79-
80-
for (const type of types) {
81-
useDialog[type] = (options: UseDialogOptions) => {
82-
return create({ ...options, type });
83-
};
84-
}
85-
86-
return {
87-
useDialog,
88-
destroyAll,
89-
dialogInfos,
90-
dialogIds,
91-
debouncedDialogInfos,
92-
removeDialog
93-
};
94-
});
95-
96-
export const useDialog = () => {
97-
const { useDialog: $useDialog } = useDialogProvider('UIDialogConsumer');
98-
99-
return $useDialog;
100-
};

src/components/config-provider/dialog-provider.vue

Lines changed: 0 additions & 56 deletions
This file was deleted.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
export { default as SConfigProvider } from './config-provider.vue';
2-
export { useConfigProvider, useDialog } from './context';
2+
export { useConfigProvider } from './context';
33

44
export type { ConfigProviderProps } from './types';
Lines changed: 2 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,6 @@
1-
import type { VNode } from 'vue';
2-
import type {
3-
DialogContentEmits,
4-
EmitsToHookProps,
5-
MaybePromise,
6-
PropsToContext,
7-
ConfigProviderProps as _ConfigProviderProps
8-
} from '@soybeanjs/headless';
1+
import type { PropsToContext, ConfigProviderProps as _ConfigProviderProps } from '@soybeanjs/headless';
92
import type { ThemeOptions } from '@soybeanjs/unocss-shadcn';
10-
import type { ThemeColor, ThemeSize } from '@/theme';
11-
import type { DialogProps } from '../dialog/types';
3+
import type { ThemeSize } from '@/theme';
124

135
export interface ConfigProviderProps extends _ConfigProviderProps {
146
/** The theme options. */
@@ -33,30 +25,3 @@ export interface ConfigProviderProps extends _ConfigProviderProps {
3325
}
3426

3527
export interface ConfigProviderContextParams extends PropsToContext<ConfigProviderProps> {}
36-
37-
export type UseDialogType = Extract<ThemeColor, 'destructive' | 'success' | 'warning' | 'info'>;
38-
39-
export interface DialogInfo {
40-
id: number;
41-
type: UseDialogType;
42-
options: UseDialogOptions;
43-
}
44-
45-
export interface UseDialogOptions
46-
extends Pick<DialogProps, 'size' | 'ui'>,
47-
Partial<EmitsToHookProps<DialogContentEmits>> {
48-
title?: string | VNode;
49-
showIcon?: boolean;
50-
description?: string | VNode;
51-
content?: VNode;
52-
footer?: VNode;
53-
confirmText?: string;
54-
cancelText?: string;
55-
onConfirm?: () => MaybePromise<boolean | void>;
56-
onCancel?: () => MaybePromise<boolean | void>;
57-
}
58-
59-
export type UseDialogReturn = {
60-
create: (options: UseDialogOptions & { type: UseDialogType }) => void;
61-
destroyAll: () => void;
62-
} & Record<UseDialogType, (options: UseDialogOptions) => void>;

src/components/dialog/context.ts

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import { computed, shallowRef, watch } from 'vue';
2+
import { useDebounceFn } from '@vueuse/core';
3+
import { useContext } from '@soybeanjs/headless/composables';
4+
import type { DialogState, UseDialogOptions, UseDialogReturn, UseDialogType } from './types';
5+
6+
export const [provideDialogProviderContext, useDialogProvider] = useContext('UiDialogProvider', () => {
7+
const states = shallowRef<DialogState[]>([]);
8+
9+
const ids = computed(() => states.value.map(state => state.id));
10+
11+
const debouncedStates = shallowRef<DialogState[]>([]);
12+
13+
const updater = useDebounceFn(() => {
14+
debouncedStates.value = states.value;
15+
}, 500);
16+
17+
watch(states, (newValue, oldValue) => {
18+
if (newValue.length > oldValue.length) {
19+
debouncedStates.value = states.value;
20+
} else {
21+
updater();
22+
}
23+
});
24+
25+
const add = (state: DialogState) => {
26+
states.value = [...states.value, state];
27+
};
28+
29+
const remove = (id: number) => {
30+
states.value = states.value.filter(state => state.id !== id);
31+
};
32+
33+
const clear = () => {
34+
states.value = [];
35+
};
36+
37+
let $id = 0;
38+
39+
const factory = (options: UseDialogOptions) => {
40+
const { showIcon = true, ...rest } = options;
41+
42+
const id = $id;
43+
$id++;
44+
45+
const state: DialogState = {
46+
id,
47+
showIcon,
48+
...rest
49+
};
50+
51+
return state;
52+
};
53+
54+
const create = (options: UseDialogOptions) => {
55+
const state = factory(options);
56+
add(state);
57+
};
58+
59+
const useDialog = create as UseDialogReturn;
60+
61+
useDialog.clear = clear;
62+
63+
const types: UseDialogType[] = ['destructive', 'success', 'warning', 'info'];
64+
65+
for (const type of types) {
66+
useDialog[type] = (options: Omit<UseDialogOptions, 'type'>) => {
67+
return create({ type, ...options });
68+
};
69+
}
70+
71+
return {
72+
useDialog,
73+
clear,
74+
ids,
75+
states: computed(() => debouncedStates.value),
76+
remove
77+
};
78+
});
79+
80+
export const useDialog = () => {
81+
const { useDialog: $useDialog } = useDialogProvider('UiDialogConsumer');
82+
83+
return $useDialog;
84+
};
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<script setup lang="ts">
2+
import { onBeforeUnmount } from 'vue';
3+
import AlertDialog from '../alert-dialog/alert-dialog.vue';
4+
import AlertDialogCancel from '../alert-dialog/alert-dialog-cancel.vue';
5+
import AlertDialogAction from '../alert-dialog/alert-dialog-action.vue';
6+
import { provideDialogProviderContext } from './context';
7+
8+
defineOptions({
9+
name: 'SDialogProvider'
10+
});
11+
12+
const { states, ids, remove, clear } = provideDialogProviderContext();
13+
14+
onBeforeUnmount(() => {
15+
clear();
16+
});
17+
</script>
18+
19+
<template>
20+
<AlertDialog
21+
v-for="state in states"
22+
:key="state.id"
23+
:type="state.type"
24+
:open="ids.includes(state.id)"
25+
:size="state.size"
26+
:ui="state.ui"
27+
:show-icon="state.showIcon"
28+
@update:open="remove(state.id)"
29+
>
30+
<template v-if="state.title" #title>
31+
<template v-if="typeof state.title === 'string'">
32+
{{ state.title }}
33+
</template>
34+
<component :is="state.title" v-else />
35+
</template>
36+
<template #description>
37+
<template v-if="typeof state.description === 'string'">
38+
{{ state.description }}
39+
</template>
40+
<component :is="state.description" v-else />
41+
</template>
42+
<component :is="state.content" v-if="state.content" />
43+
<template #footer>
44+
<component :is="state.footer" v-if="state.footer" />
45+
<template v-else>
46+
<AlertDialogCancel v-if="state.type === 'warning'" :text="state.cancelText" :before-close="state.onCancel" />
47+
<AlertDialogAction :text="state.confirmText" :before-close="state.onConfirm" />
48+
</template>
49+
</template>
50+
</AlertDialog>
51+
<slot />
52+
</template>

src/components/dialog/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ export { default as SDialog } from './dialog.vue';
22
export { default as SDialogClose } from './dialog-close.vue';
33
export { default as SDialogPure } from './dialog-pure.vue';
44

5+
export { useDialog } from './context';
6+
57
export type {
68
DialogRootProps,
79
DialogRootEmits,

0 commit comments

Comments
 (0)