ci4sManagement-cloud/react-ui/src/utils/modal.tsx

134 lines
3.3 KiB
TypeScript
Raw Normal View History

2024-04-15 18:41:14 +08:00
/*
* @Author:
* @Date: 2024-04-13 10:08:35
2024-05-14 08:55:09 +08:00
* @Description: Modal
2024-04-15 18:41:14 +08:00
*/
2024-04-22 09:15:28 +08:00
import { ConfigProvider, type ModalProps } from 'antd';
import { globalConfig } from 'antd/es/config-provider';
2024-05-14 08:55:09 +08:00
import zhCN from 'antd/locale/zh_CN';
2024-04-15 18:41:14 +08:00
import React, { useState } from 'react';
import { createRoot } from 'react-dom/client';
2024-05-24 09:02:09 +08:00
const destroyFns: (() => void)[] = [];
2024-04-15 18:41:14 +08:00
/**
* 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.
*/
2024-05-22 10:58:51 +08:00
export const openAntdModal = <T extends Omit<ModalProps, 'onOk'>>(
2024-04-15 18:41:14 +08:00
modal: (props: T) => React.ReactNode,
modalProps: T,
) => {
const CustomModel = modal;
2024-04-22 09:15:28 +08:00
const container = document.createDocumentFragment();
const root = createRoot(container);
2024-04-15 18:41:14 +08:00
const { afterClose, onCancel } = modalProps;
2024-04-22 09:15:28 +08:00
const global = globalConfig();
let timeoutId: ReturnType<typeof setTimeout>;
2024-04-15 18:41:14 +08:00
function destroy() {
2024-05-24 09:02:09 +08:00
const index = destroyFns.indexOf(close);
if (index !== -1) {
destroyFns.splice(index, 1);
}
2024-04-15 18:41:14 +08:00
root.unmount();
}
function handleAfterClose() {
afterClose?.();
2024-04-22 09:15:28 +08:00
// 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.
2024-04-15 18:41:14 +08:00
setTimeout(() => {
destroy();
}, 0);
}
function handleCancel(e: React.MouseEvent<HTMLButtonElement, MouseEvent>) {
if (onCancel) {
onCancel(e);
} else {
close();
}
}
function render(props: T) {
2024-04-22 09:15:28 +08:00
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(
2024-05-14 08:55:09 +08:00
<ConfigProvider
prefixCls={rootPrefixCls}
iconPrefixCls={iconPrefixCls}
theme={theme}
locale={zhCN}
>
2024-04-22 09:15:28 +08:00
{global.holderRender ? global.holderRender(dom) : dom}
</ConfigProvider>,
);
});
2024-04-15 18:41:14 +08:00
}
function close() {
2024-04-22 09:15:28 +08:00
render({ ...modalProps, open: false });
2024-04-15 18:41:14 +08:00
}
render({ ...modalProps, open: true });
2024-05-24 09:02:09 +08:00
destroyFns.push(close);
2024-04-15 18:41:14 +08:00
return {
2024-04-16 17:36:37 +08:00
close,
2024-04-15 18:41:14 +08:00
};
};
/**
* Generates a custom hook for managing an Ant Design modal.
*
* @param modal - The function that renders the modal content.
2024-05-24 09:02:09 +08:00
* @param defaultProps - The default modal properties.
2024-04-15 18:41:14 +08:00
* @return The modal component, open function, and close function.
*/
2024-05-24 09:02:09 +08:00
export const useModal = <T extends ModalProps>(
2024-04-15 18:41:14 +08:00
modal: (props: T) => React.ReactNode,
2024-05-24 09:02:09 +08:00
defaultProps?: T,
2024-04-15 18:41:14 +08:00
) => {
const [visible, setVisible] = useState(false);
2024-05-24 09:02:09 +08:00
const [props, setProps] = useState<T>(defaultProps || ({} as T));
2024-04-15 18:41:14 +08:00
const CustomModel = modal;
const open = (props: T) => {
2024-05-24 09:02:09 +08:00
setProps((prev) => ({
...prev,
...props,
}));
2024-04-15 18:41:14 +08:00
setVisible(true);
};
const close = () => {
setVisible(false);
};
2024-05-24 09:02:09 +08:00
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();
}
2024-04-15 18:41:14 +08:00
};