134 lines
3.3 KiB
TypeScript
134 lines
3.3 KiB
TypeScript
/*
|
|
* @Author: 赵伟
|
|
* @Date: 2024-04-13 10:08:35
|
|
* @Description: 以函数的方式打开 Modal
|
|
*/
|
|
import { ConfigProvider, type ModalProps } from 'antd';
|
|
import { globalConfig } from 'antd/es/config-provider';
|
|
import zhCN from 'antd/locale/zh_CN';
|
|
import React, { useState } from 'react';
|
|
import { createRoot } from 'react-dom/client';
|
|
|
|
const destroyFns: (() => void)[] = [];
|
|
|
|
/**
|
|
* Function to open an Ant Design modal.
|
|
*
|
|
* @param modal - The function that renders the modal content.
|
|
* @param modalProps - The modal properties.
|
|
* @return An object with a destroy method to close the modal.
|
|
*/
|
|
export const openAntdModal = <T extends Omit<ModalProps, 'onOk'>>(
|
|
modal: (props: T) => React.ReactNode,
|
|
modalProps: T,
|
|
) => {
|
|
const CustomModel = modal;
|
|
const container = document.createDocumentFragment();
|
|
const root = createRoot(container);
|
|
const { afterClose, onCancel } = modalProps;
|
|
const global = globalConfig();
|
|
let timeoutId: ReturnType<typeof setTimeout>;
|
|
|
|
function destroy() {
|
|
const index = destroyFns.indexOf(close);
|
|
if (index !== -1) {
|
|
destroyFns.splice(index, 1);
|
|
}
|
|
|
|
root.unmount();
|
|
}
|
|
|
|
function handleAfterClose() {
|
|
afterClose?.();
|
|
// Warning: Attempted to synchronously unmount a root while React was already rendering.
|
|
// React cannot finish unmounting the root until the current render has completed, which may lead to a race condition.
|
|
setTimeout(() => {
|
|
destroy();
|
|
}, 0);
|
|
}
|
|
|
|
function handleCancel(e: React.MouseEvent<HTMLButtonElement, MouseEvent>) {
|
|
if (onCancel) {
|
|
onCancel(e);
|
|
} else {
|
|
close();
|
|
}
|
|
}
|
|
|
|
function render(props: T) {
|
|
clearTimeout(timeoutId);
|
|
|
|
timeoutId = setTimeout(() => {
|
|
const rootPrefixCls = global.getPrefixCls();
|
|
const iconPrefixCls = global.getIconPrefixCls();
|
|
const theme = global.getTheme();
|
|
const dom = (
|
|
<CustomModel {...props} onCancel={handleCancel} afterClose={handleAfterClose}></CustomModel>
|
|
);
|
|
|
|
root.render(
|
|
<ConfigProvider
|
|
prefixCls={rootPrefixCls}
|
|
iconPrefixCls={iconPrefixCls}
|
|
theme={theme}
|
|
locale={zhCN}
|
|
>
|
|
{global.holderRender ? global.holderRender(dom) : dom}
|
|
</ConfigProvider>,
|
|
);
|
|
});
|
|
}
|
|
|
|
function close() {
|
|
render({ ...modalProps, open: false });
|
|
}
|
|
|
|
render({ ...modalProps, open: true });
|
|
|
|
destroyFns.push(close);
|
|
|
|
return {
|
|
close,
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Generates a custom hook for managing an Ant Design modal.
|
|
*
|
|
* @param modal - The function that renders the modal content.
|
|
* @param defaultProps - The default modal properties.
|
|
* @return The modal component, open function, and close function.
|
|
*/
|
|
|
|
export const useModal = <T extends ModalProps>(
|
|
modal: (props: T) => React.ReactNode,
|
|
defaultProps?: T,
|
|
) => {
|
|
const [visible, setVisible] = useState(false);
|
|
const [props, setProps] = useState<T>(defaultProps || ({} as T));
|
|
const CustomModel = modal;
|
|
|
|
const open = (props: T) => {
|
|
setProps((prev) => ({
|
|
...prev,
|
|
...props,
|
|
}));
|
|
setVisible(true);
|
|
};
|
|
|
|
const close = () => {
|
|
setVisible(false);
|
|
};
|
|
|
|
return [<CustomModel key="modal" open={visible} {...props} />, open, close] as const;
|
|
};
|
|
|
|
// 关闭没有手动关闭的 Modal
|
|
export const closeAllModals = () => {
|
|
let close = destroyFns.pop();
|
|
while (close) {
|
|
close();
|
|
close = destroyFns.pop();
|
|
}
|
|
};
|