From 3bcc47c0e8edf39767ef7db026e04e9c99933e76 Mon Sep 17 00:00:00 2001 From: hbyang Date: Fri, 12 Jun 2026 17:00:24 +0900 Subject: [PATCH] Schedule chips: horizontal scroll with edge arrows (PC navigable) Replace hidden-scrollbar row (inaccessible on desktop without touch) with a ChipScroller: single-row scroll + left/right arrow buttons that auto-hide at each end and a bg fade. Resets to start on tab switch. Co-Authored-By: Claude Opus 4.8 (1M context) --- frontend/src/components/ScheduleBoard.tsx | 88 +++++++++++++++++++++-- 1 file changed, 84 insertions(+), 4 deletions(-) diff --git a/frontend/src/components/ScheduleBoard.tsx b/frontend/src/components/ScheduleBoard.tsx index a7d7866..8ee2841 100644 --- a/frontend/src/components/ScheduleBoard.tsx +++ b/frontend/src/components/ScheduleBoard.tsx @@ -1,4 +1,4 @@ -import { useMemo, useState } from "react"; +import { useEffect, useLayoutEffect, useMemo, useRef, useState } from "react"; import { Link } from "react-router-dom"; import { timeOnly, dateKey } from "@/lib/format"; import { type Lang, dict, teamShort, roundLabel as tRound, dayName, monthEn } from "@/lib/i18n"; @@ -90,8 +90,8 @@ export default function ScheduleBoard({ ))} - {/* 칩: 날짜 또는 조 */} -
+ {/* 칩: 날짜 또는 조 — 단일 줄 가로 스크롤 + 양 끝 화살표(PC에서 마우스로 넘김, 끝 도달 시 숨김) */} + {tab === "date" ? dates.map((d) => ( setSelDate(d)}> @@ -103,7 +103,7 @@ export default function ScheduleBoard({ {lang === "en" ? `Group ${g}` : `${g}조`} ))} -
+ {/* 경기 카드 */}
@@ -120,6 +120,86 @@ export default function ScheduleBoard({ ); } +// 단일 줄 가로 스크롤 + 양 끝 화살표. 끝에 닿으면 해당 방향 화살표를 숨긴다. +// resetKey 가 바뀌면(탭 전환 등) 맨 앞으로 되감고 재측정한다. +function ChipScroller({ + children, + resetKey, +}: { + children: React.ReactNode; + resetKey?: string | number; +}) { + const ref = useRef(null); + const [atStart, setAtStart] = useState(true); + const [atEnd, setAtEnd] = useState(true); + + const measure = () => { + const el = ref.current; + if (!el) return; + const max = el.scrollWidth - el.clientWidth; + setAtStart(el.scrollLeft <= 1); + setAtEnd(el.scrollLeft >= max - 1); + }; + + // 탭/목록 변경 시 맨 앞으로 + 재측정 (레이아웃 확정 후) + useLayoutEffect(() => { + const el = ref.current; + if (el) el.scrollLeft = 0; + measure(); + }, [resetKey]); + + useEffect(() => { + measure(); + const el = ref.current; + if (!el) return; + el.addEventListener("scroll", measure, { passive: true }); + window.addEventListener("resize", measure); + return () => { + el.removeEventListener("scroll", measure); + window.removeEventListener("resize", measure); + }; + }, []); + + const nudge = (dir: 1 | -1) => + ref.current?.scrollBy({ left: dir * 220, behavior: "smooth" }); + + return ( +
+
+ {children} +
+ + {!atStart && ( +
+ +
+ )} + {!atEnd && ( +
+ +
+ )} +
+ ); +} + function Chip({ active, onClick,