From 4a0ad492752cac8e9938a893ce63f23c03533297 Mon Sep 17 00:00:00 2001 From: Sadegh Barati Date: Wed, 4 Dec 2024 05:05:42 +0330 Subject: [PATCH] fix(Presence): ensure onAnimationEnd when closing (#1471) (from radix-ui) --- .../radix-vue/src/Presence/usePresence.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/packages/radix-vue/src/Presence/usePresence.ts b/packages/radix-vue/src/Presence/usePresence.ts index 209cade7e..d065b0405 100644 --- a/packages/radix-vue/src/Presence/usePresence.ts +++ b/packages/radix-vue/src/Presence/usePresence.ts @@ -1,6 +1,7 @@ import { type Ref, computed, nextTick, onUnmounted, ref, watch } from 'vue' import { useStateMachine } from '@/shared' import { isClient } from '@vueuse/shared' +import { defaultWindow } from '@vueuse/core' export function usePresence( present: Ref, @@ -8,7 +9,10 @@ export function usePresence( ) { const stylesRef = ref({} as any) const prevAnimationNameRef = ref('none') + const prevPresentRef = ref(present) const initialState = present.value ? 'mounted' : 'unmounted' + let timeoutId: number | undefined + const ownerWindow = node.value?.ownerDocument.defaultView ?? defaultWindow const { state, dispatch } = useStateMachine(initialState, { mounted: { @@ -94,6 +98,20 @@ export function usePresence( if (event.target === node.value && isCurrentAnimation) { dispatchCustomEvent(`after-${directionName}`) dispatch('ANIMATION_END') + + if (!prevPresentRef.value) { + const currentFillMode = node.value.style.animationFillMode + node.value.style.animationFillMode = 'forwards' + // Reset the style after the node had time to unmount (for cases + // where the component chooses not to unmount). Doing this any + // sooner than `setTimeout` (e.g. with `requestAnimationFrame`) + // still causes a flash. + timeoutId = ownerWindow?.setTimeout(() => { + if (node.value?.style.animationFillMode === 'forwards') { + node.value.style.animationFillMode = currentFillMode + } + }) + } } // if no animation, immediately trigger 'ANIMATION_END' if (event.target === node.value && currentAnimationName === 'none') @@ -120,6 +138,7 @@ export function usePresence( // We avoid doing so during cleanup as the node may change but still exist. dispatch('ANIMATION_END') + ownerWindow?.clearTimeout(timeoutId) oldNode?.removeEventListener('animationstart', handleAnimationStart) oldNode?.removeEventListener('animationcancel', handleAnimationEnd) oldNode?.removeEventListener('animationend', handleAnimationEnd)