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.0 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  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. /**
  12. * Function to open an Ant Design modal.
  13. *
  14. * @param modal - The function that renders the modal content.
  15. * @param modalProps - The modal properties.
  16. * @return An object with a destroy method to close the modal.
  17. */
  18. export const openAntdModal = <T extends Omit<ModalProps, 'onOk'>>(
  19. modal: (props: T) => React.ReactNode,
  20. modalProps: T,
  21. ) => {
  22. const CustomModel = modal;
  23. const container = document.createDocumentFragment();
  24. const root = createRoot(container);
  25. const { afterClose, onCancel } = modalProps;
  26. const global = globalConfig();
  27. let timeoutId: ReturnType<typeof setTimeout>;
  28. function destroy() {
  29. root.unmount();
  30. }
  31. function handleAfterClose() {
  32. afterClose?.();
  33. // Warning: Attempted to synchronously unmount a root while React was already rendering.
  34. // React cannot finish unmounting the root until the current render has completed, which may lead to a race condition.
  35. setTimeout(() => {
  36. destroy();
  37. }, 0);
  38. }
  39. function handleCancel(e: React.MouseEvent<HTMLButtonElement, MouseEvent>) {
  40. if (onCancel) {
  41. onCancel(e);
  42. } else {
  43. close();
  44. }
  45. }
  46. function render(props: T) {
  47. clearTimeout(timeoutId);
  48. timeoutId = setTimeout(() => {
  49. const rootPrefixCls = global.getPrefixCls();
  50. const iconPrefixCls = global.getIconPrefixCls();
  51. const theme = global.getTheme();
  52. const dom = (
  53. <CustomModel {...props} onCancel={handleCancel} afterClose={handleAfterClose}></CustomModel>
  54. );
  55. root.render(
  56. <ConfigProvider
  57. prefixCls={rootPrefixCls}
  58. iconPrefixCls={iconPrefixCls}
  59. theme={theme}
  60. locale={zhCN}
  61. >
  62. {global.holderRender ? global.holderRender(dom) : dom}
  63. </ConfigProvider>,
  64. );
  65. });
  66. }
  67. function close() {
  68. render({ ...modalProps, open: false });
  69. }
  70. render({ ...modalProps, open: true });
  71. return {
  72. close,
  73. };
  74. };
  75. /**
  76. * Generates a custom hook for managing an Ant Design modal.
  77. *
  78. * @param modal - The function that renders the modal content.
  79. * @param key - The key for the modal.
  80. * @return The modal component, open function, and close function.
  81. */
  82. export const useAntdModal = <T extends ModalProps>(
  83. modal: (props: T) => React.ReactNode,
  84. key: React.Key,
  85. ) => {
  86. const [visible, setVisible] = useState(false);
  87. const [props, setProps] = useState<T>({} as T);
  88. const CustomModel = modal;
  89. const open = (props: T) => {
  90. setProps(props);
  91. setVisible(true);
  92. };
  93. const close = () => {
  94. setVisible(false);
  95. };
  96. return [<CustomModel key={key} open={visible} {...props} />, open, close] as const;
  97. };