전략평가 차트 전체화면
This commit is contained in:
@@ -181,6 +181,8 @@ const StrategyEvaluationChart: React.FC<Props> = ({
|
||||
const [backtestRunning, setBacktestRunning] = useState(false);
|
||||
const [backtestError, setBacktestError] = useState<string | null>(null);
|
||||
const [magnifierActive, setMagnifierActive] = useState(false);
|
||||
const [chartFullscreen, setChartFullscreen] = useState(false);
|
||||
const chartRootRef = useRef<HTMLDivElement>(null);
|
||||
const marketBtnRef = useRef<HTMLButtonElement>(null);
|
||||
const managerRef = useRef<ChartManager | null>(null);
|
||||
const [internalOverlayVisibility, setInternalOverlayVisibility] = useState<ChartOverlayVisibility>(
|
||||
@@ -680,8 +682,47 @@ const StrategyEvaluationChart: React.FC<Props> = ({
|
||||
|
||||
const selectedBar = bars[selectedBarIndex] ?? null;
|
||||
|
||||
useEffect(() => {
|
||||
const cls = 'seval-chart-fs-lock';
|
||||
if (chartFullscreen) {
|
||||
document.body.classList.add(cls);
|
||||
} else {
|
||||
document.body.classList.remove(cls);
|
||||
}
|
||||
return () => document.body.classList.remove(cls);
|
||||
}, [chartFullscreen]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!chartFullscreen) return;
|
||||
const onKey = (e: KeyboardEvent) => {
|
||||
if (e.key !== 'Escape' || isKeyboardTypingTarget(e.target)) return;
|
||||
setChartFullscreen(false);
|
||||
};
|
||||
document.addEventListener('keydown', onKey);
|
||||
return () => document.removeEventListener('keydown', onKey);
|
||||
}, [chartFullscreen]);
|
||||
|
||||
useEffect(() => {
|
||||
const raf = requestAnimationFrame(() => {
|
||||
requestAnimationFrame(() => {
|
||||
const root = chartRootRef.current;
|
||||
const mgr = managerRef.current;
|
||||
if (!root || !mgr) return;
|
||||
const wrap = root.querySelector('.tv-chart-wrap') as HTMLElement | null;
|
||||
const w = root.clientWidth;
|
||||
const h = wrap?.clientHeight ?? root.clientHeight;
|
||||
if (w > 0 && h > 0) mgr.resize(w, h);
|
||||
mgr.refreshPaneSeparatorOverlay();
|
||||
});
|
||||
});
|
||||
return () => cancelAnimationFrame(raf);
|
||||
}, [chartFullscreen, bars.length]);
|
||||
|
||||
return (
|
||||
<div className="seval-chart btd-analysis-chart">
|
||||
<div
|
||||
ref={chartRootRef}
|
||||
className={`seval-chart btd-analysis-chart${chartFullscreen ? ' seval-chart--fullscreen' : ''}`}
|
||||
>
|
||||
<div className="seval-chart-toolbar btd-analysis-toolbar">
|
||||
<div className="seval-chart-toolbar-run">
|
||||
<button
|
||||
@@ -737,6 +778,31 @@ const StrategyEvaluationChart: React.FC<Props> = ({
|
||||
)}
|
||||
</div>
|
||||
<div className="seval-chart-toolbar-actions">
|
||||
<button
|
||||
type="button"
|
||||
className={`btd-analysis-tool seval-chart-fullscreen-btn${chartFullscreen ? ' seval-chart-fullscreen-btn--active' : ''}`}
|
||||
title={chartFullscreen ? '전체화면 종료 (Esc)' : '차트 전체화면 (캔들·보조지표)'}
|
||||
aria-label={chartFullscreen ? '전체화면 종료' : '차트 전체화면'}
|
||||
aria-pressed={chartFullscreen}
|
||||
disabled={loading || bars.length === 0}
|
||||
onClick={() => setChartFullscreen(v => !v)}
|
||||
>
|
||||
{chartFullscreen ? (
|
||||
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden>
|
||||
<polyline points="6,6 1,1" />
|
||||
<polyline points="10,6 15,1" />
|
||||
<polyline points="6,10 1,15" />
|
||||
<polyline points="10,10 15,15" />
|
||||
</svg>
|
||||
) : (
|
||||
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden>
|
||||
<polyline points="1,6 1,1 6,1" />
|
||||
<polyline points="10,1 15,1 15,6" />
|
||||
<polyline points="15,10 15,15 10,15" />
|
||||
<polyline points="6,15 1,15 1,10" />
|
||||
</svg>
|
||||
)}
|
||||
</button>
|
||||
{onOpenAiVerify && (
|
||||
<button
|
||||
type="button"
|
||||
|
||||
@@ -503,6 +503,45 @@
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* 차트 전체화면 — 캔들·보조지표 pane 전체 뷰포트 */
|
||||
.seval-chart--fullscreen {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 10050;
|
||||
background: var(--se-bg, var(--bg, #131722));
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.seval-chart--fullscreen .btd-analysis-main {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.seval-chart--fullscreen .seval-chart-body {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
body.seval-chart-fs-lock {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.seval-chart-fullscreen-btn {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.seval-chart-fullscreen-btn--active {
|
||||
color: var(--se-accent, #3d9be9);
|
||||
background: color-mix(in srgb, var(--se-accent, #3d9be9) 14%, transparent);
|
||||
border-color: color-mix(in srgb, var(--se-accent, #3d9be9) 45%, var(--se-border, var(--border)));
|
||||
}
|
||||
|
||||
.seval-chart-report-btn {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
|
||||
Reference in New Issue
Block a user