use-modal.ts 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. import type { ExtendedModalApi, ModalApiOptions, ModalProps } from './modal';
  2. import { defineComponent, h, inject, nextTick, provide, reactive } from 'vue';
  3. import { useStore } from '@vben-core/shared/store';
  4. import VbenModal from './modal.vue';
  5. import { ModalApi } from './modal-api';
  6. const USER_MODAL_INJECT_KEY = Symbol('VBEN_MODAL_INJECT');
  7. export function useVbenModal<TParentModalProps extends ModalProps = ModalProps>(
  8. options: ModalApiOptions = {},
  9. ) {
  10. // Modal一般会抽离出来,所以如果有传入 connectedComponent,则表示为外部调用,与内部组件进行连接
  11. // 外部的Modal通过provide/inject传递api
  12. const { connectedComponent } = options;
  13. if (connectedComponent) {
  14. const extendedApi = reactive({});
  15. const Modal = defineComponent(
  16. (props: TParentModalProps, { attrs, slots }) => {
  17. provide(USER_MODAL_INJECT_KEY, {
  18. extendApi(api: ExtendedModalApi) {
  19. // 不能直接给 reactive 赋值,会丢失响应
  20. // 不能用 Object.assign,会丢失 api 的原型函数
  21. Object.setPrototypeOf(extendedApi, api);
  22. },
  23. options,
  24. });
  25. checkProps(extendedApi as ExtendedModalApi, {
  26. ...props,
  27. ...attrs,
  28. ...slots,
  29. });
  30. return () =>
  31. h(
  32. connectedComponent,
  33. {
  34. ...props,
  35. ...attrs,
  36. },
  37. slots,
  38. );
  39. },
  40. {
  41. inheritAttrs: false,
  42. name: 'VbenParentModal',
  43. },
  44. );
  45. return [Modal, extendedApi as ExtendedModalApi] as const;
  46. }
  47. const injectData = inject<any>(USER_MODAL_INJECT_KEY, {});
  48. const mergedOptions = {
  49. ...injectData.options,
  50. ...options,
  51. } as ModalApiOptions;
  52. mergedOptions.onOpenChange = (isOpen: boolean) => {
  53. options.onOpenChange?.(isOpen);
  54. injectData.options?.onOpenChange?.(isOpen);
  55. };
  56. const api = new ModalApi(mergedOptions);
  57. const extendedApi: ExtendedModalApi = api as never;
  58. extendedApi.useStore = (selector) => {
  59. return useStore(api.store, selector);
  60. };
  61. const Modal = defineComponent(
  62. (props: ModalProps, { attrs, slots }) => {
  63. return () =>
  64. h(
  65. VbenModal,
  66. {
  67. ...props,
  68. ...attrs,
  69. modalApi: extendedApi,
  70. },
  71. slots,
  72. );
  73. },
  74. {
  75. inheritAttrs: false,
  76. name: 'VbenModal',
  77. },
  78. );
  79. injectData.extendApi?.(extendedApi);
  80. return [Modal, extendedApi] as const;
  81. }
  82. async function checkProps(api: ExtendedModalApi, attrs: Record<string, any>) {
  83. if (!attrs || Object.keys(attrs).length === 0) {
  84. return;
  85. }
  86. await nextTick();
  87. const state = api?.store?.state;
  88. if (!state) {
  89. return;
  90. }
  91. const stateKeys = new Set(Object.keys(state));
  92. for (const attr of Object.keys(attrs)) {
  93. if (stateKeys.has(attr) && !['class'].includes(attr)) {
  94. // connectedComponent存在时,不要传入Modal的props,会造成复杂度提升,如果你需要修改Modal的props,请使用 useModal 或者api
  95. console.warn(
  96. `[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.`,
  97. );
  98. }
  99. }
  100. }