{"version":3,"file":"presence.mjs","sources":["../src/presence/usePresence.ts"],"sourcesContent":["import { computed, onWatcherCleanup, type Ref, toValue, watch, watchEffect } from 'vue'\nimport { useStateMachine } from '../hooks/index.ts'\n\nfunction getAnimationName(styles?: CSSStyleDeclaration) {\n  return styles?.animationName || 'none'\n}\n\nexport function usePresence(\n  elRef: Ref<HTMLElement | undefined>,\n  present: Ref<boolean> | (() => boolean),\n  onChange?: (value: boolean) => void,\n) {\n  let styles: CSSStyleDeclaration = {} as CSSStyleDeclaration\n  let prevAnimationName = 'none'\n  const initialState = toValue(present) ? 'mounted' : 'unmounted'\n\n  const [state, send] = useStateMachine(initialState, {\n    mounted: {\n      UNMOUNT: 'unmounted',\n      ANIMATION_OUT: 'unmountSuspended',\n    },\n    unmountSuspended: {\n      MOUNT: 'mounted',\n      ANIMATION_END: 'unmounted',\n    },\n    unmounted: {\n      MOUNT: 'mounted',\n    },\n  })\n\n  watchEffect(() => {\n    const currentAnimationName = getAnimationName(styles)\n    prevAnimationName = state.value === 'mounted' ? currentAnimationName : 'none'\n  })\n\n  watch(present, async (present, wasPresent) => {\n    onChange?.(present)\n\n    const currentAnimationName = getAnimationName(styles)\n\n    if (present) {\n      send('MOUNT')\n    }\n    else if (currentAnimationName === 'none' || styles?.display === 'none') {\n      // If there is no exit animation or the element is hidden, animations won't run\n      // so we unmount instantly\n      send('UNMOUNT')\n    }\n    else {\n      /**\n       * When `present` changes to `false`, we check changes to animation-name to\n       * determine whether an animation has started. We chose this approach (reading\n       * computed styles) because there is no `animationrun` event and `animationstart`\n       * fires after `animation-delay` has expired which would be too late.\n       */\n      const isAnimating = prevAnimationName !== currentAnimationName\n\n      if (wasPresent && isAnimating) {\n        send('ANIMATION_OUT')\n      }\n      else {\n        send('UNMOUNT')\n      }\n    }\n  }, {\n    flush: 'post',\n  })\n\n  /**\n   * Triggering an ANIMATION_OUT during an ANIMATION_IN will fire an `animationcancel`\n   * event for ANIMATION_IN after we have entered `unmountSuspended` state. So, we\n   * make sure we only trigger ANIMATION_END for the currently active animation.\n   */\n  async function handleAnimationEnd(event: AnimationEvent) {\n    const currentAnimationName = getAnimationName(styles)\n    const isCurrentAnimation = currentAnimationName.includes(\n      event.animationName,\n    )\n    if (event.target === elRef.value && isCurrentAnimation) {\n      // With React 18 concurrency this update is applied\n      // a frame after the animation ends, creating a flash of visible content.\n      // By manually flushing we ensure they sync within a frame, removing the flash.\n      send('ANIMATION_END')\n    }\n  }\n\n  function handleAnimationStart(event: AnimationEvent) {\n    if (event.target === elRef.value)\n      // if animation occurred, store its name as the previous animation.\n      prevAnimationName = getAnimationName(styles)\n  }\n\n  watch(elRef, (node) => {\n    if (node) {\n      styles = getComputedStyle(node)\n      node.addEventListener('animationstart', handleAnimationStart)\n      node.addEventListener('animationcancel', handleAnimationEnd)\n      node.addEventListener('animationend', handleAnimationEnd)\n\n      onWatcherCleanup(() => {\n        node.removeEventListener('animationstart', handleAnimationStart)\n        node.removeEventListener('animationcancel', handleAnimationEnd)\n        node.removeEventListener('animationend', handleAnimationEnd)\n      })\n    }\n    else {\n      // Transition to the unmounted state if the el is removed prematurely.\n      // We avoid doing so during cleanup as the el may change but still exist.\n      send('ANIMATION_END')\n    }\n  })\n\n  return computed(() => state.value === 'mounted' || state.value === 'unmountSuspended')\n}\n"],"names":["present"],"mappings":"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"}