feat(modal): add useModal (#6517)
* feat(modal): add useModal hook * feat(modal): add HookModal demo * test(modal): update HookModal demo snap * feat(modal): update modal docs * chore(modal): update modal typepull/6527/head
parent
0df6abe3a7
commit
69640c0af8
|
@ -149,8 +149,6 @@ export interface ModalLocale {
|
||||||
justOkText: string;
|
justOkText: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const destroyFns = [];
|
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
compatConfig: { MODE: 3 },
|
compatConfig: { MODE: 3 },
|
||||||
name: 'AModal',
|
name: 'AModal',
|
||||||
|
@ -167,6 +165,7 @@ export default defineComponent({
|
||||||
props,
|
props,
|
||||||
);
|
);
|
||||||
const [wrapSSR, hashId] = useStyle(prefixCls);
|
const [wrapSSR, hashId] = useStyle(prefixCls);
|
||||||
|
|
||||||
warning(
|
warning(
|
||||||
props.visible === undefined,
|
props.visible === undefined,
|
||||||
'Modal',
|
'Modal',
|
||||||
|
|
|
@ -1,5 +1,17 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`renders ./components/modal/demo/HookModal.vue correctly 1`] = `
|
||||||
|
<div><button class="ant-btn ant-btn-default" type="button">
|
||||||
|
<!----><span>Confirm</span>
|
||||||
|
</button><button class="ant-btn ant-btn-default" type="button">
|
||||||
|
<!----><span>With promise</span>
|
||||||
|
</button><button class="ant-btn ant-btn-dashed" type="button">
|
||||||
|
<!----><span>Delete</span>
|
||||||
|
</button><button class="ant-btn ant-btn-dashed" type="button">
|
||||||
|
<!----><span>With extra props</span>
|
||||||
|
</button></div>
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`renders ./components/modal/demo/async.vue correctly 1`] = `
|
exports[`renders ./components/modal/demo/async.vue correctly 1`] = `
|
||||||
<div><button class="ant-btn ant-btn-primary" type="button">
|
<div><button class="ant-btn ant-btn-primary" type="button">
|
||||||
<!----><span>Open Modal with async logic</span>
|
<!----><span>Open Modal with async logic</span>
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
import { createVNode, render as vueRender } from 'vue';
|
import { createVNode, render as vueRender } from 'vue';
|
||||||
import ConfirmDialog from './ConfirmDialog';
|
import ConfirmDialog from './ConfirmDialog';
|
||||||
import type { ModalFuncProps } from './Modal';
|
import type { ModalFuncProps } from './Modal';
|
||||||
import { destroyFns } from './Modal';
|
|
||||||
import ConfigProvider, { globalConfigForApi } from '../config-provider';
|
import ConfigProvider, { globalConfigForApi } from '../config-provider';
|
||||||
import omit from '../_util/omit';
|
import omit from '../_util/omit';
|
||||||
|
|
||||||
import { getConfirmLocale } from './locale';
|
import { getConfirmLocale } from './locale';
|
||||||
|
import destroyFns from './destroyFns';
|
||||||
|
|
||||||
type ConfigUpdate = ModalFuncProps | ((prevConfig: ModalFuncProps) => ModalFuncProps);
|
type ConfigUpdate = ModalFuncProps | ((prevConfig: ModalFuncProps) => ModalFuncProps);
|
||||||
|
export type ModalStaticFunctions<T = ModalFunc> = Record<NonNullable<ModalFuncProps['type']>, T>;
|
||||||
|
|
||||||
export type ModalFunc = (props: ModalFuncProps) => {
|
export type ModalFunc = (props: ModalFuncProps) => {
|
||||||
destroy: () => void;
|
destroy: () => void;
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
<docs>
|
||||||
|
---
|
||||||
|
order: 12
|
||||||
|
title:
|
||||||
|
zh-CN: 使用useModal获取上下文
|
||||||
|
en-US: Use useModal to get context
|
||||||
|
---
|
||||||
|
|
||||||
|
## zh-CN
|
||||||
|
|
||||||
|
通过 `Modal.useModal` 创建支持读取 context 的 `contextHolder`。
|
||||||
|
|
||||||
|
## en-US
|
||||||
|
|
||||||
|
Use `Modal.useModal` to get `contextHolder` with context accessible issue.
|
||||||
|
|
||||||
|
</docs>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<a-button @click="showConfirm">Confirm</a-button>
|
||||||
|
<a-button @click="showPromiseConfirm">With promise</a-button>
|
||||||
|
<a-button type="dashed" @click="showDeleteConfirm">Delete</a-button>
|
||||||
|
<a-button type="dashed" @click="showPropsConfirm">With extra props</a-button>
|
||||||
|
<contextHolder />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { Modal } from 'ant-design-vue';
|
||||||
|
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
|
||||||
|
import { createVNode } from 'vue';
|
||||||
|
const [modal, contextHolder] = Modal.useModal();
|
||||||
|
const showConfirm = () => {
|
||||||
|
modal.confirm({
|
||||||
|
title: 'Do you Want to delete these items?',
|
||||||
|
icon: createVNode(ExclamationCircleOutlined),
|
||||||
|
content: createVNode('div', { style: 'color:red;' }, 'Some descriptions'),
|
||||||
|
onOk() {
|
||||||
|
console.log('OK');
|
||||||
|
},
|
||||||
|
onCancel() {
|
||||||
|
console.log('Cancel');
|
||||||
|
},
|
||||||
|
class: 'test',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const showDeleteConfirm = () => {
|
||||||
|
modal.confirm({
|
||||||
|
title: 'Are you sure delete this task?',
|
||||||
|
icon: createVNode(ExclamationCircleOutlined),
|
||||||
|
content: 'Some descriptions',
|
||||||
|
okText: 'Yes',
|
||||||
|
okType: 'danger',
|
||||||
|
cancelText: 'No',
|
||||||
|
onOk() {
|
||||||
|
console.log('OK');
|
||||||
|
},
|
||||||
|
onCancel() {
|
||||||
|
console.log('Cancel');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const showPropsConfirm = () => {
|
||||||
|
modal.confirm({
|
||||||
|
title: 'Are you sure delete this task?',
|
||||||
|
icon: createVNode(ExclamationCircleOutlined),
|
||||||
|
content: 'Some descriptions',
|
||||||
|
okText: 'Yes',
|
||||||
|
okType: 'danger',
|
||||||
|
okButtonProps: {
|
||||||
|
disabled: true,
|
||||||
|
},
|
||||||
|
cancelText: 'No',
|
||||||
|
onOk() {
|
||||||
|
console.log('OK');
|
||||||
|
},
|
||||||
|
onCancel() {
|
||||||
|
console.log('Cancel');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
function showPromiseConfirm() {
|
||||||
|
modal.confirm({
|
||||||
|
title: 'Do you want to delete these items?',
|
||||||
|
icon: createVNode(ExclamationCircleOutlined),
|
||||||
|
content: 'When clicked the OK button, this dialog will be closed after 1 second',
|
||||||
|
async onOk() {
|
||||||
|
try {
|
||||||
|
return await new Promise((resolve, reject) => {
|
||||||
|
setTimeout(Math.random() > 0.5 ? resolve : reject, 1000);
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
return console.log('Oops errors!');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onCancel() {},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -5,6 +5,7 @@
|
||||||
<custom-footer />
|
<custom-footer />
|
||||||
<confirm />
|
<confirm />
|
||||||
<info />
|
<info />
|
||||||
|
<HookModal />
|
||||||
<locale />
|
<locale />
|
||||||
<manual />
|
<manual />
|
||||||
<position />
|
<position />
|
||||||
|
@ -31,6 +32,7 @@ import Width from './width.vue';
|
||||||
import Fullscreen from './fullscreen.vue';
|
import Fullscreen from './fullscreen.vue';
|
||||||
import ButtonProps from './button-props.vue';
|
import ButtonProps from './button-props.vue';
|
||||||
import modalRenderVue from './modal-render.vue';
|
import modalRenderVue from './modal-render.vue';
|
||||||
|
import HookModal from './HookModal.vue';
|
||||||
import CN from '../index.zh-CN.md';
|
import CN from '../index.zh-CN.md';
|
||||||
import US from '../index.en-US.md';
|
import US from '../index.en-US.md';
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
|
@ -52,6 +54,7 @@ export default defineComponent({
|
||||||
ButtonProps,
|
ButtonProps,
|
||||||
Fullscreen,
|
Fullscreen,
|
||||||
modalRenderVue,
|
modalRenderVue,
|
||||||
|
HookModal,
|
||||||
},
|
},
|
||||||
setup() {
|
setup() {
|
||||||
return {};
|
return {};
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
const destroyFns: Array<Function> = [];
|
||||||
|
export default destroyFns;
|
|
@ -116,21 +116,44 @@ router.beforeEach((to, from, next) => {
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Modal.useModal()
|
||||||
|
|
||||||
|
When you need using Context, you can use `contextHolder` which created by `Modal.useModal` to insert into children. Modal created by hooks will get all the context where `contextHolder` are. Created `modal` has the same creating function with `Modal.method`.
|
||||||
|
|
||||||
|
```html
|
||||||
|
<template>
|
||||||
|
<contextHolder />
|
||||||
|
<!-- <component :is='contextHolder'/> -->
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { Modal } from 'ant-design-vue';
|
||||||
|
const [modal, contextHolder] = Modal.useModal();
|
||||||
|
|
||||||
|
modal.confirm({
|
||||||
|
// ...
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
## FAQ
|
## FAQ
|
||||||
|
|
||||||
### Why can't the Modal method obtain global registered components, context, vuex, etc. and ConfigProvider `locale/prefixCls/theme` configuration, and can't update data responsively?
|
### Why can't the Modal method obtain global registered components, context, vuex, etc. and ConfigProvider `locale/prefixCls/theme` configuration, and can't update data responsively?
|
||||||
|
|
||||||
Call the Modal method directly, and the component will dynamically create a new Vue entity through `Vue.render`. Its context is not the same as the context where the current code is located, so the context information cannot be obtained.
|
Call the Modal method directly, and the component will dynamically create a new Vue entity through `Vue.render`. Its context is not the same as the context where the current code is located, so the context information cannot be obtained.
|
||||||
|
|
||||||
When you need context information (for example, using a globally registered component), you can pass the current component context through the `appContext` property. When you need to keep the property responsive, you can use the function to return:
|
When you need context information (for example, using a globally registered component), you can use `Modal.useModal` to get `modal` instance and `contextHolder` node. And put it in your children:
|
||||||
|
|
||||||
```tsx
|
```html
|
||||||
import { getCurrentInstance } from 'vue';
|
<template>
|
||||||
|
<contextHolder />
|
||||||
|
<!-- <component :is='contextHolder'/> -->
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { Modal } from 'ant-design-vue';
|
||||||
|
const [modal, contextHolder] = Modal.useModal();
|
||||||
|
|
||||||
const appContext = getCurrentInstance().appContext;
|
modal.confirm({
|
||||||
const title = ref('some message');
|
// ...
|
||||||
Modal.confirm({
|
});
|
||||||
title: () => title.value, // the change of title will update the title in confirm synchronously
|
</script>
|
||||||
appContext,
|
|
||||||
});
|
|
||||||
```
|
```
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
import type { App, Plugin } from 'vue';
|
import type { App, Plugin } from 'vue';
|
||||||
import type { ModalFunc, ModalFuncProps } from './Modal';
|
import type { ModalFunc, ModalFuncProps } from './Modal';
|
||||||
import Modal, { destroyFns } from './Modal';
|
import Modal from './Modal';
|
||||||
import confirm, { withWarn, withInfo, withSuccess, withError, withConfirm } from './confirm';
|
import confirm, { withWarn, withInfo, withSuccess, withError, withConfirm } from './confirm';
|
||||||
|
import useModal from './useModal';
|
||||||
|
import destroyFns from './destroyFns';
|
||||||
export type { ActionButtonProps } from '../_util/ActionButton';
|
export type { ActionButtonProps } from '../_util/ActionButton';
|
||||||
export type { ModalProps, ModalFuncProps } from './Modal';
|
export type { ModalProps, ModalFuncProps } from './Modal';
|
||||||
|
|
||||||
function modalWarn(props: ModalFuncProps) {
|
function modalWarn(props: ModalFuncProps) {
|
||||||
return confirm(withWarn(props));
|
return confirm(withWarn(props));
|
||||||
}
|
}
|
||||||
|
Modal.useModal = useModal;
|
||||||
Modal.info = function infoFn(props: ModalFuncProps) {
|
Modal.info = function infoFn(props: ModalFuncProps) {
|
||||||
return confirm(withInfo(props));
|
return confirm(withInfo(props));
|
||||||
};
|
};
|
||||||
|
@ -60,4 +61,6 @@ export default Modal as typeof Modal &
|
||||||
readonly confirm: ModalFunc;
|
readonly confirm: ModalFunc;
|
||||||
|
|
||||||
readonly destroyAll: () => void;
|
readonly destroyAll: () => void;
|
||||||
|
|
||||||
|
readonly useModal: typeof useModal;
|
||||||
};
|
};
|
||||||
|
|
|
@ -120,21 +120,44 @@ router.beforeEach((to, from, next) => {
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Modal.useModal()
|
||||||
|
|
||||||
|
当你需要使用 Context 时,可以通过 `Modal.useModal` 创建一个 `contextHolder` 插入子节点中。通过 hooks 创建的临时 Modal 将会得到 `contextHolder` 所在位置的所有上下文。创建的 `modal` 对象拥有与 [`Modal.method`](#modalmethod) 相同的创建通知方法。
|
||||||
|
|
||||||
|
```html
|
||||||
|
<template>
|
||||||
|
<contextHolder />
|
||||||
|
<!-- <component :is='contextHolder'/> -->
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { Modal } from 'ant-design-vue';
|
||||||
|
const [modal, contextHolder] = Modal.useModal();
|
||||||
|
|
||||||
|
modal.confirm({
|
||||||
|
// ...
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
## FAQ
|
## FAQ
|
||||||
|
|
||||||
### 为什么 Modal 方法不能获取 全局注册组件、context、vuex 等内容和 ConfigProvider `locale/prefixCls/theme` 配置, 以及不能响应式更新数据 ?
|
### 为什么 Modal 方法不能获取 全局注册组件、context、vuex 等内容和 ConfigProvider `locale/prefixCls/theme` 配置, 以及不能响应式更新数据 ?
|
||||||
|
|
||||||
直接调用 Modal 方法,组件会通过 `Vue.render` 动态创建新的 Vue 实体。其 context 与当前代码所在 context 并不相同,因而无法获取 context 信息。
|
直接调用 Modal 方法,组件会通过 `Vue.render` 动态创建新的 Vue 实体。其 context 与当前代码所在 context 并不相同,因而无法获取 context 信息。
|
||||||
|
|
||||||
当你需要 context 信息(例如使用全局注册的组件)时,可以通过 `appContext` 属性传递当前组件 context, 当你需要保留属性响应式时,你可以使用函数返回:
|
当你需要 context 信息(例如使用全局注册的组件)时,可以通过 Modal.useModal 方法会返回 modal 实体以及 contextHolder 节点。将其插入到你需要获取 context 位置即可:
|
||||||
|
|
||||||
```tsx
|
```html
|
||||||
import { getCurrentInstance } from 'vue';
|
<template>
|
||||||
|
<contextHolder />
|
||||||
|
<!-- <component :is='contextHolder'/> -->
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { Modal } from 'ant-design-vue';
|
||||||
|
const [modal, contextHolder] = Modal.useModal();
|
||||||
|
|
||||||
const appContext = getCurrentInstance().appContext;
|
modal.confirm({
|
||||||
const title = ref('some message');
|
// ...
|
||||||
Modal.confirm({
|
});
|
||||||
title: () => title.value, // 此时 title 的改变,会同步更新 confirm 中的 title
|
</script>
|
||||||
appContext,
|
|
||||||
});
|
|
||||||
```
|
```
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
import type { PropType } from 'vue';
|
||||||
|
import { computed, defineComponent } from 'vue';
|
||||||
|
import { useConfigContextInject } from '../../config-provider/context';
|
||||||
|
import { useLocaleReceiver } from '../../locale-provider/LocaleReceiver';
|
||||||
|
import defaultLocale from '../../locale/en_US';
|
||||||
|
import ConfirmDialog from '../ConfirmDialog';
|
||||||
|
import type { ModalFuncProps } from '../Modal';
|
||||||
|
import initDefaultProps from '../../_util/props-util/initDefaultProps';
|
||||||
|
export interface HookModalProps {
|
||||||
|
afterClose: () => void;
|
||||||
|
config: ModalFuncProps;
|
||||||
|
destroyAction: (...args: any[]) => void;
|
||||||
|
open: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface HookModalRef {
|
||||||
|
destroy: () => void;
|
||||||
|
update: (config: ModalFuncProps) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const comfirmFuncProps = () => ({
|
||||||
|
config: Object as PropType<ModalFuncProps>,
|
||||||
|
afterClose: Function as PropType<() => void>,
|
||||||
|
destroyAction: Function as PropType<(e: any) => void>,
|
||||||
|
open: Boolean,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'HookModal',
|
||||||
|
inheritAttrs: false,
|
||||||
|
props: initDefaultProps(comfirmFuncProps(), {
|
||||||
|
config: {
|
||||||
|
width: 520,
|
||||||
|
okType: 'primary',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
setup(props: HookModalProps, { expose }) {
|
||||||
|
const open = computed(() => props.open);
|
||||||
|
const innerConfig = computed(() => props.config);
|
||||||
|
const { direction, getPrefixCls } = useConfigContextInject();
|
||||||
|
const prefixCls = getPrefixCls('modal');
|
||||||
|
const rootPrefixCls = getPrefixCls();
|
||||||
|
|
||||||
|
const afterClose = () => {
|
||||||
|
props?.afterClose();
|
||||||
|
innerConfig.value.afterClose?.();
|
||||||
|
};
|
||||||
|
|
||||||
|
const close = (...args: any[]) => {
|
||||||
|
props.destroyAction(...args);
|
||||||
|
};
|
||||||
|
|
||||||
|
expose({ destroy: close });
|
||||||
|
const mergedOkCancel = innerConfig.value.okCancel ?? innerConfig.value.type === 'confirm';
|
||||||
|
const [contextLocale] = useLocaleReceiver('Modal', defaultLocale.Modal);
|
||||||
|
return () => (
|
||||||
|
<ConfirmDialog
|
||||||
|
prefixCls={prefixCls}
|
||||||
|
rootPrefixCls={rootPrefixCls}
|
||||||
|
{...innerConfig.value}
|
||||||
|
close={close}
|
||||||
|
open={open.value}
|
||||||
|
afterClose={afterClose}
|
||||||
|
okText={
|
||||||
|
innerConfig.value.okText ||
|
||||||
|
(mergedOkCancel ? contextLocale?.value.okText : contextLocale?.value.justOkText)
|
||||||
|
}
|
||||||
|
direction={innerConfig.value.direction || direction.value}
|
||||||
|
cancelText={innerConfig.value.cancelText || contextLocale?.value.cancelText}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
|
@ -0,0 +1,150 @@
|
||||||
|
import type { ComputedRef, Ref } from 'vue';
|
||||||
|
import { isRef, unref, computed, defineComponent, ref, watch } from 'vue';
|
||||||
|
import type { VueNode } from '../../_util/type';
|
||||||
|
import type { ModalFuncProps } from '../Modal';
|
||||||
|
import type { HookModalRef } from './HookModal';
|
||||||
|
import type { ModalStaticFunctions } from '../confirm';
|
||||||
|
import { withConfirm, withError, withInfo, withSuccess, withWarn } from '../confirm';
|
||||||
|
|
||||||
|
import HookModal from './HookModal';
|
||||||
|
import destroyFns from '../destroyFns';
|
||||||
|
|
||||||
|
let uuid = 0;
|
||||||
|
|
||||||
|
interface ElementsHolderRef {
|
||||||
|
addModal: (modal: ComputedRef<JSX.Element>) => () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ElementsHolder = defineComponent({
|
||||||
|
name: 'ElementsHolder',
|
||||||
|
inheritAttrs: false,
|
||||||
|
setup(_, { expose }) {
|
||||||
|
const modals = ref<ComputedRef<JSX.Element>[]>([]);
|
||||||
|
const addModal = (modal: ComputedRef<JSX.Element>) => {
|
||||||
|
modals.value.push(modal);
|
||||||
|
return () => {
|
||||||
|
modals.value = modals.value.filter(currentModal => currentModal !== modal);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
expose({ addModal });
|
||||||
|
return () => {
|
||||||
|
return <>{modals.value.map(modal => modal.value)}</>;
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
export type ModalFuncWithRef = (props: Ref<ModalFuncProps> | ModalFuncProps) => {
|
||||||
|
destroy: () => void;
|
||||||
|
update: (configUpdate: ModalFuncProps) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
function useModal(): readonly [
|
||||||
|
Omit<ModalStaticFunctions<ModalFuncWithRef>, 'warn'>,
|
||||||
|
() => VueNode,
|
||||||
|
] {
|
||||||
|
const holderRef = ref<ElementsHolderRef>(null);
|
||||||
|
// ========================== Effect ==========================
|
||||||
|
const actionQueue = ref([]);
|
||||||
|
watch(
|
||||||
|
actionQueue,
|
||||||
|
() => {
|
||||||
|
if (actionQueue.value.length) {
|
||||||
|
const cloneQueue = [...actionQueue.value];
|
||||||
|
cloneQueue.forEach(action => {
|
||||||
|
action();
|
||||||
|
});
|
||||||
|
actionQueue.value = [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// =========================== Hook ===========================
|
||||||
|
const getConfirmFunc = (withFunc: (config: ModalFuncProps) => ModalFuncProps) =>
|
||||||
|
function hookConfirm(config: Ref<ModalFuncProps> | ModalFuncProps) {
|
||||||
|
uuid += 1;
|
||||||
|
const open = ref(true);
|
||||||
|
const modalRef = ref<HookModalRef>(null);
|
||||||
|
const configRef = ref(unref(config));
|
||||||
|
const updateConfig = ref({});
|
||||||
|
watch(
|
||||||
|
() => config,
|
||||||
|
val => {
|
||||||
|
updateAction({
|
||||||
|
...(isRef(val) ? val.value : val),
|
||||||
|
...updateConfig.value,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
// eslint-disable-next-line prefer-const
|
||||||
|
let closeFunc: Function | undefined;
|
||||||
|
const modal = computed(() => (
|
||||||
|
<HookModal
|
||||||
|
key={`modal-${uuid}`}
|
||||||
|
config={withFunc(configRef.value)}
|
||||||
|
ref={modalRef}
|
||||||
|
open={open.value}
|
||||||
|
destroyAction={destroyAction}
|
||||||
|
afterClose={() => {
|
||||||
|
closeFunc?.();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
|
||||||
|
closeFunc = holderRef.value?.addModal(modal);
|
||||||
|
|
||||||
|
if (closeFunc) {
|
||||||
|
destroyFns.push(closeFunc);
|
||||||
|
}
|
||||||
|
|
||||||
|
const destroyAction = (...args: any[]) => {
|
||||||
|
open.value = false;
|
||||||
|
const triggerCancel = args.some(param => param && param.triggerCancel);
|
||||||
|
if (configRef.value.onCancel && triggerCancel) {
|
||||||
|
configRef.value.onCancel(() => {}, ...args.slice(1));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateAction = (newConfig: ModalFuncProps) => {
|
||||||
|
configRef.value = {
|
||||||
|
...configRef.value,
|
||||||
|
...newConfig,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const destroy = () => {
|
||||||
|
if (modalRef.value) {
|
||||||
|
destroyAction();
|
||||||
|
} else {
|
||||||
|
actionQueue.value = [...actionQueue.value, destroyAction];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const update = (newConfig: ModalFuncProps) => {
|
||||||
|
updateConfig.value = newConfig;
|
||||||
|
if (modalRef.value) {
|
||||||
|
updateAction(newConfig);
|
||||||
|
} else {
|
||||||
|
actionQueue.value = [...actionQueue.value, () => updateAction(newConfig)];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
destroy,
|
||||||
|
update,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const fns = computed(() => ({
|
||||||
|
info: getConfirmFunc(withInfo),
|
||||||
|
success: getConfirmFunc(withSuccess),
|
||||||
|
error: getConfirmFunc(withError),
|
||||||
|
warning: getConfirmFunc(withWarn),
|
||||||
|
confirm: getConfirmFunc(withConfirm),
|
||||||
|
}));
|
||||||
|
|
||||||
|
return [fns.value, () => <ElementsHolder key="modal-holder" ref={holderRef} />] as const;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useModal;
|
Loading…
Reference in New Issue