diff --git a/src/hooks/index.ts b/src/hooks/index.ts index 3d725904..58774104 100644 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -16,7 +16,7 @@ export { useLocalStorage } from './useLocalStorage'; export { useLongPress } from './useLongPress'; export { useMousePosition } from './useMousePosition'; export { useOnlineStatus } from './useOnlineStatus'; -export { useOutsideInteraction } from './useOutsideInteraction'; +export { useOutsideClick } from './useOutsideClick'; export { usePrefersColorScheme } from './usePrefersColorScheme'; export { useScrollLock } from './useScrollLock'; export { useScrollY } from './useScrollY'; @@ -27,3 +27,4 @@ export { useTranslation } from './useTranslation'; export { useUnmountEffect } from './useUnmountEffect'; export { useWindowSize } from './useWindowSize'; export { useWorker } from './useWorker'; +export { useKeyDown } from './useKeyDown'; diff --git a/src/hooks/useKeyDown/index.ts b/src/hooks/useKeyDown/index.ts new file mode 100644 index 00000000..5da898e7 --- /dev/null +++ b/src/hooks/useKeyDown/index.ts @@ -0,0 +1 @@ +export { default as useKeyDown } from './useKeyDown'; diff --git a/src/hooks/useKeyDown/type.ts b/src/hooks/useKeyDown/type.ts new file mode 100644 index 00000000..b59ddfd8 --- /dev/null +++ b/src/hooks/useKeyDown/type.ts @@ -0,0 +1,4 @@ +export interface UseKeyDownProps { + key: string; + onKeyPress: () => void; +} diff --git a/src/hooks/useKeyDown/useKeyDown.tsx b/src/hooks/useKeyDown/useKeyDown.tsx new file mode 100644 index 00000000..bf7166d4 --- /dev/null +++ b/src/hooks/useKeyDown/useKeyDown.tsx @@ -0,0 +1,27 @@ +import { useEffect } from 'react'; +import { UseKeyDownProps } from '@/hooks/useKeyDown/type'; + +/** + * 주어진 키 입력에 대한 이벤트를 처리하는 훅 + * + * @param {string} key - 감지할 키 값 (예: 'Escape', 'Enter' 등) + * @param {() => void} onKeyPress - 지정된 키가 눌렸을 때 실행할 콜백 함수 + * + * @returns {void} + */ +const useKeyDown = ({ key, onKeyPress }: UseKeyDownProps): void => { + useEffect(() => { + const handleKeyDown = (event: KeyboardEvent) => { + if (event.key === key && !event.repeat) { + onKeyPress(); + } + }; + + document.addEventListener('keydown', handleKeyDown); + return () => { + document.removeEventListener('keydown', handleKeyDown); + }; + }, [key, onKeyPress]); +}; + +export default useKeyDown; diff --git a/src/hooks/useOutsideClick/index.ts b/src/hooks/useOutsideClick/index.ts new file mode 100644 index 00000000..7eea4faa --- /dev/null +++ b/src/hooks/useOutsideClick/index.ts @@ -0,0 +1 @@ +export { default as useOutsideClick } from './useOutsideClick'; diff --git a/src/hooks/useOutsideClick/type.ts b/src/hooks/useOutsideClick/type.ts new file mode 100644 index 00000000..c679277a --- /dev/null +++ b/src/hooks/useOutsideClick/type.ts @@ -0,0 +1,6 @@ +export type EventType = 'mousedown' | 'mouseup' | 'touchstart' | 'touchend'; + +export interface UseOutsideClickProps { + onClickOutside: () => void; + events?: EventType[]; +} diff --git a/src/hooks/useOutsideClick/useOutsideClick.ts b/src/hooks/useOutsideClick/useOutsideClick.ts new file mode 100644 index 00000000..8f856c76 --- /dev/null +++ b/src/hooks/useOutsideClick/useOutsideClick.ts @@ -0,0 +1,50 @@ +import { useCallback, useEffect, useRef, RefObject } from 'react'; +import { EventType, UseOutsideClickProps } from './type'; + +const defaultEvents: EventType[] = ['mousedown', 'touchstart']; + +/** + * 지정된 element 외부의 click event를 감지하는 hook + * onClickOutside 함수를 통해 마우스 클릭 이벤트를 처리할 수 있습니다. + * + * @param {Object} props - hook props + * @param {() => void} props.onClickOutside - 외부 interaction 발생 시 실행될 콜백 함수 + * @param {EventType[]} [props.events] - 감지할 이벤트 (default :['mousedown', 'touchstart']) + * + * @returns {React.RefObject} element에 연결할 ref 객체 + */ + +const useOutsideClick = ({ + onClickOutside, + events = defaultEvents, +}: UseOutsideClickProps): RefObject => { + const ref = useRef(null); + + const handleClick = useCallback( + (event: Event) => { + const target = event.target as Node | null; + if (!target) return; + + if (ref.current && !ref.current.contains(target)) { + onClickOutside(); + } + }, + [onClickOutside] + ); + + useEffect(() => { + if (events.length === 0) return; + + events.forEach((event) => document.addEventListener(event, handleClick)); + + return () => { + events.forEach((event) => + document.removeEventListener(event, handleClick) + ); + }; + }, [handleClick, events]); + + return ref; +}; + +export default useOutsideClick; diff --git a/src/hooks/useOutsideInteraction/index.ts b/src/hooks/useOutsideInteraction/index.ts deleted file mode 100644 index 49e2b68c..00000000 --- a/src/hooks/useOutsideInteraction/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as useOutsideInteraction } from './useOutsideInteraction'; diff --git a/src/hooks/useOutsideInteraction/type.ts b/src/hooks/useOutsideInteraction/type.ts deleted file mode 100644 index 0e921556..00000000 --- a/src/hooks/useOutsideInteraction/type.ts +++ /dev/null @@ -1,11 +0,0 @@ -export type EventType = - | 'mousedown' - | 'mouseup' - | 'touchstart' - | 'touchend' - | 'keydown'; - -export interface UseOutsideInteractionProps { - handleOutsideInteraction: () => void; - events?: EventType[]; -} diff --git a/src/hooks/useOutsideInteraction/useOutsideInteraction.ts b/src/hooks/useOutsideInteraction/useOutsideInteraction.ts deleted file mode 100644 index 04f2d09e..00000000 --- a/src/hooks/useOutsideInteraction/useOutsideInteraction.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { useCallback, useEffect, useRef, RefObject } from 'react'; -import { EventType, UseOutsideInteractionProps } from './type'; - -const defaultEvents: EventType[] = ['mousedown', 'touchstart', 'keydown']; - -/** - * 지정된 element 외부의 interaction을 감지하는 hook - * 마우스 클릭, 터치, 키보드 이벤트(Escape 키)를 처리할 수 있습니다. - * - * @param {Object} props - hook props - * @param {() => void} props.handleOutsideInteraction - 외부 interaction 발생 시 실행될 콜백 함수 - * @param {EventType[]} [props.events] - 감지할 이벤트 (default :['mousedown', 'touchstart', 'keydown']) - * - * @returns {React.RefObject} element에 연결할 ref 객체 - */ - -const useOutsideInteraction = ({ - handleOutsideInteraction, - events = defaultEvents, -}: UseOutsideInteractionProps): RefObject => { - const ref = useRef(null); - - const handleEvent = useCallback( - (event: Event) => { - if (event instanceof KeyboardEvent) { - if (event.key === 'Escape') { - handleOutsideInteraction(); - return; - } - } - - const target = event.target as Node | null; - if (!target) return; - - if (ref.current && !ref.current.contains(target)) { - handleOutsideInteraction(); - } - }, - [handleOutsideInteraction] - ); - - useEffect(() => { - if (events.length === 0) return; - - events.forEach((event) => document.addEventListener(event, handleEvent)); - - return () => { - events.forEach((event) => - document.removeEventListener(event, handleEvent) - ); - }; - }, [handleEvent, events]); - - return ref; -}; - -export default useOutsideInteraction;