{"version":3,"file":"ToastRootImpl.cjs","sources":["../../src/Toast/ToastRootImpl.vue"],"sourcesContent":["<script lang=\"ts\">\nimport { isClient } from '@vueuse/shared'\nimport type { PrimitiveProps } from '@/Primitive'\nimport type { SwipeEvent } from './utils'\nimport { createContext, getActiveElement, useForwardExpose } from '@/shared'\nimport { useCollection } from '@/Collection'\n\nexport type ToastRootImplEmits = {\n  close: []\n  /** Event handler called when the escape key is down. It can be prevented by calling `event.preventDefault`. */\n  escapeKeyDown: [event: KeyboardEvent]\n  /** Event handler called when the dismiss timer is paused. This occurs when the pointer is moved over the viewport, the viewport is focused or when the window is blurred. */\n  pause: []\n  /** Event handler called when the dismiss timer is resumed. This occurs when the pointer is moved away from the viewport, the viewport is blurred or when the window is focused. */\n  resume: []\n  /** Event handler called when starting a swipe interaction. It can be prevented by calling `event.preventDefault`. */\n  swipeStart: [event: SwipeEvent]\n  /** Event handler called during a swipe interaction. It can be prevented by calling `event.preventDefault`. */\n  swipeMove: [event: SwipeEvent]\n  swipeCancel: [event: SwipeEvent]\n  /** Event handler called at the end of a swipe interaction. It can be prevented by calling `event.preventDefault`. */\n  swipeEnd: [event: SwipeEvent]\n}\n\nexport interface ToastRootImplProps extends PrimitiveProps {\n  /**\n   * Control the sensitivity of the toast for accessibility purposes.\n   *\n   * For toasts that are the result of a user action, choose `foreground`. Toasts generated from background tasks should use `background`.\n   */\n  type?: 'foreground' | 'background'\n  /**\n   * The controlled open state of the dialog. Can be bind as `v-model:open`.\n   */\n  open?: boolean\n  /**\n   * Time in milliseconds that toast should remain visible for. Overrides value\n   * given to `ToastProvider`.\n   */\n  duration?: number\n}\n\nexport const [injectToastRootContext, provideToastRootContext]\n  = createContext<{ onClose: () => void }>('ToastRoot')\n</script>\n\n<script setup lang=\"ts\">\nimport { Primitive } from '@/Primitive'\nimport { computed, onMounted, onUnmounted, ref, watch, watchEffect } from 'vue'\nimport { injectToastProviderContext } from './ToastProvider.vue'\nimport { TOAST_SWIPE_CANCEL, TOAST_SWIPE_END, TOAST_SWIPE_MOVE, TOAST_SWIPE_START, VIEWPORT_PAUSE, VIEWPORT_RESUME, getAnnounceTextContent, handleAndDispatchCustomEvent, isDeltaInDirection } from './utils'\nimport { onKeyStroke, useRafFn } from '@vueuse/core'\nimport ToastAnnounce from './ToastAnnounce.vue'\n\ndefineOptions({\n  inheritAttrs: false,\n})\n\nconst props = withDefaults(defineProps<ToastRootImplProps>(), {\n  open: false,\n  as: 'li',\n})\n\nconst emits = defineEmits<ToastRootImplEmits>()\n\nconst { forwardRef, currentElement } = useForwardExpose()\nconst { CollectionItem } = useCollection()\n\nconst providerContext = injectToastProviderContext()\nconst pointerStartRef = ref<{ x: number, y: number } | null>(null)\nconst swipeDeltaRef = ref<{ x: number, y: number } | null>(null)\nconst duration = computed(\n  () => typeof props.duration === 'number'\n    ? props.duration\n    : providerContext.duration.value,\n)\n\nconst closeTimerStartTimeRef = ref(0)\nconst closeTimerRemainingTimeRef = ref(duration.value)\nconst closeTimerRef = ref(0)\nconst remainingTime = ref(duration.value)\n\nconst remainingRaf = useRafFn(() => {\n  const elapsedTime = new Date().getTime() - closeTimerStartTimeRef.value\n  remainingTime.value = Math.max(closeTimerRemainingTimeRef.value - elapsedTime, 0)\n}, { fpsLimit: 60 })\n\nfunction startTimer(duration: number) {\n  if (duration <= 0 || duration === Number.POSITIVE_INFINITY)\n    return\n  // startTimer is used inside a watch with immediate set to true.\n  // This results in code execution during SSR.\n  // Ensure this code only runs in a browser environment\n  if (!isClient)\n    return\n  window.clearTimeout(closeTimerRef.value)\n  closeTimerStartTimeRef.value = new Date().getTime()\n  closeTimerRef.value = window.setTimeout(handleClose, duration)\n}\n\nfunction handleClose(event?: PointerEvent) {\n  const isNonPointerEvent = event?.pointerType === ''\n\n  // reka: update to only perform focus when user focus via keyboard\n  // focus viewport if focus is within toast to read the remaining toast\n  // count to SR users and ensure focus isn't lost\n  const isFocusInToast = currentElement.value?.contains(getActiveElement())\n  if (isFocusInToast && isNonPointerEvent)\n    providerContext.viewport.value?.focus()\n\n  if (isNonPointerEvent) {\n    // when manually close the toast, we reset isClosePausedRef\n    providerContext.isClosePausedRef.value = false\n  }\n\n  emits('close')\n}\n\nconst announceTextContent = computed(() => currentElement.value ? getAnnounceTextContent(currentElement.value) : null)\n\nif (props.type && !['foreground', 'background'].includes(props.type)) {\n  const error = 'Invalid prop `type` supplied to `Toast`. Expected `foreground | background`.'\n  throw new Error(error)\n}\n\nwatchEffect((cleanupFn) => {\n  const viewport = providerContext.viewport.value\n  if (viewport) {\n    const handleResume = () => {\n      startTimer(closeTimerRemainingTimeRef.value)\n      remainingRaf.resume()\n      emits('resume')\n    }\n    const handlePause = () => {\n      const elapsedTime = new Date().getTime() - closeTimerStartTimeRef.value\n      closeTimerRemainingTimeRef.value = closeTimerRemainingTimeRef.value - elapsedTime\n      window.clearTimeout(closeTimerRef.value)\n      remainingRaf.pause()\n      emits('pause')\n    }\n    viewport.addEventListener(VIEWPORT_PAUSE, handlePause)\n    viewport.addEventListener(VIEWPORT_RESUME, handleResume)\n    return () => {\n      viewport.removeEventListener(VIEWPORT_PAUSE, handlePause)\n      viewport.removeEventListener(VIEWPORT_RESUME, handleResume)\n    }\n  }\n})\n\n// start timer when toast opens or duration changes.\n// we include `open` in deps because closed !== unmounted when animating\n// so it could reopen before being completely unmounted\nwatch(() => [props.open, duration.value], () => {\n  // Reset the timer when the toast is rerendered with the new duration\n  closeTimerRemainingTimeRef.value = duration.value\n\n  if (props.open && !providerContext.isClosePausedRef.value)\n    startTimer(duration.value)\n}, { immediate: true })\n\nonKeyStroke('Escape', (event) => {\n  emits('escapeKeyDown', event)\n  if (!event.defaultPrevented) {\n    providerContext.isFocusedToastEscapeKeyDownRef.value = true\n    handleClose()\n  }\n})\n\nonMounted(() => {\n  providerContext.onToastAdd()\n})\nonUnmounted(() => {\n  providerContext.onToastRemove()\n})\n\nprovideToastRootContext({ onClose: handleClose })\n</script>\n\n<template>\n  <ToastAnnounce\n    v-if=\"announceTextContent\"\n    role=\"alert\"\n    :aria-live=\"type === 'foreground' ? 'assertive' : 'polite'\"\n    aria-atomic=\"true\"\n  >\n    {{ announceTextContent }}\n  </ToastAnnounce>\n\n  <Teleport\n    v-if=\"providerContext.viewport.value\"\n    :to=\"providerContext.viewport.value\"\n  >\n    <CollectionItem>\n      <Primitive\n        :ref=\"forwardRef\"\n        role=\"alert\"\n        aria-live=\"off\"\n        aria-atomic=\"true\"\n        tabindex=\"0\"\n        v-bind=\"$attrs\"\n        :as=\"as\"\n        :as-child=\"asChild\"\n        :data-state=\"open ? 'open' : 'closed'\"\n        :data-swipe-direction=\"providerContext.swipeDirection.value\"\n        :style=\"{ userSelect: 'none', touchAction: 'none' }\"\n        @pointerdown.left=\"(event: PointerEvent) => {\n          pointerStartRef = { x: event.clientX, y: event.clientY };\n        }\"\n        @pointermove=\"(event: PointerEvent) => {\n          if (!pointerStartRef) return;\n          const x = event.clientX - pointerStartRef.x;\n          const y = event.clientY - pointerStartRef.y;\n          const hasSwipeMoveStarted = Boolean(swipeDeltaRef);\n          const isHorizontalSwipe = ['left', 'right'].includes(providerContext.swipeDirection.value);\n          const clamp = ['left', 'up'].includes(providerContext.swipeDirection.value)\n            ? Math.min\n            : Math.max;\n          const clampedX = isHorizontalSwipe ? clamp(0, x) : 0;\n          const clampedY = !isHorizontalSwipe ? clamp(0, y) : 0;\n          const moveStartBuffer = event.pointerType === 'touch' ? 10 : 2;\n          const delta = { x: clampedX, y: clampedY };\n          const eventDetail = { originalEvent: event, delta };\n          if (hasSwipeMoveStarted) {\n            swipeDeltaRef = delta;\n            handleAndDispatchCustomEvent(TOAST_SWIPE_MOVE, (ev: SwipeEvent) => emits('swipeMove', ev), eventDetail);\n          }\n          else if (isDeltaInDirection(delta, providerContext.swipeDirection.value, moveStartBuffer)) {\n            swipeDeltaRef = delta;\n            handleAndDispatchCustomEvent(TOAST_SWIPE_START, (ev: SwipeEvent) => emits('swipeStart', ev), eventDetail);\n            (event.target as HTMLElement).setPointerCapture(event.pointerId);\n          }\n          else if (Math.abs(x) > moveStartBuffer || Math.abs(y) > moveStartBuffer) {\n            // User is swiping in wrong direction so we disable swipe gesture\n            // for the current pointer down interaction\n            pointerStartRef = null;\n          }\n        }\"\n        @pointerup=\"(event: PointerEvent) => {\n          const delta = swipeDeltaRef;\n          const target = event.target as HTMLElement;\n          if (target.hasPointerCapture(event.pointerId)) {\n            target.releasePointerCapture(event.pointerId);\n          }\n          swipeDeltaRef = null;\n          pointerStartRef = null;\n          if (delta) {\n            const toast = event.currentTarget;\n            const eventDetail = { originalEvent: event, delta };\n            if (\n              isDeltaInDirection(delta, providerContext.swipeDirection.value, providerContext.swipeThreshold.value)\n            ) {\n              handleAndDispatchCustomEvent(TOAST_SWIPE_END, (ev: SwipeEvent) => emits('swipeEnd', ev), eventDetail);\n            }\n            else {\n              handleAndDispatchCustomEvent(TOAST_SWIPE_CANCEL, (ev: SwipeEvent) => emits('swipeCancel', ev), eventDetail);\n            }\n            // Prevent click event from triggering on items within the toast when\n            // pointer up is part of a swipe gesture\n            toast?.addEventListener('click', (event) => event.preventDefault(), {\n              once: true,\n            });\n          }\n        }\"\n      >\n        <slot\n          :remaining=\"remainingTime\"\n          :duration=\"duration\"\n        />\n      </Primitive>\n    </CollectionItem>\n  </Teleport>\n</template>\n"],"names":["createContext","useForwardExpose","useCollection","injectToastProviderContext","ref","computed","useRafFn","duration","isClient","getActiveElement","getAnnounceTextContent","watchEffect","VIEWPORT_PAUSE","VIEWPORT_RESUME","watch","onKeyStroke","onMounted","onUnmounted"],"mappings":"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"}