152 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Vue
		
	
	
			
		
		
	
	
			152 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Vue
		
	
	
| 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: () => VueNode) => () => void;
 | |
| }
 | |
| 
 | |
| const ElementsHolder = defineComponent({
 | |
|   name: 'ElementsHolder',
 | |
|   inheritAttrs: false,
 | |
|   setup(_, { expose }) {
 | |
|     const modals = shallowRef<(() => VueNode)[]>([]);
 | |
|     const addModal = (modal: () => VueNode) => {
 | |
|       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;
 |