import type { DrawerApiOptions, DrawerProps, ExtendedDrawerApi, } from './drawer'; import { defineComponent, h, inject, nextTick, onDeactivated, provide, reactive, ref, } from 'vue'; import { useStore } from '@vben-core/shared/store'; import { DrawerApi } from './drawer-api'; import VbenDrawer from './drawer.vue'; const USER_DRAWER_INJECT_KEY = Symbol('VBEN_DRAWER_INJECT'); const DEFAULT_DRAWER_PROPS: Partial = {}; export function setDefaultDrawerProps(props: Partial) { Object.assign(DEFAULT_DRAWER_PROPS, props); } export function useVbenDrawer< TParentDrawerProps extends DrawerProps = DrawerProps, >(options: DrawerApiOptions = {}) { // Drawer一般会抽离出来,所以如果有传入 connectedComponent,则表示为外部调用,与内部组件进行连接 // 外部的Drawer通过provide/inject传递api const { connectedComponent } = options; if (connectedComponent) { const extendedApi = reactive({}); const isDrawerReady = ref(true); const Drawer = defineComponent( (props: TParentDrawerProps, { attrs, slots }) => { provide(USER_DRAWER_INJECT_KEY, { extendApi(api: ExtendedDrawerApi) { // 不能直接给 reactive 赋值,会丢失响应 // 不能用 Object.assign,会丢失 api 的原型函数 Object.setPrototypeOf(extendedApi, api); }, options, async reCreateDrawer() { isDrawerReady.value = false; await nextTick(); isDrawerReady.value = true; }, }); checkProps(extendedApi as ExtendedDrawerApi, { ...props, ...attrs, ...slots, }); return () => h( isDrawerReady.value ? connectedComponent : 'div', { ...props, ...attrs }, slots, ); }, // eslint-disable-next-line vue/one-component-per-file { name: 'VbenParentDrawer', inheritAttrs: false, }, ); /** * 在开启keepAlive情况下 直接通过浏览器按钮/手势等返回 不会关闭弹窗 */ onDeactivated(() => { (extendedApi as ExtendedDrawerApi)?.close?.(); }); return [Drawer, extendedApi as ExtendedDrawerApi] as const; } const injectData = inject(USER_DRAWER_INJECT_KEY, {}); const mergedOptions = { ...DEFAULT_DRAWER_PROPS, ...injectData.options, ...options, } as DrawerApiOptions; mergedOptions.onOpenChange = (isOpen: boolean) => { options.onOpenChange?.(isOpen); injectData.options?.onOpenChange?.(isOpen); }; const onClosed = mergedOptions.onClosed; mergedOptions.onClosed = () => { onClosed?.(); if (mergedOptions.destroyOnClose) { injectData.reCreateDrawer?.(); } }; const api = new DrawerApi(mergedOptions); const extendedApi: ExtendedDrawerApi = api as never; extendedApi.useStore = (selector) => { return useStore(api.store, selector); }; const Drawer = defineComponent( (props: DrawerProps, { attrs, slots }) => { return () => h(VbenDrawer, { ...props, ...attrs, drawerApi: extendedApi }, slots); }, // eslint-disable-next-line vue/one-component-per-file { name: 'VbenDrawer', inheritAttrs: false, }, ); injectData.extendApi?.(extendedApi); return [Drawer, extendedApi] as const; } async function checkProps(api: ExtendedDrawerApi, attrs: Record) { if (!attrs || Object.keys(attrs).length === 0) { return; } await nextTick(); const state = api?.store?.state; if (!state) { return; } const stateKeys = new Set(Object.keys(state)); for (const attr of Object.keys(attrs)) { if (stateKeys.has(attr) && !['class'].includes(attr)) { // connectedComponent存在时,不要传入Drawer的props,会造成复杂度提升,如果你需要修改Drawer的props,请使用 useVbenDrawer 或者api console.warn( `[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.`, ); } } }