Skip to content

Commit

Permalink
✨ feat: 컴포넌트 바깥 클릭했을 때 callback 실행하는 커스텀훅 구현
Browse files Browse the repository at this point in the history
  • Loading branch information
baegyeong committed Nov 19, 2024
1 parent 1a5b105 commit 9c22b07
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 24 deletions.
55 changes: 31 additions & 24 deletions packages/frontend/src/components/layouts/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,20 @@ import logoTitle from '/logoTitle.png';
import { MenuList } from './MenuList';
import { Search } from './search';
import { BOTTOM_MENU_ITEMS, TOP_MENU_ITEMS } from '@/constants/menuItems';
import { useOutsideClick } from '@/hooks/useOutsideClick';
import { type MenuSection } from '@/types/menu';
import { cn } from '@/utils/cn';

export const Sidebar = () => {
const [isHovered, setIsHovered] = useState(false);
const [showSearch, setShowSearch] = useState(false);

const ref = useOutsideClick(() => {
if (showSearch) {
setShowSearch(false);
}
});

const handleMenuItemClick = (item: MenuSection) => {
if (item.text === '검색') {
setShowSearch((prev) => !prev);
Expand All @@ -19,26 +26,26 @@ export const Sidebar = () => {

return (
<div ref={ref}>
<nav
className={cn(
'fixed left-0 top-0 h-full cursor-pointer bg-white px-1 py-4 shadow-md',
'transition-all duration-300 ease-in-out',
isHovered ? 'w-60' : 'w-24',
)}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
>
<section className="flex flex-col justify-center gap-8">
<header className="flex items-center gap-4">
<img src={logoCharacter} alt="로고 캐릭터" className="w-20" />
<img
src={logoTitle}
alt="로고 제목"
className={cn('w-24 pt-5', isHovered ? 'display' : 'hidden')}
/>
</header>
<div
className={cn(
<nav
className={cn(
'fixed left-0 top-0 h-full cursor-pointer bg-white px-1 py-4 shadow-md',
'transition-all duration-300 ease-in-out',
isHovered ? 'w-60' : 'w-24',
)}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
>
<section className="flex flex-col justify-center gap-8">
<header className="flex items-center gap-4">
<img src={logoCharacter} alt="로고 캐릭터" className="w-20" />
<img
src={logoTitle}
alt="로고 제목"
className={cn('w-24 pt-5', isHovered ? 'display' : 'hidden')}
/>
</header>
<div
className={cn(
'flex h-[calc(100vh-11rem)]',
isHovered ? 'gap-4' : '',
)}
Expand All @@ -51,9 +58,9 @@ export const Sidebar = () => {
/>
<MenuList items={BOTTOM_MENU_ITEMS} isHovered={isHovered} />
</div>
</div>
</section>
</nav>
</div>
</section>
</nav>
<div
className={cn(
'fixed top-0 transition-all duration-300 ease-in-out',
Expand All @@ -62,6 +69,6 @@ export const Sidebar = () => {
>
{showSearch && <Search className="h-screen" />}
</div>
</div>
</div>
);
};
23 changes: 23 additions & 0 deletions packages/frontend/src/hooks/useOutsideClick.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { useEffect, useRef } from 'react';

export const useOutsideClick = (callback: () => void) => {
const ref = useRef<HTMLDivElement>(null);

useEffect(() => {
const handleClickOutside = (event: MouseEvent | TouchEvent) => {
if (!ref.current?.contains(event.target as Node)) {
callback();
}
};

document.addEventListener('mouseup', handleClickOutside);
document.addEventListener('touchend', handleClickOutside);

return () => {
document.removeEventListener('mouseup', handleClickOutside);
document.removeEventListener('touchend', handleClickOutside);
};
}, [callback]);

return ref;
};

0 comments on commit 9c22b07

Please sign in to comment.