feat: 문제집 풀이 — 번호 스크롤 네비 추가
- 답안 패널 위에 가로 스크롤 번호 칩 바 추가 - 현재 문제는 인디고 글로우, 답안 완료는 보라색, 표시(★)는 주황 - 문제 전환 시 현재 번호로 자동 스크롤 (smooth center) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -727,6 +727,8 @@ export default function ExamPage() {
|
||||
</ProblemTextCard>
|
||||
</ProblemPanel>
|
||||
|
||||
<ProblemScrollNav problems={problems} answers={answers} flagged={flagged} currentIndex={currentIndex} onJump={moveProblem} />
|
||||
|
||||
<AnswerPanel>
|
||||
<AnswerPanelHeader>
|
||||
<AnswerPanelTitle>답안 선택</AnswerPanelTitle>
|
||||
@@ -925,6 +927,54 @@ export default function ExamPage() {
|
||||
return focusMode ? examContent : <AppShell>{examContent}</AppShell>;
|
||||
}
|
||||
|
||||
function ProblemScrollNav({
|
||||
problems,
|
||||
answers,
|
||||
flagged,
|
||||
currentIndex,
|
||||
onJump,
|
||||
}: {
|
||||
problems: ProblemSetDetail['problems'];
|
||||
answers: AnswerMap;
|
||||
flagged: Record<number, boolean>;
|
||||
currentIndex: number;
|
||||
onJump: (index: number) => void;
|
||||
}) {
|
||||
const scrollRef = useRef<HTMLDivElement>(null);
|
||||
const activeRef = useRef<HTMLButtonElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!activeRef.current || !scrollRef.current) return;
|
||||
const container = scrollRef.current;
|
||||
const chip = activeRef.current;
|
||||
const left = chip.offsetLeft - container.offsetWidth / 2 + chip.offsetWidth / 2;
|
||||
container.scrollTo({ left, behavior: 'smooth' });
|
||||
}, [currentIndex]);
|
||||
|
||||
return (
|
||||
<ScrollNavWrap ref={scrollRef}>
|
||||
{problems.map((problem, index) => {
|
||||
const answered = answers[problem.id] !== undefined && answers[problem.id] !== null;
|
||||
const isFlagged = !!flagged[problem.id];
|
||||
const isCurrent = index === currentIndex;
|
||||
return (
|
||||
<ScrollNavChip
|
||||
key={problem.id}
|
||||
ref={isCurrent ? activeRef : undefined}
|
||||
type="button"
|
||||
$current={isCurrent}
|
||||
$answered={answered}
|
||||
$flagged={isFlagged}
|
||||
onClick={() => onJump(index)}
|
||||
>
|
||||
{problem.number}
|
||||
</ScrollNavChip>
|
||||
);
|
||||
})}
|
||||
</ScrollNavWrap>
|
||||
);
|
||||
}
|
||||
|
||||
function toWholeSeconds(ms: number): number {
|
||||
if (ms <= 0) return 0;
|
||||
return Math.max(1, Math.round(ms / 1000));
|
||||
@@ -1296,6 +1346,59 @@ const AudioElement = styled.audio`
|
||||
display: none;
|
||||
`;
|
||||
|
||||
const ScrollNavWrap = styled.div`
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
overflow-x: auto;
|
||||
padding: 4px 2px;
|
||||
scroll-snap-type: x mandatory;
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
`;
|
||||
|
||||
const ScrollNavChip = styled.button<{
|
||||
$current: boolean;
|
||||
$answered: boolean;
|
||||
$flagged: boolean;
|
||||
}>`
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 40px;
|
||||
height: 36px;
|
||||
padding: 0 8px;
|
||||
flex-shrink: 0;
|
||||
scroll-snap-align: center;
|
||||
border-radius: 10px;
|
||||
border: 1px solid
|
||||
${({ $current, $flagged, $answered }) =>
|
||||
$current
|
||||
? 'rgba(129, 140, 248, 0.7)'
|
||||
: $flagged
|
||||
? 'rgba(245, 158, 11, 0.55)'
|
||||
: $answered
|
||||
? 'rgba(99, 102, 241, 0.35)'
|
||||
: theme.color.borderSoftAlpha};
|
||||
background: ${({ $current, $answered }) =>
|
||||
$current
|
||||
? 'rgba(79, 70, 229, 0.28)'
|
||||
: $answered
|
||||
? 'rgba(79, 70, 229, 0.1)'
|
||||
: 'rgba(255, 255, 255, 0.03)'};
|
||||
color: ${({ $current }) =>
|
||||
$current ? theme.color.textBright : theme.color.textSub};
|
||||
font-size: 13px;
|
||||
font-weight: ${({ $current }) => ($current ? 800 : 600)};
|
||||
font-family: ${theme.font.mono};
|
||||
transition: all 0.12s ease;
|
||||
box-shadow: ${({ $current }) =>
|
||||
$current ? theme.shadow.glowIndigo : 'none'};
|
||||
`;
|
||||
|
||||
const Workspace = styled.main`
|
||||
display: grid;
|
||||
gap: 24px;
|
||||
|
||||
Reference in New Issue
Block a user