use-drawer.ts 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. import type {
  2. DrawerApiOptions,
  3. DrawerProps,
  4. ExtendedDrawerApi,
  5. } from './drawer';
  6. import {
  7. defineComponent,
  8. h,
  9. inject,
  10. nextTick,
  11. provide,
  12. reactive,
  13. ref,
  14. } from 'vue';
  15. import { useStore } from '@vben-core/shared/store';
  16. import { DrawerApi } from './drawer-api';
  17. import VbenDrawer from './drawer.vue';
  18. const USER_DRAWER_INJECT_KEY = Symbol('VBEN_DRAWER_INJECT');
  19. const DEFAULT_DRAWER_PROPS: Partial<DrawerProps> = {};
  20. export function setDefaultDrawerProps(props: Partial<DrawerProps>) {
  21. Object.assign(DEFAULT_DRAWER_PROPS, props);
  22. }
  23. export function useVbenDrawer<
  24. TParentDrawerProps extends DrawerProps = DrawerProps,
  25. >(options: DrawerApiOptions = {}) {
  26. // Drawer一般会抽离出来,所以如果有传入 connectedComponent,则表示为外部调用,与内部组件进行连接
  27. // 外部的Drawer通过provide/inject传递api
  28. const { connectedComponent } = options;
  29. if (connectedComponent) {
  30. const extendedApi = reactive({});
  31. const isDrawerReady = ref(true);
  32. const Drawer = defineComponent(
  33. (props: TParentDrawerProps, { attrs, slots }) => {
  34. provide(USER_DRAWER_INJECT_KEY, {
  35. extendApi(api: ExtendedDrawerApi) {
  36. // 不能直接给 reactive 赋值,会丢失响应
  37. // 不能用 Object.assign,会丢失 api 的原型函数
  38. Object.setPrototypeOf(extendedApi, api);
  39. },
  40. options,
  41. async reCreateDrawer() {
  42. isDrawerReady.value = false;
  43. await nextTick();
  44. isDrawerReady.value = true;
  45. },
  46. });
  47. checkProps(extendedApi as ExtendedDrawerApi, {
  48. ...props,
  49. ...attrs,
  50. ...slots,
  51. });
  52. return () =>
  53. h(
  54. isDrawerReady.value ? connectedComponent : 'div',
  55. { ...props, ...attrs },
  56. slots,
  57. );
  58. },
  59. {
  60. inheritAttrs: false,
  61. name: 'VbenParentDrawer',
  62. },
  63. );
  64. return [Drawer, extendedApi as ExtendedDrawerApi] as const;
  65. }
  66. const injectData = inject<any>(USER_DRAWER_INJECT_KEY, {});
  67. const mergedOptions = {
  68. ...DEFAULT_DRAWER_PROPS,
  69. ...injectData.options,
  70. ...options,
  71. } as DrawerApiOptions;
  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.reCreateDrawer?.();
  81. }
  82. };
  83. const api = new DrawerApi(mergedOptions);
  84. const extendedApi: ExtendedDrawerApi = api as never;
  85. extendedApi.useStore = (selector) => {
  86. return useStore(api.store, selector);
  87. };
  88. const Drawer = defineComponent(
  89. (props: DrawerProps, { attrs, slots }) => {
  90. return () =>
  91. h(VbenDrawer, { ...props, ...attrs, drawerApi: extendedApi }, slots);
  92. },
  93. {
  94. inheritAttrs: false,
  95. name: 'VbenDrawer',
  96. },
  97. );
  98. injectData.extendApi?.(extendedApi);
  99. return [Drawer, extendedApi] as const;
  100. }
  101. async function checkProps(api: ExtendedDrawerApi, attrs: Record<string, any>) {
  102. if (!attrs || Object.keys(attrs).length === 0) {
  103. return;
  104. }
  105. await nextTick();
  106. const state = api?.store?.state;
  107. if (!state) {
  108. return;
  109. }
  110. const stateKeys = new Set(Object.keys(state));
  111. for (const attr of Object.keys(attrs)) {
  112. if (stateKeys.has(attr) && !['class'].includes(attr)) {
  113. // connectedComponent存在时,不要传入Drawer的props,会造成复杂度提升,如果你需要修改Drawer的props,请使用 useVbenDrawer 或者api
  114. console.warn(
  115. `[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.`,
  116. );
  117. }
  118. }
  119. }