{"version":3,"file":"index.mjs","names":[],"sources":["../../../src/hooks/useIntersectionObserver/index.ts"],"sourcesContent":["import { useCallback, useRef, useState } from 'react';\nimport { usePreservedCallback } from '../usePreservedCallback';\nimport { noop } from '@modern-kit/utils';\n\nexport interface UseIntersectionObserverProps extends IntersectionObserverInit {\n  onIntersectStart?: (entry: IntersectionObserverEntry) => void;\n  onIntersectEnd?: (entry: IntersectionObserverEntry) => void;\n  calledOnce?: boolean;\n  enabled?: boolean;\n}\n\ninterface UseIntersectionObserverReturnType<T extends HTMLElement> {\n  ref: React.RefCallback<T>;\n  isIntersecting: boolean;\n  hasIntersected: boolean;\n}\n\n/**\n * @description `useIntersectionObserver` 훅은 주어진 타겟 요소가 뷰포트(viewport) 내에 들어오거나 나가는지 관찰하기 위한 Intersection Observer를 설정합니다.\n *\n * 이 훅은 요소가 교차할 때 호출할 콜백을 제공하며, 한 번만 호출되거나 반복적으로 호출되도록 설정할 수 있습니다.\n *\n * @template T - 관찰할 HTML 요소 타입, 기본적으로 `HTMLElement`를 상속합니다.\n *\n * @param {(entry: IntersectionObserverEntry) => void} params.onIntersectStart - 타겟 요소가 viewport 내에 들어갈 때 호출되는 콜백 함수입니다.\n * @param {(entry: IntersectionObserverEntry) => void} params.onIntersectEnd - 타겟 요소가 viewport에서 나갈 때 호출되는 콜백 함수입니다.\n * @param {boolean} [params.enabled=true] - Observer를 활성화할지 여부를 나타냅니다. `false`일 경우 Observer가 작동하지 않습니다.\n * @param {boolean} [params.calledOnce=false] - 요소가 교차할 때 콜백을 `한 번`만 호출할지 여부를 나타냅니다.\n * @param {Element} [params.root=null] - 교차할 때 기준이 되는 root 요소입니다. 기본값은 `null`이며 이는 viewport를 의미합니다.\n * @param {number | number[]} [params.threshold=0] - Observer가 콜백을 호출하는 임계값을 나타냅니다.\n * @param {string} [params.rootMargin='0px 0px 0px 0px'] - 루트 요소에 대한 마진을 지정합니다. 이는 뷰포트 또는 루트 요소의 경계를 확장하거나 축소하는데 사용됩니다.\n *\n * @returns {UseIntersectionObserverReturnType<T>} ref를 포함한 isIntersecting과 hasIntersected 값을 반환합니다.\n * - `ref`: 관찰할 타겟 요소에 전달할 ref\n * - `isIntersecting`: 타겟 요소가 교차하는지 여부를 나타내는 boolean 값\n * - `hasIntersected`: 타겟 요소가 최초로 교차했는지 여부를 나타내는 boolean 값\n *\n * @example\n * ```tsx\n * const { ref: targetRef, isIntersecting, hasIntersected } = useIntersectionObserver<HTMLDivElement>({\n *   onIntersectStart: () => console.log('onIntersectStart'),\n *   onIntersectEnd: () => console.log('onIntersectEnd'),\n *   calledOnce: true,\n *   enabled: true,\n *   ...intersectionObserverOptions, // root, threshold, rootMargin\n * });\n *\n * <div ref={targetRef} />\n * ```\n */\nexport function useIntersectionObserver<T extends HTMLElement>({\n  onIntersectStart = noop,\n  onIntersectEnd = noop,\n  enabled = true,\n  calledOnce = false,\n  root = null,\n  threshold = 0,\n  rootMargin = '0px 0px 0px 0px',\n}: UseIntersectionObserverProps = {}): UseIntersectionObserverReturnType<T> {\n  const [isIntersecting, setIsIntersecting] = useState(false);\n  const [hasIntersected, setHasIntersected] = useState(false);\n\n  const observerRef = useRef<IntersectionObserver | null>(null);\n  const calledCount = useRef(0);\n\n  const intersectionObserverCallback = usePreservedCallback(\n    ([entry]: IntersectionObserverEntry[], observer: IntersectionObserver) => {\n      if (!entry) return;\n\n      const targetElement = entry.target as T;\n      setIsIntersecting(entry.isIntersecting);\n\n      if (entry.isIntersecting) {\n        calledCount.current += 1;\n\n        setHasIntersected(true);\n        onIntersectStart(entry);\n      } else if (isIntersecting) {\n        // 최초 mount 시에 호출을 방지하고, 타겟 요소가 viewport에서 나갈 때만 호출\n        calledCount.current += 1;\n\n        onIntersectEnd(entry);\n      }\n\n      if (calledOnce && calledCount.current > 1) {\n        observer.unobserve(targetElement);\n      }\n    }\n  );\n\n  const targetRef = useCallback(\n    (node: T) => {\n      // 기존 observer가 활성화된 상태에서 새로운 요소를 관찰하기 전에 기존 observer 관찰 중지하며, 메모리 누수를 방지\n      if (observerRef.current) {\n        observerRef.current.disconnect();\n        observerRef.current = null;\n      }\n\n      if (node == null || !enabled) return;\n\n      observerRef.current = new IntersectionObserver(\n        intersectionObserverCallback,\n        {\n          threshold,\n          root,\n          rootMargin,\n        }\n      );\n      observerRef.current.observe(node);\n    },\n    [enabled, threshold, root, rootMargin, intersectionObserverCallback]\n  );\n\n  return { ref: targetRef, isIntersecting, hasIntersected };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkDA,SAAgB,wBAA+C,EAC7D,mBAAmB,MACnB,iBAAiB,MACjB,UAAU,MACV,aAAa,OACb,OAAO,MACP,YAAY,GACZ,aAAa,sBACmB,EAAE,EAAwC;CAC1E,MAAM,CAAC,gBAAgB,qBAAqB,SAAS,MAAM;CAC3D,MAAM,CAAC,gBAAgB,qBAAqB,SAAS,MAAM;CAE3D,MAAM,cAAc,OAAoC,KAAK;CAC7D,MAAM,cAAc,OAAO,EAAE;CAE7B,MAAM,+BAA+B,sBAClC,CAAC,QAAqC,aAAmC;EACxE,IAAI,CAAC,OAAO;EAEZ,MAAM,gBAAgB,MAAM;EAC5B,kBAAkB,MAAM,eAAe;EAEvC,IAAI,MAAM,gBAAgB;GACxB,YAAY,WAAW;GAEvB,kBAAkB,KAAK;GACvB,iBAAiB,MAAM;SAClB,IAAI,gBAAgB;GAEzB,YAAY,WAAW;GAEvB,eAAe,MAAM;;EAGvB,IAAI,cAAc,YAAY,UAAU,GACtC,SAAS,UAAU,cAAc;GAGtC;CAyBD,OAAO;EAAE,KAvBS,aACf,SAAY;GAEX,IAAI,YAAY,SAAS;IACvB,YAAY,QAAQ,YAAY;IAChC,YAAY,UAAU;;GAGxB,IAAI,QAAQ,QAAQ,CAAC,SAAS;GAE9B,YAAY,UAAU,IAAI,qBACxB,8BACA;IACE;IACA;IACA;IACD,CACF;GACD,YAAY,QAAQ,QAAQ,KAAK;KAEnC;GAAC;GAAS;GAAW;GAAM;GAAY;GAA6B,CAG/C;EAAE;EAAgB;EAAgB"}