lotto lab UI 수정
- 히스토리, 과거 이력 더 보기 좋게 변경
This commit is contained in:
@@ -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>
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user