use-drawer.ts 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. import type {
  2. DrawerApiOptions,
  3. DrawerProps,
  4. ExtendedDrawerApi,
  5. } from './drawer';
  6. import {
  7. defineComponent,
  8. h,
  9. inject,
  10. nextTick,
  11. onDeactivated,
  12. provide,
  13. reactive,
  14. ref,
  15. } from 'vue';
  16. import { useStore } from '@vben-core/shared/store';
  17. import { DrawerApi } from './drawer-api';
  18. import VbenDrawer from './drawer.vue';
  19. const USER_DRAWER_INJECT_KEY = Symbol('VBEN_DRAWER_INJECT');
  20. const DEFAULT_DRAWER_PROPS: Partial<DrawerProps> = {};
  21. export function setDefaultDrawerProps(props: Partial<DrawerProps>) {
  22. Object.assign(DEFAULT_DRAWER_PROPS, props);
  23. }
  24. export function useVbenDrawer<
  25. TParentDrawerProps extends DrawerProps = DrawerProps,
  26. >(options: DrawerApiOptions = {}) {
  27. // Drawer一般会抽离出来,所以如果有传入 connectedComponent,则表示为外部调用,与内部组件进行连接
  28. // 外部的Drawer通过provide/inject传递api
  29. const { connectedComponent } = options;
  30. if (connectedComponent) {
  31. const extendedApi = reactive({});
  32. const isDrawerReady = ref(true);
  33. const Drawer = defineComponent(
  34. (props: TParentDrawerProps, { attrs, slots }) => {
  35. provide(USER_DRAWER_INJECT_KEY, {
  36. extendApi(api: ExtendedDrawerApi) {
  37. // 不能直接给 reactive 赋值,会丢失响应
  38. // 不能用 Object.assign,会丢失 api 的原型函数
  39. Object.setPrototypeOf(extendedApi, api);
  40. },
  41. options,
  42. async reCreateDrawer() {
  43. isDrawerReady.value = false;
  44. await nextTick();
  45. isDrawerReady.value = true;
  46. },
  47. });
  48. checkProps(extendedApi as ExtendedDrawerApi, {
  49. ...props,
  50. ...attrs,
  51. ...slots,
  52. });
  53. return () =>
  54. h(
  55. isDrawerReady.value ? connectedComponent : 'div',
  56. { ...props, ...attrs },
  57. slots,
  58. );
  59. },
  60. // eslint-disable-next-line vue/one-component-per-file
  61. {
  62. name: 'VbenParentDrawer',
  63. inheritAttrs: false,
  64. },
  65. );
  66. /**
  67. * 在开启keepAlive情况下 直接通过浏览器按钮/手势等返回 不会关闭弹窗
  68. */
  69. onDeactivated(() => {
  70. (extendedApi as ExtendedDrawerApi)?.close?.();
  71. });
  72. return [Drawer, extendedApi as ExtendedDrawerApi] as const;
  73. }
  74. const injectData = inject<any>(USER_DRAWER_INJECT_KEY, {});
  75. const mergedOptions = {
  76. ...DEFAULT_DRAWER_PROPS,
  77. ...injectData.options,
  78. ...options,
  79. } as DrawerApiOptions;
  80. mergedOptions.onOpenChange = (isOpen: boolean) => {
  81. options.onOpenChange?.(isOpen);
  82. injectData.options?.onOpenChange?.(isOpen);
  83. };
  84. const onClosed = mergedOptions.onClosed;
  85. mergedOptions.onClosed = () => {
  86. onClosed?.();
  87. if (mergedOptions.destroyOnClose) {
  88. injectData.reCreateDrawer?.();
  89. }
  90. };
  91. const api = new DrawerApi(mergedOptions);
  92. const extendedApi: ExtendedDrawerApi = api as never;
  93. extendedApi.useStore = (selector) => {
  94. return useStore(api.store, selector);
  95. };
  96. const Drawer = defineComponent(
  97. (props: DrawerProps, { attrs, slots }) => {
  98. return () =>
  99. h(VbenDrawer, { ...props, ...attrs, drawerApi: extendedApi }, slots);
  100. },
  101. // eslint-disable-next-line vue/one-component-per-file
  102. {
  103. name: 'VbenDrawer',
  104. inheritAttrs: false,
  105. },
  106. );
  107. injectData.extendApi?.(extendedApi);
  108. return [Drawer, extendedApi] as const;
  109. }
  110. async function checkProps(api: ExtendedDrawerApi, 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存在时,不要传入Drawer的props,会造成复杂度提升,如果你需要修改Drawer的props,请使用 useVbenDrawer 或者api
  123. console.warn(
  124. `[Vben Drawer]: When 'connectedComponent' exists, do not set props or slots '${attr}', which will increase complexity. If you need to modify the props of Drawer, please use useVbenDrawer or api.`,
  125. );
  126. }
  127. }
  128. }