UNPKG

@modern-kit/react

Version:
1 lines 9.93 kB
{"version":3,"file":"index.mjs","sources":["../../../src/hooks/useScrollEvent/useScrollEvent.utils.ts","../../../src/hooks/useScrollEvent/index.ts"],"sourcesContent":["/**\n * @description 스크롤 진행 상태를 반환하는 함수입니다.\n */\nexport const getScrollProgress = (\n current: number,\n total: number,\n clientSize: number\n): number => {\n const maxScroll = total - clientSize;\n\n if (maxScroll <= 0) return 0;\n\n return Math.min(Math.round((current / maxScroll) * 100), 100);\n};\n\n/**\n * @description 스크롤 방향을 반환하는 함수입니다.\n */\nexport const getScrollDirection = (\n currentScrollX: number,\n currentScrollY: number,\n prevScrollY: number,\n prevScrollX: number\n) => {\n return {\n y:\n currentScrollY === prevScrollY\n ? \"none\"\n : currentScrollY > prevScrollY\n ? \"down\"\n : \"up\",\n x:\n currentScrollX === prevScrollX\n ? \"none\"\n : currentScrollX > prevScrollX\n ? \"right\"\n : \"left\",\n } as const;\n};\n","import React, { useCallback, useEffect, useRef, useState } from 'react';\nimport { throttle } from '@modern-kit/utils';\nimport { getScrollDirection, getScrollProgress } from './useScrollEvent.utils';\n\ninterface UseScrollProps {\n throttleDelay?: number;\n throttleLeading?: boolean;\n throttleTrailing?: boolean;\n enabled?: boolean;\n}\n\ninterface UpdateScrollPositionProps {\n scrollX: number;\n scrollY: number;\n scrollWidth: number;\n scrollHeight: number;\n clientWidth: number;\n clientHeight: number;\n}\n\ninterface ScrollState {\n scrollX: number;\n scrollY: number;\n scrollWidth: number;\n scrollHeight: number;\n direction: {\n y: 'up' | 'down' | 'none';\n x: 'left' | 'right' | 'none';\n };\n progress: {\n y: number;\n x: number;\n };\n}\n\n/**\n * @description 특정 요소나 window의 스크롤 위치를 추적하는 React 커스텀 훅입니다.\n * 스크롤 방향, 진행도, 위치 등 스크롤 관련 정보를 제공합니다.\n *\n * @template T - HTMLElement를 상속하는 제네릭 타입\n * @param {UseScrollProps} props - 훅의 설정 객체\n * @param {boolean} [props.enabled=true] - 스크롤 이벤트 활성화 여부\n * @param {number} [props.throttleDelay=0] - 스크롤 이벤트 쓰로틀링 딜레이(밀리초)\n * @param {boolean} [props.throttleLeading=true] - 스크롤 이벤트 쓰로틀링 시작 시점 이벤트 무시 여부\n * @param {boolean} [props.throttleTrailing=true] - 스크롤 이벤트 쓰로틀링 마지막 이벤트 무시 여부\n * @returns {{\n * ref: React.RefObject<T>;\n * scrollState: ScrollState;\n * }} 스크롤 관련 데이터와 ref\n * - `ref`: 스크롤을 추적할 요소에 연결할 ref 객체\n * - `scrollState.scrollX`: 현재 스크롤 위치의 X좌표\n * - `scrollState.scrollY`: 현재 스크롤 위치의 Y좌표\n * - `scrollState.scrollWidth`: 스크롤 가능한 최대 너비\n * - `scrollState.scrollHeight`: 스크롤 가능한 최대 높이\n * - `scrollState.direction`: 현재 스크롤 방향\n * - `scrollState.progress`: 현재 스크롤 진행도\n *\n * @example\n * ```tsx\n * // ref를 전달하지 않으면 기본적으로 window의 스크롤 위치를 추적\n * const { scrollState } = useScrollEvent();\n * ```\n *\n * @example\n * ```tsx\n * // ref를 전달하면 해당 요소를 기준으로 스크롤 위치를 추적\n * const { ref, scrollState } = useScrollEvent<HTMLDivElement>();\n *\n * return <div ref={ref}>{...}</div>\n * ```\n *\n * @example\n * ```tsx\n * // 스크롤 이벤트 쓰로틀링 딜레이를 설정\n * const { scrollState } = useScrollEvent({ throttleDelay: 300, enabled: true });\n * ```\n */\nexport function useScrollEvent<T extends HTMLElement>({\n throttleDelay = 0,\n throttleLeading = true,\n throttleTrailing = true,\n enabled = true,\n}: UseScrollProps = {}): {\n ref: React.RefObject<T>;\n scrollState: ScrollState;\n} {\n const ref = useRef<T>(null);\n const prevScrollY = useRef(0);\n const prevScrollX = useRef(0);\n\n const [scrollState, setScrollState] = useState<ScrollState>({\n scrollX: 0,\n scrollY: 0,\n scrollWidth: 0,\n scrollHeight: 0,\n direction: {\n y: 'none',\n x: 'none',\n },\n progress: {\n y: 0,\n x: 0,\n },\n });\n\n const updateScrollPosition = useCallback(\n ({\n scrollX,\n scrollY,\n scrollWidth,\n scrollHeight,\n clientWidth,\n clientHeight,\n }: UpdateScrollPositionProps) => {\n setScrollState({\n scrollX,\n scrollY,\n scrollWidth,\n scrollHeight,\n direction: getScrollDirection(\n scrollX,\n scrollY,\n prevScrollY.current,\n prevScrollX.current\n ),\n progress: {\n y: getScrollProgress(scrollY, scrollHeight, clientHeight),\n x: getScrollProgress(scrollX, scrollWidth, clientWidth),\n },\n });\n\n // 이전 스크롤 위치 업데이트\n prevScrollY.current = scrollY;\n prevScrollX.current = scrollX;\n },\n []\n );\n\n const handleScrollByRef = useCallback(() => {\n if (!ref.current) return;\n const targetElement = ref.current;\n\n updateScrollPosition({\n scrollX: targetElement.scrollLeft,\n scrollY: targetElement.scrollTop,\n scrollWidth: targetElement.scrollWidth,\n scrollHeight: targetElement.scrollHeight,\n clientWidth: targetElement.clientWidth,\n clientHeight: targetElement.clientHeight,\n });\n }, [updateScrollPosition]);\n\n const handleScrollByWindow = useCallback(() => {\n updateScrollPosition({\n scrollX: window.scrollX,\n scrollY: window.scrollY,\n scrollWidth: document.documentElement.scrollWidth,\n scrollHeight: document.documentElement.scrollHeight,\n clientWidth: window.innerWidth,\n clientHeight: window.innerHeight,\n });\n }, [updateScrollPosition]);\n\n useEffect(() => {\n if (!enabled) return;\n\n const targetElement = ref.current ?? window;\n\n const scrollHandler = ref.current\n ? handleScrollByRef\n : handleScrollByWindow;\n\n const throttledScrollHandler = throttle(scrollHandler, throttleDelay, {\n leading: throttleLeading,\n trailing: throttleTrailing,\n });\n\n scrollHandler(); // 초기 스크롤 위치 업데이트\n targetElement.addEventListener('scroll', throttledScrollHandler);\n\n return () => {\n targetElement.removeEventListener('scroll', throttledScrollHandler);\n throttledScrollHandler.cancel();\n };\n }, [\n handleScrollByRef,\n handleScrollByWindow,\n enabled,\n throttleDelay,\n throttleLeading,\n throttleTrailing,\n ]);\n\n return {\n ref: ref as React.RefObject<T>,\n scrollState,\n };\n}\n"],"names":[],"mappings":"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"}