You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

modal.tsx 3.4 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. /*
  2. * @Author: 赵伟
  3. * @Date: 2024-04-13 10:08:35
  4. * @Description: 以函数的方式打开 Modal
  5. */
  6. import { ConfigProvider, type ModalProps } from 'antd';
  7. import { globalConfig } from 'antd/es/config-provider';
  8. import zhCN from 'antd/locale/zh_CN';
  9. import React, { useState } from 'react';
  10. import { createRoot } from 'react-dom/client';
  11. const destroyFns: (() => void)[] = [];
  12. /**
  13. * Function to open an Ant Design modal.
  14. *
  15. * @param modal - The function that renders the modal content.
  16. * @param modalProps - The modal properties.
  17. * @return An object with a destroy method to close the modal.
  18. */
  19. export const openAntdModal = <T extends Omit<ModalProps, 'onOk'>>(
  20. modal: (props: T) => React.ReactNode,
  21. modalProps: T,
  22. ) => {
  23. const CustomModel = modal;
  24. const container = document.createDocumentFragment();
  25. const root = createRoot(container);
  26. const { afterClose, onCancel } = modalProps;
  27. const global = globalConfig();
  28. let timeoutId: ReturnType<typeof setTimeout>;
  29. function destroy() {
  30. const index = destroyFns.indexOf(close);
  31. if (index !== -1) {
  32. destroyFns.splice(index, 1);
  33. }
  34. root.unmount();
  35. }
  36. function handleAfterClose() {
  37. afterClose?.();
  38. // Warning: Attempted to synchronously unmount a root while React was already rendering.
  39. // React cannot finish unmounting the root until the current render has completed, which may lead to a race condition.
  40. setTimeout(() => {
  41. destroy();
  42. }, 0);
  43. }
  44. function handleCancel(e: React.MouseEvent<HTMLButtonElement, MouseEvent>) {
  45. if (onCancel) {
  46. onCancel(e);
  47. } else {
  48. close();
  49. }
  50. }
  51. function render(props: T) {
  52. clearTimeout(timeoutId);
  53. timeoutId = setTimeout(() => {
  54. const rootPrefixCls = global.getPrefixCls();
  55. const iconPrefixCls = global.getIconPrefixCls();
  56. const theme = global.getTheme();
  57. const dom = (
  58. <CustomModel {...props} onCancel={handleCancel} afterClose={handleAfterClose}></CustomModel>
  59. );
  60. root.render(
  61. <ConfigProvider
  62. prefixCls={rootPrefixCls}
  63. iconPrefixCls={iconPrefixCls}
  64. theme={theme}
  65. locale={zhCN}
  66. >
  67. {global.holderRender ? global.holderRender(dom) : dom}
  68. </ConfigProvider>,
  69. );
  70. });
  71. }
  72. function close() {
  73. render({ ...modalProps, open: false });
  74. }
  75. render({ ...modalProps, open: true });
  76. destroyFns.push(close);
  77. return {
  78. close,
  79. };
  80. };
  81. /**
  82. * Generates a custom hook for managing an Ant Design modal.
  83. *
  84. * @param modal - The function that renders the modal content.
  85. * @param defaultProps - The default modal properties.
  86. * @return The modal component, open function, and close function.
  87. */
  88. export const useModal = <T extends ModalProps>(
  89. modal: (props: T) => React.ReactNode,
  90. defaultProps?: T,
  91. ) => {
  92. const [visible, setVisible] = useState(false);
  93. const [props, setProps] = useState<T>(defaultProps || ({} as T));
  94. const CustomModel = modal;
  95. const open = (props: T) => {
  96. setProps((prev) => ({
  97. ...prev,
  98. ...props,
  99. }));
  100. setVisible(true);
  101. };
  102. const close = () => {
  103. setVisible(false);
  104. };
  105. return [<CustomModel key="modal" open={visible} {...props} />, open, close] as const;
  106. };
  107. // 关闭没有手动关闭的 Modal
  108. export const closeAllModals = () => {
  109. let close = destroyFns.pop();
  110. while (close) {
  111. close();
  112. close = destroyFns.pop();
  113. }
  114. };