import { PATHS } from '@/shared/constants/paths'; import { motion } from 'framer-motion'; import { IMAGE_ROOT } from '@/shared/constants/common'; import { useNavigate } from '@/shared/lib/hooks/use-navigate'; import { MenuCategory } from '@/entities/menu/ui/menu-category'; import { FavoriteWrapper } from '@/entities/home/ui/favorite-wrapper'; import { useStore } from '@/shared/model/store'; import { FilterMotionDuration, FilterMotionStyle, FilterMotionVariants, MenuItems } from '@/entities/common/model/constant'; import { useEffect, useRef, useState } from 'react'; import { useLocation } from 'react-router'; import { setHomeReloadKey } from '@/pages/home/home-page'; import { useShortcutSaveMutation } from '@/entities/user/api/use-shortcut-save-mutation'; import { ShortcutSaveParams, ShortcutSaveResponse } from '@/entities/user/model/types'; // 상수 정의 const SCROLL_ANIMATION_DURATION = 800; const SCROLL_UP_OFFSET_PX = 150; const BUTTON_SCROLL_OFFSET = 30; // 타입 정의 interface ShortButton { menuId: number; menuName: string; index: number; } export interface MenuProps { menuOn: boolean; setMenuOn: (menuOn: boolean) => void; favoriteEdit?: boolean; } export const Menu = ({ menuOn, setMenuOn, favoriteEdit }: MenuProps) => { const userMids = useStore.getState().UserStore.userMids; const userInfo = useStore.getState().UserStore.userInfo; const location = useLocation(); const { navigate } = useNavigate(); const { mutateAsync: shortcutSave } = useShortcutSaveMutation(); const [shortBtns, setShortBtns] = useState([]); const [editMode, setEditMode] = useState(false); const [changeMenuId, setChangeMenuId] = useState(); const [shortBtnIdx, setShortBtnIdx] = useState(0); const buttonRefs = useRef>([]); const scrollRef = useRef(null); const shortBtnScrollRef = useRef(null); const isButtonScrolling = useRef(false); const scrollTimer = useRef(null); const lastScrollTop = useRef(0); // const [menuIds, setMenuIds] = useState>([]); const callShortcutSave = () => { if(userInfo.usrid){ let userFavorite = useStore.getState().UserStore.userFavorite; let menuIds = userFavorite.map((value, index) => { return value.menuId; }); let params: ShortcutSaveParams = { usrid: userInfo.usrid, isDefault: false, menuIds: menuIds }; shortcutSave(params).then((rs: ShortcutSaveResponse) => { }); } }; const onClickToNavigate = (path: string) => { onClickToMenuClose(); navigate(path); }; const scrollCategoryButtonToLeft = (index: number) => { const buttonElement = shortBtnScrollRef.current?.children[index] as HTMLElement; if (buttonElement && shortBtnScrollRef.current) { const scrollLeft = buttonElement.offsetLeft - BUTTON_SCROLL_OFFSET; shortBtnScrollRef.current.scrollTo({ top: 0, left: scrollLeft, behavior: 'smooth' }); } }; const onClickToMenuNavigate = (menuId: number, index: number) => { isButtonScrolling.current = true; setShortBtnIdx(index); scrollCategoryButtonToLeft(index); const categoryElement = buttonRefs.current[index]; if (categoryElement && scrollRef.current) { const scrollContainer = scrollRef.current; const containerStyle = window.getComputedStyle(scrollContainer); const paddingTop = parseFloat(containerStyle.paddingTop) || 0; const scrollPosition = categoryElement.offsetTop - paddingTop; lastScrollTop.current = scrollPosition; scrollContainer.scrollTo({ top: scrollPosition, left: 0, behavior: 'smooth' }); } if (scrollTimer.current) { clearTimeout(scrollTimer.current); } scrollTimer.current = setTimeout(() => { if (scrollRef.current) { const actualScrollTop = scrollRef.current.scrollTop; lastScrollTop.current = actualScrollTop; setShortBtnIdx(index); } isButtonScrolling.current = false; }, SCROLL_ANIMATION_DURATION); }; const onClickToMenuClose = () => { if(editMode){ setEditMode(false); callShortcutSave(); // 여기에 저장 로직? } else{ setMenuOn(false); if(location.pathname === PATHS.home){ setHomeReloadKey(); } } }; const getMenuCategory = () => { let rs = []; for(let i=0;i ); } } return rs; }; const shortBtnsSetting = () => { const shortList: ShortButton[] = MenuItems.map((item, index) => ({ menuId: item.menuId, menuName: item.menuName, index })); setShortBtns(shortList); }; const getCurrentCategoryIndex = (scrollTop: number): number => { if (buttonRefs.current.length === 0 || !scrollRef.current) return 0; const containerStyle = window.getComputedStyle(scrollRef.current); const paddingTop = parseFloat(containerStyle.paddingTop) || 0; const adjustedScrollTop = scrollTop + paddingTop; // 뷰포트의 상단 기준점 (약간의 오프셋 추가) const viewportTopWithOffset = adjustedScrollTop + 50; // 현재 뷰포트에 가장 많이 보이는 카테고리 찾기 for (let i = 0; i < buttonRefs.current.length; i++) { const element = buttonRefs.current[i]; if (!element) continue; const currentTop = element.offsetTop; // 마지막 카테고리인 경우 if (i === buttonRefs.current.length - 1) { if (viewportTopWithOffset >= currentTop) { return i; } } else { // 다음 카테고리가 있는 경우 const nextElement = buttonRefs.current[i + 1]; if (nextElement) { const nextTop = nextElement.offsetTop; // 현재 카테고리 영역 내에 뷰포트 상단이 있으면 선택 if (viewportTopWithOffset >= currentTop && viewportTopWithOffset < nextTop) { return i; } } } } return 0; }; const menuListScroll = () => { if (isButtonScrolling.current) return; const scrollTop = scrollRef.current?.scrollTop || 0; const currentIndex = getCurrentCategoryIndex(scrollTop); if (currentIndex !== shortBtnIdx) { setShortBtnIdx(currentIndex); scrollCategoryButtonToLeft(currentIndex); } lastScrollTop.current = scrollTop; }; useEffect(() => { shortBtnsSetting(); }, []); useEffect(() => { if(favoriteEdit){ setEditMode(favoriteEdit) } },[favoriteEdit]); // 메뉴가 열릴 때 초기화 useEffect(() => { if (menuOn) { // 스크롤 위치 초기화 if (scrollRef.current) { scrollRef.current.scrollTop = 0; } if (shortBtnScrollRef.current) { shortBtnScrollRef.current.scrollLeft = 0; } // 선택된 버튼 인덱스 초기화 setShortBtnIdx(0); // lastScrollTop 초기화 lastScrollTop.current = 0; } }, [menuOn]); return ( <>
{ userMids[0] } (madzoneviper)
{ !editMode && }
{ }
{ shortBtns.map((value, index) => ( onClickToMenuNavigate(value.menuId, index) } >{ value.menuName } )) }
{ getMenuCategory() }
); };