{"version":3,"file":"use-scroll-into-view.cjs","names":["useReducedMotion"],"sources":["../../src/use-scroll-into-view/use-scroll-into-view.ts"],"sourcesContent":["import { useCallback, useEffect, useRef } from 'react';\nimport { useReducedMotion } from '../use-reduced-motion/use-reduced-motion';\nimport { useWindowEvent } from '../use-window-event/use-window-event';\n\ninterface UseScrollIntoViewAnimation {\n  /** Target element alignment relatively to parent based on current axis */\n  alignment?: 'start' | 'end' | 'center';\n}\n\nexport interface UseScrollIntoViewOptions {\n  /** Callback fired after scroll */\n  onScrollFinish?: () => void;\n\n  /** Duration of scroll in milliseconds */\n  duration?: number;\n\n  /** Axis of scroll */\n  axis?: 'x' | 'y';\n\n  /** Custom mathematical easing function */\n  easing?: (t: number) => number;\n\n  /** Additional distance between nearest edge and element */\n  offset?: number;\n\n  /** Indicator if animation may be interrupted by user scrolling */\n  cancelable?: boolean;\n\n  /** Prevents content jumping in scrolling lists with multiple targets */\n  isList?: boolean;\n}\n\nexport interface UseScrollIntoViewReturnValue<\n  Target extends HTMLElement = any,\n  Parent extends HTMLElement | null = null,\n> {\n  scrollableRef: React.RefObject<Parent | null>;\n  targetRef: React.RefObject<Target | null>;\n  scrollIntoView: (params?: UseScrollIntoViewAnimation) => void;\n  cancel: () => void;\n}\n\nexport function useScrollIntoView<\n  Target extends HTMLElement = any,\n  Parent extends HTMLElement | null = null,\n>({\n  duration = 1250,\n  axis = 'y',\n  onScrollFinish,\n  easing = easeInOutQuad,\n  offset = 0,\n  cancelable = true,\n  isList = false,\n}: UseScrollIntoViewOptions = {}): UseScrollIntoViewReturnValue<Target, Parent> {\n  const frameID = useRef(0);\n  const startTime = useRef(0);\n  const shouldStop = useRef(false);\n\n  const scrollableRef = useRef<Parent | null>(null);\n  const targetRef = useRef<Target | null>(null);\n\n  const reducedMotion = useReducedMotion();\n\n  const cancel = (): void => {\n    if (frameID.current) {\n      cancelAnimationFrame(frameID.current);\n    }\n  };\n\n  const scrollIntoView = useCallback(\n    ({ alignment = 'start' }: UseScrollIntoViewAnimation = {}) => {\n      shouldStop.current = false;\n\n      if (frameID.current) {\n        cancel();\n      }\n\n      const start = getScrollStart({ parent: scrollableRef.current, axis }) ?? 0;\n\n      const change =\n        getRelativePosition({\n          parent: scrollableRef.current,\n          target: targetRef.current,\n          axis,\n          alignment,\n          offset,\n          isList,\n        }) - (scrollableRef.current ? 0 : start);\n\n      function animateScroll() {\n        if (startTime.current === 0) {\n          startTime.current = performance.now();\n        }\n\n        const now = performance.now();\n        const elapsed = now - startTime.current;\n\n        // Easing timing progress\n        const t = reducedMotion || duration === 0 ? 1 : elapsed / duration;\n\n        const distance = start + change * easing(t);\n\n        setScrollParam({\n          parent: scrollableRef.current,\n          axis,\n          distance,\n        });\n\n        if (!shouldStop.current && t < 1) {\n          frameID.current = requestAnimationFrame(animateScroll);\n        } else {\n          typeof onScrollFinish === 'function' && onScrollFinish();\n          startTime.current = 0;\n          frameID.current = 0;\n          cancel();\n        }\n      }\n      animateScroll();\n    },\n    [axis, duration, easing, isList, offset, onScrollFinish, reducedMotion]\n  );\n\n  const handleStop = () => {\n    if (cancelable) {\n      shouldStop.current = true;\n    }\n  };\n\n  /**\n   * Detection of one of these events stops scroll animation\n   * wheel - mouse wheel / touch pad\n   * touchmove - any touchable device\n   */\n\n  useWindowEvent('wheel', handleStop, {\n    passive: true,\n  });\n\n  useWindowEvent('touchmove', handleStop, {\n    passive: true,\n  });\n\n  // Cleanup requestAnimationFrame\n  useEffect(() => cancel, []);\n\n  return {\n    scrollableRef,\n    targetRef,\n    scrollIntoView,\n    cancel,\n  };\n}\n\n// ---------------------------------------------------\n// Helpers\n// ---------------------------------------------------\n\nfunction easeInOutQuad(t: number) {\n  return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;\n}\n\nfunction getRelativePosition({ axis, target, parent, alignment, offset, isList }: any): number {\n  if (!target || (!parent && typeof document === 'undefined')) {\n    return 0;\n  }\n  const isCustomParent = !!parent;\n  const parentElement = parent || document.body;\n  const parentPosition = parentElement.getBoundingClientRect();\n  const targetPosition = target.getBoundingClientRect();\n\n  const getDiff = (property: 'top' | 'left'): number =>\n    targetPosition[property] - parentPosition[property];\n\n  if (axis === 'y') {\n    const diff = getDiff('top');\n\n    if (diff === 0) {\n      return 0;\n    }\n\n    if (alignment === 'start') {\n      const distance = diff - offset;\n      const shouldScroll = distance <= targetPosition.height * (isList ? 0 : 1) || !isList;\n\n      return shouldScroll ? distance : 0;\n    }\n\n    const parentHeight = isCustomParent ? parentPosition.height : window.innerHeight;\n\n    if (alignment === 'end') {\n      const distance = diff + offset - parentHeight + targetPosition.height;\n      const shouldScroll = distance >= -targetPosition.height * (isList ? 0 : 1) || !isList;\n\n      return shouldScroll ? distance : 0;\n    }\n\n    if (alignment === 'center') {\n      return diff - parentHeight / 2 + targetPosition.height / 2;\n    }\n\n    return 0;\n  }\n\n  if (axis === 'x') {\n    const diff = getDiff('left');\n\n    if (diff === 0) {\n      return 0;\n    }\n\n    if (alignment === 'start') {\n      const distance = diff - offset;\n      const shouldScroll = distance <= targetPosition.width || !isList;\n\n      return shouldScroll ? distance : 0;\n    }\n\n    const parentWidth = isCustomParent ? parentPosition.width : window.innerWidth;\n\n    if (alignment === 'end') {\n      const distance = diff + offset - parentWidth + targetPosition.width;\n      const shouldScroll = distance >= -targetPosition.width || !isList;\n\n      return shouldScroll ? distance : 0;\n    }\n\n    if (alignment === 'center') {\n      return diff - parentWidth / 2 + targetPosition.width / 2;\n    }\n\n    return 0;\n  }\n\n  return 0;\n}\n\nfunction getScrollStart({ axis, parent }: any) {\n  if (!parent && typeof document === 'undefined') {\n    return 0;\n  }\n\n  const method = axis === 'y' ? 'scrollTop' : 'scrollLeft';\n\n  if (parent) {\n    return parent[method];\n  }\n\n  const { body, documentElement } = document;\n\n  // While one of it has a value the second is equal 0\n  return body[method] + documentElement[method];\n}\n\nfunction setScrollParam({ axis, parent, distance }: any) {\n  if (!parent && typeof document === 'undefined') {\n    return;\n  }\n\n  const method = axis === 'y' ? 'scrollTop' : 'scrollLeft';\n\n  if (parent) {\n    parent[method] = distance;\n  } else {\n    const { body, documentElement } = document;\n    body[method] = distance;\n    documentElement[method] = distance;\n  }\n}\n\nexport namespace useScrollIntoView {\n  export type Options = UseScrollIntoViewOptions;\n  export type ReturnValue<\n    Target extends HTMLElement,\n    Parent extends HTMLElement | null,\n  > = UseScrollIntoViewReturnValue<Target, Parent>;\n}\n"],"mappings":"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"}