import { Pin, ChevronRight, ChevronsDownUp, ChevronsUpDown, Circle } from 'lucide-react'; import type { MenuItem } from '@/stores/menuStore'; import { useEffect, useRef, useCallback } from 'react'; import { useFavoritesStore, MAX_FAVORITES } from '@/stores/favoritesStore'; import { getIconName } from '@/lib/utils/menuTransform'; import type { FavoriteItem } from '@/stores/favoritesStore'; import { toast } from 'sonner'; interface SidebarProps { menuItems: MenuItem[]; activeMenu: string; expandedMenus: string[]; sidebarCollapsed: boolean; isMobile: boolean; onMenuClick: (menuId: string, path: string) => void; onToggleSubmenu: (menuId: string) => void; onToggleAll?: () => void; onCloseMobileSidebar?: () => void; onExpandSidebar?: () => void; } // 재귀적 메뉴 아이템 컴포넌트 Props interface MenuItemComponentProps { item: MenuItem; depth: number; // 0: 1depth, 1: 2depth, 2: 3depth activeMenu: string; expandedMenus: string[]; sidebarCollapsed: boolean; isMobile: boolean; activeMenuRef: React.RefObject; onMenuClick: (menuId: string, path: string) => void; onToggleSubmenu: (menuId: string) => void; onCloseMobileSidebar?: () => void; onExpandSidebar?: () => void; } // 재귀적 메뉴 아이템 컴포넌트 (3depth 이상 지원) function MenuItemComponent({ item, depth, activeMenu, expandedMenus, sidebarCollapsed, isMobile, activeMenuRef, onMenuClick, onToggleSubmenu, onCloseMobileSidebar, onExpandSidebar, }: MenuItemComponentProps) { const IconComponent = item.icon; const hasChildren = item.children && item.children.length > 0; const isExpanded = expandedMenus.includes(item.id); const isActive = activeMenu === item.id; const isLeaf = !hasChildren; // 즐겨찾기 상태 const { toggleFavorite, isFavorite } = useFavoritesStore(); const isFav = isLeaf ? isFavorite(item.id) : false; const handleStarClick = useCallback((e: React.MouseEvent) => { e.stopPropagation(); e.preventDefault(); const favItem: FavoriteItem = { id: item.id, label: item.label, iconName: getIconName(item.icon), path: item.path, addedAt: Date.now(), }; const result = toggleFavorite(favItem); if (result === 'max_reached') { toast.warning(`즐겨찾기는 최대 ${MAX_FAVORITES}개까지 등록할 수 있습니다.`); } }, [item, toggleFavorite]); const handleClick = () => { if (hasChildren) { // 접힌 상태에서 카테고리 클릭 시 사이드바 자동 펼침 if (sidebarCollapsed && onExpandSidebar) { onExpandSidebar(); } onToggleSubmenu(item.id); } else { onMenuClick(item.id, item.path); if (isMobile && onCloseMobileSidebar) { onCloseMobileSidebar(); } } }; // depth별 스타일 설정 // 1depth (depth=0): 아이콘 + 굵은 텍스트 + 배경색 // 2depth (depth=1): 작은 아이콘 + 일반 텍스트 + 왼쪽 보더 // 3depth (depth=2+): 점(dot) 아이콘 + 작은 텍스트 + 더 깊은 들여쓰기 const is1Depth = depth === 0; const is2Depth = depth === 1; const is3DepthOrMore = depth >= 2; // 1depth 메뉴 렌더링 if (is1Depth) { return (
{/* 메인 메뉴 버튼 + 별표 래퍼 */}
{isLeaf && !sidebarCollapsed && ( )}
{/* 자식 메뉴 (재귀) */} {hasChildren && isExpanded && !sidebarCollapsed && (
{item.children?.map((child) => ( ))}
)}
); } // 2depth 메뉴 렌더링 if (is2Depth) { return (
{isLeaf && ( )}
{/* 자식 메뉴 (3depth) */} {hasChildren && isExpanded && (
{item.children?.map((child) => ( ))}
)}
); } // 3depth 이상 메뉴 렌더링 (점 아이콘 + 작은 텍스트) if (is3DepthOrMore) { return (
{isLeaf && ( )}
{/* 자식 메뉴 (4depth 이상 - 재귀) */} {hasChildren && isExpanded && (
{item.children?.map((child) => ( ))}
)}
); } return null; } export default function Sidebar({ menuItems, activeMenu, expandedMenus, sidebarCollapsed, isMobile, onMenuClick, onToggleSubmenu, onToggleAll, onCloseMobileSidebar, onExpandSidebar, }: SidebarProps) { // 활성 메뉴 자동 스크롤을 위한 ref const activeMenuRef = useRef(null); const menuContainerRef = useRef(null); // 활성 메뉴가 변경될 때 자동 스크롤 useEffect(() => { if (activeMenuRef.current && menuContainerRef.current) { // 부드러운 스크롤로 활성 메뉴를 화면에 표시 activeMenuRef.current.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest', }); } }, [activeMenu]); return (
{/* 메뉴 */}
{/* 전체 열기/닫기 토글 버튼 - 사이드바 펼침 상태에서만 표시 */} {!sidebarCollapsed && onToggleAll && ( )}
{menuItems.map((item) => ( ))}
); }