From e86a7906fe27a48a3c53078404169af52603573a Mon Sep 17 00:00:00 2001 From: Netfan Date: Mon, 30 Dec 2024 21:18:31 +0800 Subject: [PATCH] feat: drawer support `onOpened` & `onClosed` --- docs/src/components/common-ui/vben-drawer.md | 14 +++++---- .../src/drawer/__tests__/drawer-api.test.ts | 15 ++++++++++ .../ui-kit/popup-ui/src/drawer/drawer-api.ts | 29 ++++++++++++++++++- .../ui-kit/popup-ui/src/drawer/drawer.ts | 10 +++++++ .../ui-kit/popup-ui/src/drawer/drawer.vue | 2 ++ .../shadcn-ui/src/ui/sheet/SheetContent.vue | 19 ++++++++++-- 6 files changed, 80 insertions(+), 9 deletions(-) diff --git a/docs/src/components/common-ui/vben-drawer.md b/docs/src/components/common-ui/vben-drawer.md index 7091570f1..6659a3de3 100644 --- a/docs/src/components/common-ui/vben-drawer.md +++ b/docs/src/components/common-ui/vben-drawer.md @@ -108,12 +108,14 @@ const [Drawer, drawerApi] = useVbenDrawer({ 以下事件,只有在 `useVbenDrawer({onCancel:()=>{}})` 中传入才会生效。 -| 事件名 | 描述 | 类型 | -| --- | --- | --- | -| onBeforeClose | 关闭前触发,返回 `false`则禁止关闭 | `()=>boolean` | -| onCancel | 点击取消按钮触发 | `()=>void` | -| onConfirm | 点击确认按钮触发 | `()=>void` | -| onOpenChange | 关闭或者打开弹窗时触发 | `(isOpen:boolean)=>void` | +| 事件名 | 描述 | 类型 | 版本限制 | +| --- | --- | --- | --- | +| onBeforeClose | 关闭前触发,返回 `false`则禁止关闭 | `()=>boolean` | --- | +| onCancel | 点击取消按钮触发 | `()=>void` | --- | +| onClosed | 关闭动画播放完毕时触发 | `()=>void` | >5.5.2 | +| onConfirm | 点击确认按钮触发 | `()=>void` | --- | +| onOpenChange | 关闭或者打开弹窗时触发 | `(isOpen:boolean)=>void` | --- | +| onOpened | 打开动画播放完毕时触发 | `()=>void` | >5.5.2 | ### Slots diff --git a/packages/@core/ui-kit/popup-ui/src/drawer/__tests__/drawer-api.test.ts b/packages/@core/ui-kit/popup-ui/src/drawer/__tests__/drawer-api.test.ts index 8d715ff82..b55a9dac5 100644 --- a/packages/@core/ui-kit/popup-ui/src/drawer/__tests__/drawer-api.test.ts +++ b/packages/@core/ui-kit/popup-ui/src/drawer/__tests__/drawer-api.test.ts @@ -110,4 +110,19 @@ describe('drawerApi', () => { expect(drawerApi.store.state.title).toBe('Batch Title'); expect(drawerApi.store.state.confirmText).toBe('Batch Confirm'); }); + + it('should call onClosed callback when provided', () => { + const onClosed = vi.fn(); + const drawerApiWithHook = new DrawerApi({ onClosed }); + drawerApiWithHook.onClosed(); + expect(onClosed).toHaveBeenCalled(); + }); + + it('should call onOpened callback when provided', () => { + const onOpened = vi.fn(); + const drawerApiWithHook = new DrawerApi({ onOpened }); + drawerApiWithHook.open(); + drawerApiWithHook.onOpened(); + expect(onOpened).toHaveBeenCalled(); + }); }); diff --git a/packages/@core/ui-kit/popup-ui/src/drawer/drawer-api.ts b/packages/@core/ui-kit/popup-ui/src/drawer/drawer-api.ts index 6ceee0889..4e65abda1 100644 --- a/packages/@core/ui-kit/popup-ui/src/drawer/drawer-api.ts +++ b/packages/@core/ui-kit/popup-ui/src/drawer/drawer-api.ts @@ -6,7 +6,12 @@ import { bindMethods, isFunction } from '@vben-core/shared/utils'; export class DrawerApi { private api: Pick< DrawerApiOptions, - 'onBeforeClose' | 'onCancel' | 'onConfirm' | 'onOpenChange' + | 'onBeforeClose' + | 'onCancel' + | 'onClosed' + | 'onConfirm' + | 'onOpenChange' + | 'onOpened' >; // private prevState!: DrawerState; private state!: DrawerState; @@ -23,8 +28,10 @@ export class DrawerApi { connectedComponent: _, onBeforeClose, onCancel, + onClosed, onConfirm, onOpenChange, + onOpened, ...storeState } = options; @@ -68,8 +75,10 @@ export class DrawerApi { this.api = { onBeforeClose, onCancel, + onClosed, onConfirm, onOpenChange, + onOpened, }; bindMethods(this); } @@ -106,6 +115,15 @@ export class DrawerApi { } } + /** + * 弹窗关闭动画播放完毕后的回调 + */ + onClosed() { + if (!this.state.isOpen) { + this.api.onClosed?.(); + } + } + /** * 确认操作 */ @@ -113,6 +131,15 @@ export class DrawerApi { this.api.onConfirm?.(); } + /** + * 弹窗打开动画播放完毕后的回调 + */ + onOpened() { + if (this.state.isOpen) { + this.api.onOpened?.(); + } + } + open() { this.store.setState((prev) => ({ ...prev, isOpen: true })); } diff --git a/packages/@core/ui-kit/popup-ui/src/drawer/drawer.ts b/packages/@core/ui-kit/popup-ui/src/drawer/drawer.ts index f75ee50cb..f4c92006e 100644 --- a/packages/@core/ui-kit/popup-ui/src/drawer/drawer.ts +++ b/packages/@core/ui-kit/popup-ui/src/drawer/drawer.ts @@ -138,6 +138,11 @@ export interface DrawerApiOptions extends DrawerState { * 点击取消按钮的回调 */ onCancel?: () => void; + /** + * 弹窗关闭动画结束的回调 + * @returns + */ + onClosed?: () => void; /** * 点击确定按钮的回调 */ @@ -148,4 +153,9 @@ export interface DrawerApiOptions extends DrawerState { * @returns */ onOpenChange?: (isOpen: boolean) => void; + /** + * 弹窗打开动画结束的回调 + * @returns + */ + onOpened?: () => void; } diff --git a/packages/@core/ui-kit/popup-ui/src/drawer/drawer.vue b/packages/@core/ui-kit/popup-ui/src/drawer/drawer.vue index 31a409882..39f9a38d1 100644 --- a/packages/@core/ui-kit/popup-ui/src/drawer/drawer.vue +++ b/packages/@core/ui-kit/popup-ui/src/drawer/drawer.vue @@ -139,10 +139,12 @@ const getAppendTo = computed(() => { :side="placement" :z-index="zIndex" @close-auto-focus="handleFocusOutside" + @closed="() => drawerApi?.onClosed()" @escape-key-down="escapeKeyDown" @focus-outside="handleFocusOutside" @interact-outside="interactOutside" @open-auto-focus="handerOpenAutoFocus" + @opened="() => drawerApi?.onOpened()" @pointer-down-outside="pointerDownOutside" > -import { computed } from 'vue'; +import { computed, ref } from 'vue'; import { cn } from '@vben-core/shared/utils'; @@ -32,7 +32,9 @@ const props = withDefaults(defineProps(), { zIndex: 1000, }); -const emits = defineEmits(); +const emits = defineEmits< + { close: []; closed: []; opened: [] } & DialogContentEmits +>(); const delegatedProps = computed(() => { const { @@ -59,6 +61,17 @@ const position = computed(() => { }); const forwarded = useForwardPropsEmits(delegatedProps, emits); +const contentRef = ref | null>(null); +function onAnimationEnd(event: AnimationEvent) { + // 只有在 contentRef 的动画结束时才触发 opened/closed 事件 + if (event.target === contentRef.value?.$el) { + if (props.open) { + emits('opened'); + } else { + emits('closed'); + } + } +}