From 2de5cb8f932568aa4cce5713fa2b4508d3053bc0 Mon Sep 17 00:00:00 2001 From: caadiq Date: Tue, 13 Jan 2026 23:06:08 +0900 Subject: [PATCH] =?UTF-8?q?feat(mobile/schedule):=20=EA=B2=80=EC=83=89=20U?= =?UTF-8?q?X=20=EA=B0=9C=EC=84=A0=20=EB=B0=8F=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8=ED=94=BD=EC=BB=A4=20=EC=8A=A4=ED=83=80=EC=9D=BC=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 데이트픽커 년/월 선택에서 오늘 날짜 초록색 강조 - 검색어 변경 시 스크롤 초기화 개선 (requestAnimationFrame) - 유튜브 스타일 검색 UX 구현 - X 버튼 클릭 시 추천 검색어 화면으로 전환 - 뒤로가기 시 검색 결과 복원 및 검색어 유지 Co-Authored-By: Claude Opus 4.5 --- frontend/src/pages/mobile/public/Schedule.jsx | 178 +++++++++++------- 1 file changed, 111 insertions(+), 67 deletions(-) diff --git a/frontend/src/pages/mobile/public/Schedule.jsx b/frontend/src/pages/mobile/public/Schedule.jsx index 839517e..40c18d3 100644 --- a/frontend/src/pages/mobile/public/Schedule.jsx +++ b/frontend/src/pages/mobile/public/Schedule.jsx @@ -33,6 +33,8 @@ function MobileSchedule() { const [originalSearchQuery, setOriginalSearchQuery] = useState(''); // 필터링용 원본 쿼리 const [suggestions, setSuggestions] = useState([]); // 추천 검색어 목록 const [isLoadingSuggestions, setIsLoadingSuggestions] = useState(false); + const [lastSearchTerm, setLastSearchTerm] = useState(''); // 마지막 검색어 (복원용) + const [showSuggestionsScreen, setShowSuggestionsScreen] = useState(false); // 추천 검색어 화면 표시 여부 // 검색 모드 진입 함수 (history 상태 추가) const enterSearchMode = () => { @@ -46,22 +48,37 @@ function MobileSchedule() { setSearchInput(''); setOriginalSearchQuery(''); setSearchTerm(''); + setLastSearchTerm(''); setShowSuggestions(false); + setShowSuggestionsScreen(false); setSelectedSuggestionIndex(-1); }; + // 추천 검색어 화면 숨기고 검색 결과로 돌아가기 + const hideSuggestionsScreen = () => { + setShowSuggestionsScreen(false); + setSearchInput(lastSearchTerm); // 검색어 복원 + setOriginalSearchQuery(lastSearchTerm); + }; + // 뒤로가기 버튼 처리 useEffect(() => { const handlePopState = (e) => { if (isSearchMode) { - // 검색 모드에서 뒤로가기 시 검색 모드 종료 - exitSearchMode(); + // 추천 검색어 화면이고 검색 결과가 있으면 → 검색 결과로 돌아가기 + if (showSuggestionsScreen && searchTerm) { + hideSuggestionsScreen(); + window.history.pushState({ searchMode: true }, ''); + } else { + // 그 외에는 검색 모드 종료 + exitSearchMode(); + } } }; window.addEventListener('popstate', handlePopState); return () => window.removeEventListener('popstate', handlePopState); - }, [isSearchMode]); + }, [isSearchMode, showSuggestionsScreen, searchTerm, lastSearchTerm]); // 달력 월 변경 함수 const changeCalendarMonth = (delta) => { @@ -111,15 +128,16 @@ function MobileSchedule() { // 검색어 변경 시 스크롤 위치 초기화 useEffect(() => { - if (searchTerm) { - // virtualizer 스크롤 초기화 - virtualizer.scrollToOffset(0); - // DOM 스크롤도 초기화 (fallback) - if (scrollContainerRef.current) { - scrollContainerRef.current.scrollTop = 0; - } + if (searchTerm && !showSuggestionsScreen) { + // 약간의 지연 후 스크롤 초기화 (렌더링 완료 후) + requestAnimationFrame(() => { + virtualizer.scrollToOffset(0); + if (scrollContainerRef.current) { + scrollContainerRef.current.scrollTop = 0; + } + }); } - }, [searchTerm]); + }, [searchTerm, showSuggestionsScreen]); useEffect(() => { if (inView && hasNextPage && !isFetchingNextPage && isSearchMode && searchTerm) { @@ -326,14 +344,18 @@ function MobileSchedule() { setSearchInput(e.target.value); setOriginalSearchQuery(e.target.value); setShowSuggestions(true); + setShowSuggestionsScreen(true); setSelectedSuggestionIndex(-1); }} - onFocus={() => setShowSuggestions(true)} + onFocus={() => { + setShowSuggestions(true); + setShowSuggestionsScreen(true); + }} onKeyDown={(e) => { if (e.key === 'ArrowDown') { e.preventDefault(); - const newIndex = selectedSuggestionIndex < suggestions.length - 1 - ? selectedSuggestionIndex + 1 + const newIndex = selectedSuggestionIndex < suggestions.length - 1 + ? selectedSuggestionIndex + 1 : 0; setSelectedSuggestionIndex(newIndex); if (suggestions[newIndex]) { @@ -341,8 +363,8 @@ function MobileSchedule() { } } else if (e.key === 'ArrowUp') { e.preventDefault(); - const newIndex = selectedSuggestionIndex > 0 - ? selectedSuggestionIndex - 1 + const newIndex = selectedSuggestionIndex > 0 + ? selectedSuggestionIndex - 1 : suggestions.length - 1; setSelectedSuggestionIndex(newIndex); if (suggestions[newIndex]) { @@ -350,11 +372,14 @@ function MobileSchedule() { } } else if (e.key === 'Enter') { e.preventDefault(); - if (selectedSuggestionIndex >= 0 && suggestions[selectedSuggestionIndex]) { - setSearchInput(suggestions[selectedSuggestionIndex]); - setSearchTerm(suggestions[selectedSuggestionIndex]); - } else if (searchInput.trim()) { - setSearchTerm(searchInput); + const term = selectedSuggestionIndex >= 0 && suggestions[selectedSuggestionIndex] + ? suggestions[selectedSuggestionIndex] + : searchInput.trim(); + if (term) { + setSearchInput(term); + setSearchTerm(term); + setLastSearchTerm(term); // 검색어 저장 + setShowSuggestionsScreen(false); } setShowSuggestions(false); setSelectedSuggestionIndex(-1); @@ -366,14 +391,15 @@ function MobileSchedule() { autoFocus={!searchTerm} /> {searchInput && ( - - ))} + {yearRange.map(y => { + const isCurrentYear = y === new Date().getFullYear(); + return ( + + ); + })} {/* 월 선택 */}
- {Array.from({ length: 12 }, (_, i) => i + 1).map(m => ( - - ))} + {Array.from({ length: 12 }, (_, i) => i + 1).map(m => { + const today = new Date(); + const isCurrentMonth = year === today.getFullYear() && m === today.getMonth() + 1; + return ( + + ); + })}
) : (