use-modal.ts 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. import type { ExtendedModalApi, ModalApiOptions, ModalProps } from './modal';
  2. import {
  3. defineComponent,
  4. h,
  5. inject,
  6. nextTick,
  7. provide,
  8. reactive,
  9. ref,
  10. } from 'vue';
  11. import { useStore } from '@vben-core/shared/store';
  12. import { ModalApi } from './modal-api';
  13. import VbenModal from './modal.vue';
  14. const USER_MODAL_INJECT_KEY = Symbol('VBEN_MODAL_INJECT');
  15. const DEFAULT_MODAL_PROPS: Partial<ModalProps> = {};
  16. export function setDefaultModalProps(props: Partial<ModalProps>) {
  17. Object.assign(DEFAULT_MODAL_PROPS, props);
  18. }
  19. export function useVbenModal<TParentModalProps extends ModalProps = ModalProps>(
  20. options: ModalApiOptions = {},
  21. ) {
  22. // Modal一般会抽离出来,所以如果有传入 connectedComponent,则表示为外部调用,与内部组件进行连接
  23. // 外部的Modal通过provide/inject传递api
  24. const { connectedComponent } = options;
  25. if (connectedComponent) {
  26. const extendedApi = reactive({});
  27. const isModalReady = ref(true);
  28. const Modal = defineComponent(
  29. (props: TParentModalProps, { attrs, slots }) => {
  30. provide(USER_MODAL_INJECT_KEY, {
  31. extendApi(api: ExtendedModalApi) {
  32. // 不能直接给 reactive 赋值,会丢失响应
  33. // 不能用 Object.assign,会丢失 api 的原型函数
  34. Object.setPrototypeOf(extendedApi, api);
  35. },
  36. options,
  37. async reCreateModal() {
  38. isModalReady.value = false;
  39. await nextTick();
  40. isModalReady.value = true;
  41. },
  42. });
  43. checkProps(extendedApi as ExtendedModalApi, {
  44. ...props,
  45. ...attrs,
  46. ...slots,
  47. });
  48. return () =>
  49. h(
  50. isModalReady.value ? connectedComponent : 'div',
  51. {
  52. ...props,
  53. ...attrs,
  54. },
  55. slots,
  56. );
  57. },
  58. // eslint-disable-next-line vue/one-component-per-file
  59. {
  60. name: 'VbenParentModal',
  61. inheritAttrs: false,
  62. },
  63. );
  64. return [Modal, extendedApi as ExtendedModalApi] as const;
  65. }
  66. const injectData = inject<any>(USER_MODAL_INJECT_KEY, {});
  67. const mergedOptions = {
  68. ...DEFAULT_MODAL_PROPS,
  69. ...injectData.options,
  70. ...options,
  71. } as ModalApiOptions;
  72. mergedOptions.onOpenChange = (isOpen: boolean) => {
  73. options.onOpenChange?.(isOpen);
  74. injectData.options?.onOpenChange?.(isOpen);
  75. };
  76. const onClosed = mergedOptions.onClosed;
  77. mergedOptions.onClosed = () => {
  78. onClosed?.();
  79. if (mergedOptions.destroyOnClose) {
  80. injectData.reCreateModal?.();
  81. }
  82. };
  83. const api = new ModalApi(mergedOptions);
  84. const extendedApi: ExtendedModalApi = api as never;
  85. extendedApi.useStore = (selector) => {
  86. return useStore(api.store, selector);
  87. };
  88. const Modal = defineComponent(
  89. (props: ModalProps, { attrs, slots }) => {
  90. return () =>
  91. h(
  92. VbenModal,
  93. {
  94. ...props,
  95. ...attrs,
  96. modalApi: extendedApi,
  97. },
  98. slots,
  99. );
  100. },
  101. // eslint-disable-next-line vue/one-component-per-file
  102. {
  103. name: 'VbenModal',
  104. inheritAttrs: false,
  105. },
  106. );
  107. injectData.extendApi?.(extendedApi);
  108. return [Modal, extendedApi] as const;
  109. }
  110. async function checkProps(api: ExtendedModalApi, attrs: Record<string, any>) {
  111. if (!attrs || Object.keys(attrs).length === 0) {
  112. return;
  113. }
  114. await nextTick();
  115. const state = api?.store?.state;
  116. if (!state) {
  117. return;
  118. }
  119. const stateKeys = new Set(Object.keys(state));
  120. for (const attr of Object.keys(attrs)) {
  121. if (stateKeys.has(attr) && !['class'].includes(attr)) {
  122. // connectedComponent存在时,不要传入Modal的props,会造成复杂度提升,如果你需要修改Modal的props,请使用 useModal 或者api
  123. console.warn(
  124. `[Vben Modal]: When 'connectedComponent' exists, do not set props or slots '${attr}', which will increase complexity. If you need to modify the props of Modal, please use useVbenModal or api.`,
  125. );
  126. }
  127. }
  128. }