lotto lab UI 수정

- 히스토리, 과거 이력 더 보기 좋게 변경
This commit is contained in:
2026-01-26 00:56:09 +09:00
parent 80a61e74ee
commit 472a55c0a7
2 changed files with 130 additions and 56 deletions

View File

@@ -1,4 +1,4 @@
import React, { useEffect, useMemo, useState } from 'react'; import React, { useEffect, useMemo, useRef, useState } from 'react';
import { deleteHistory, getHistory, getLatest, getStats, recommend } from '../../api'; import { deleteHistory, getHistory, getLatest, getStats, recommend } from '../../api';
const fmtKST = (value) => value?.replace('T', ' ') ?? ''; const fmtKST = (value) => value?.replace('T', ' ') ?? '';
@@ -166,7 +166,9 @@ const FrequencyChart = ({ stats }) => {
</div> </div>
</div> </div>
<div className="lotto-chart__plot" role="list"> <div className="lotto-chart__plot" role="list">
{series.map((item) => ( {series.map((item) => {
const showLabel = item.number === 1 || item.number % 5 === 0;
return (
<div <div
key={item.number} key={item.number}
className="lotto-chart__col" className="lotto-chart__col"
@@ -178,9 +180,12 @@ const FrequencyChart = ({ stats }) => {
title={`${item.number}번: ${item.count}`} title={`${item.number}번: ${item.count}`}
aria-label={`${item.number}${item.count}`} aria-label={`${item.number}${item.count}`}
/> />
<span className="lotto-chart__x">{item.number}</span> <span className="lotto-chart__x" aria-hidden={!showLabel}>
{showLabel ? item.number : ''}
</span>
</div> </div>
))} );
})}
</div> </div>
</div> </div>
); );
@@ -205,6 +210,9 @@ export default function Functions() {
const [result, setResult] = useState(null); const [result, setResult] = useState(null);
const [history, setHistory] = useState([]); const [history, setHistory] = useState([]);
const [historyExpanded, setHistoryExpanded] = useState(false);
const historyEndRef = useRef(null);
const prevHistoryExpandedRef = useRef(false);
const [stats, setStats] = useState(() => readStatsCache()); const [stats, setStats] = useState(() => readStatsCache());
const [statsLoading, setStatsLoading] = useState(false); const [statsLoading, setStatsLoading] = useState(false);
const [statsError, setStatsError] = useState(''); const [statsError, setStatsError] = useState('');
@@ -306,6 +314,20 @@ export default function Functions() {
refreshStats(); refreshStats();
}, []); }, []);
const visibleHistory = historyExpanded ? history : history.slice(0, 5);
useEffect(() => {
if (historyExpanded && !prevHistoryExpandedRef.current) {
requestAnimationFrame(() => {
historyEndRef.current?.scrollIntoView({
behavior: 'smooth',
block: 'end',
});
});
}
prevHistoryExpandedRef.current = historyExpanded;
}, [historyExpanded, visibleHistory.length]);
return ( return (
<div className="lotto-functions"> <div className="lotto-functions">
{error ? ( {error ? (
@@ -542,7 +564,9 @@ export default function Functions() {
)} )}
</section> </section>
<section className="lotto-panel"> </div>
<section className="lotto-panel lotto-panel--wide">
<div className="lotto-panel__head"> <div className="lotto-panel__head">
<div> <div>
<p className="lotto-panel__eyebrow">Distribution</p> <p className="lotto-panel__eyebrow">Distribution</p>
@@ -570,9 +594,7 @@ export default function Functions() {
</div> </div>
</div> </div>
{statsError ? ( {statsError ? <p className="lotto-empty">{statsError}</p> : null}
<p className="lotto-empty">{statsError}</p>
) : null}
{stats ? ( {stats ? (
<FrequencyChart stats={stats} /> <FrequencyChart stats={stats} />
) : ( ) : (
@@ -581,7 +603,6 @@ export default function Functions() {
</p> </p>
)} )}
</section> </section>
</div>
<section className="lotto-panel"> <section className="lotto-panel">
<div className="lotto-panel__head"> <div className="lotto-panel__head">
@@ -594,6 +615,30 @@ export default function Functions() {
</div> </div>
<div className="lotto-panel__actions"> <div className="lotto-panel__actions">
<span className="lotto-chip">{history.length}</span> <span className="lotto-chip">{history.length}</span>
{history.length > 5 ? (
<button
className="button ghost small lotto-history-toggle"
onClick={() =>
setHistoryExpanded((prev) => !prev)
}
aria-expanded={historyExpanded}
aria-label={
historyExpanded
? '히스토리 접기'
: '히스토리 더보기'
}
>
{historyExpanded ? '접기' : '더보기'}
<span
className={`lotto-history-toggle__icon ${
historyExpanded ? 'is-open' : ''
}`}
aria-hidden
>
</span>
</button>
) : null}
<button <button
className="button ghost small" className="button ghost small"
onClick={refreshHistory} onClick={refreshHistory}
@@ -610,7 +655,7 @@ export default function Functions() {
<p className="lotto-empty">저장된 히스토리가 없습니다.</p> <p className="lotto-empty">저장된 히스토리가 없습니다.</p>
) : ( ) : (
<div className="lotto-history"> <div className="lotto-history">
{history.map((item) => ( {visibleHistory.map((item) => (
<div key={item.id} className="lotto-history__item"> <div key={item.id} className="lotto-history__item">
<div className="lotto-history__meta"> <div className="lotto-history__meta">
<p>#{item.id}</p> <p>#{item.id}</p>
@@ -641,6 +686,7 @@ export default function Functions() {
</div> </div>
</div> </div>
))} ))}
<span ref={historyEndRef} />
</div> </div>
)} )}
</section> </section>

View File

@@ -69,6 +69,18 @@
gap: 16px; gap: 16px;
} }
.lotto-panel--wide .lotto-chart {
grid-template-columns: 60px minmax(0, 1fr);
}
.lotto-panel--wide .lotto-chart__plot {
height: 220px;
}
.lotto-panel--wide .lotto-chart__ticks {
min-height: 220px;
}
.lotto-panel__head { .lotto-panel__head {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
@@ -505,6 +517,22 @@
gap: 12px; gap: 12px;
} }
.lotto-history-toggle {
display: inline-flex;
align-items: center;
gap: 6px;
}
.lotto-history-toggle__icon {
display: inline-block;
transition: transform 0.2s ease;
font-size: 10px;
}
.lotto-history-toggle__icon.is-open {
transform: rotate(180deg);
}
.lotto-history__item { .lotto-history__item {
border: 1px solid var(--line); border: 1px solid var(--line);
border-radius: 18px; border-radius: 18px;