You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ant-design-vue/components/modal/useModal/index.tsx

152 lines
4.3 KiB

import type { Ref } from 'vue';
import { isRef, unref, computed, defineComponent, shallowRef, watch } from 'vue';
import type { MaybeRef, 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: () => JSX.Element) => () => void;
}
const ElementsHolder = defineComponent({
name: 'ElementsHolder',
inheritAttrs: false,
setup(_, { expose }) {
const modals = shallowRef<(() => JSX.Element)[]>([]);
const addModal = (modal: () => JSX.Element) => {
modals.value.push(modal);
modals.value = modals.value.slice();
return () => {
modals.value = modals.value.filter(currentModal => currentModal !== modal);
};
};
expose({ addModal });
return () => {
return modals.value.map(modal => modal());
};
},
});
export type ModalFuncWithRef = (props: MaybeRef<ModalFuncProps>) => {
destroy: () => void;
update: (configUpdate: ModalFuncProps) => void;
};
function useModal(): readonly [
Omit<ModalStaticFunctions<ModalFuncWithRef>, 'warn'>,
() => VueNode,
] {
const holderRef = shallowRef<ElementsHolderRef>(null);
// ========================== Effect ==========================
const actionQueue = shallowRef([]);
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 = shallowRef(true);
const modalRef = shallowRef<HookModalRef>(null);
const configRef = shallowRef(unref(config));
const updateConfig = shallowRef({});
watch(
() => config,
val => {
updateAction({
...(isRef(val) ? val.value : val),
...updateConfig.value,
});
},
);
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));
}
};
// eslint-disable-next-line prefer-const
let closeFunc: Function | undefined;
const modal = () => (
<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 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),
}));
const holderKey = Symbol('modalHolderKey');
return [fns.value, () => <ElementsHolder key={holderKey} ref={holderRef} />] as const;
}
export default useModal;