보조지표 전체보기 후 복귀버튼 추가

This commit is contained in:
Macbook
2026-06-02 23:44:37 +09:00
parent c6976a2e03
commit 08fb442d4a
5 changed files with 206 additions and 2 deletions
+30 -2
View File
@@ -6,6 +6,7 @@ import React, { useState, useEffect, useCallback, useRef } from 'react';
import type { IndicatorConfig } from '../types';
import { ChartManager } from '../utils/ChartManager';
import { getPaneHostId } from '../utils/indicatorPaneMerge';
import { isIndicatorPaneFocused, resolveFocusedPaneLayoutId } from '../utils/indicatorPaneFocus';
import { buildPaneItems, resolvePaneItemLayout } from './paneLegendLayout';
import { PAPER_OVERLAY_MENU_TITLE } from '../utils/paperChartOverlayVisibility';
@@ -154,8 +155,8 @@ function buildMenuItems(
const itemHostId = getPaneHostId(item.id, indicators);
const isMergedPane = samePaneItems.length > 1;
const showSplit = isMergedPane && item.id === itemHostId && !!opts.onSplit;
const isFocused = opts.focusedId === item.id;
const showExpand = !!(opts.onExpand || (isFocused && opts.onRestore));
const isFocused = isIndicatorPaneFocused(item.id, opts.focusedId, indicators);
const showExpand = !!(opts.onExpand || (opts.focusedId && opts.onRestore));
const done = opts.onDone ?? (() => {});
const cfg = indicators.find(ind => ind.id === item.id);
const isHidden = cfg?.hidden === true;
@@ -302,6 +303,17 @@ const ChartRightToolbar: React.FC<ChartRightToolbarProps> = ({
const showOverlayMenu = !!(candleOverlayToggles?.length && candlePane && onToggleCandleOverlay);
const overlayMenuOpen = openMenuId === CANDLE_OVERLAY_MENU_ID;
const focusedLayoutId = focusedId
? resolveFocusedPaneLayoutId(focusedId, indicators)
: null;
const focusedPaneLayout = focusedLayoutId
? (() => {
const item = paneItems.find(p => isIndicatorPaneFocused(p.id, focusedId, indicators));
if (item) return resolvePaneItemLayout(manager, item);
return manager.getIndicatorPaneScreenLayout(focusedLayoutId);
})()
: null;
const overlayMenuItems: MenuItemDef[] = showOverlayMenu
? candleOverlayToggles!.map(item => ({
key: item.key,
@@ -320,6 +332,22 @@ const ChartRightToolbar: React.FC<ChartRightToolbarProps> = ({
className="chart-right-toolbar-inner"
style={{ height: Math.max(innerHeight, chartHeight) }}
>
{focusedId && onRestore && focusedPaneLayout && (
<div
className="chart-right-toolbar-cluster chart-right-toolbar-cluster--focus-restore"
style={{ top: focusedPaneLayout.topY + 4 }}
>
<button
type="button"
className="tv-side-btn chart-right-toolbar-btn chart-right-toolbar-btn--restore active"
title="기본 보기로 복원 (모든 보조지표 표시)"
onClick={e => { e.stopPropagation(); onRestore(); }}
>
<IconRestore />
</button>
</div>
)}
{showOverlayMenu && (
<div
className="chart-right-toolbar-cluster chart-right-toolbar-cluster--overlay"
@@ -0,0 +1,87 @@
/**
* 보조지표 단독 전체보기 모드 — 해당 pane 우측 상단 «기본 보기로 복원» 버튼
*/
import React, { useState, useEffect, useCallback } from 'react';
import type { IndicatorConfig } from '../types';
import { ChartManager } from '../utils/ChartManager';
import { resolveFocusedPaneLayoutId } from '../utils/indicatorPaneFocus';
const IconRestore = () => (
<svg width="13" height="13" viewBox="0 0 14 14" fill="none"
stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<polyline points="5,13 1,13 1,9"/>
<line x1="1" y1="13" x2="5.5" y2="8.5"/>
<polyline points="9,1 13,1 13,5"/>
<line x1="13" y1="1" x2="8.5" y2="5.5"/>
</svg>
);
export interface IndicatorPaneControlsProps {
manager: ChartManager;
containerEl: HTMLElement;
indicators: IndicatorConfig[];
focusedId: string | null;
onRestore: () => void;
}
const IndicatorPaneControls: React.FC<IndicatorPaneControlsProps> = ({
manager,
containerEl,
indicators,
focusedId,
onRestore,
}) => {
const [pos, setPos] = useState<{ left: number; top: number } | null>(null);
const updatePos = useCallback(() => {
if (!focusedId) {
setPos(null);
return;
}
const layoutId = resolveFocusedPaneLayoutId(focusedId, indicators);
const lay = manager.getIndicatorPaneScreenLayout(layoutId);
if (!lay) {
setPos(null);
return;
}
const rect = containerEl.getBoundingClientRect();
setPos({
left: rect.left + rect.width - 40,
top: rect.top + lay.topY + 4,
});
}, [manager, containerEl, focusedId, indicators]);
useEffect(() => {
updatePos();
const ro = new ResizeObserver(() => updatePos());
ro.observe(containerEl);
const unsub = manager.subscribePaneLayout(() => updatePos());
const id = setInterval(updatePos, 400);
return () => {
ro.disconnect();
unsub();
clearInterval(id);
};
}, [containerEl, manager, updatePos, focusedId]);
if (!focusedId || !pos) return null;
return (
<div
className="indicator-pane-controls"
style={{ position: 'fixed', left: pos.left, top: pos.top, zIndex: 505 }}
onMouseDown={e => e.stopPropagation()}
>
<button
type="button"
className="pane-btn pane-btn-expand active"
onClick={e => { e.stopPropagation(); onRestore(); }}
title="기본 보기로 복원 (모든 보조지표 표시)"
>
<IconRestore />
</button>
</div>
);
};
export default IndicatorPaneControls;
+37
View File
@@ -56,6 +56,7 @@ import ChartHoverToolbar from './ChartHoverToolbar';
import ChartMagnifier from './ChartMagnifier';
import ChartContextMenu from './ChartContextMenu';
import CandlePaneControls from './CandlePaneControls';
import IndicatorPaneControls from './IndicatorPaneControls';
import { getKoreanName } from '../utils/marketNameCache';
import { DEFAULT_DISPLAY_TIMEZONE } from '../utils/timezone';
import { chartHasStaleIndicators, classifyIndicatorChartChange, singleIndParamKey } from '../utils/indicatorChartSync';
@@ -1667,6 +1668,15 @@ const TradingChart: React.FC<TradingChartProps> = ({
onRestore={() => setCandleOnlyMode(false)}
/>
)}
{chartMgr && focusedIndicatorId && onRestoreIndicators && chartPaintReady && (
<IndicatorPaneControlsPortal
manager={chartMgr}
indicators={indicators}
getContainer={() => containerRef.current}
focusedId={focusedIndicatorId}
onRestore={onRestoreIndicators}
/>
)}
{chartMgr && !candleOnlyMode && showPaneLegend && chartPaintReady && (
<PaneLegendPortal
key={indKey(indicators)}
@@ -1723,6 +1733,33 @@ const CandlePaneTimeAxisPortal: React.FC<{
return <CandlePaneTimeAxis manager={manager} containerEl={el} />;
};
const IndicatorPaneControlsPortal: React.FC<{
manager: ChartManager;
indicators: IndicatorConfig[];
getContainer: () => HTMLElement | null;
focusedId: string;
onRestore: () => void;
}> = ({ manager, indicators, getContainer, focusedId, onRestore }) => {
const [el, setEl] = useState<HTMLElement | null>(null);
useEffect(() => {
const c = getContainer();
if (c) { setEl(c); return; }
const tid = setTimeout(() => setEl(getContainer()), 100);
return () => clearTimeout(tid);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
if (!el) return null;
return (
<IndicatorPaneControls
manager={manager}
containerEl={el}
indicators={indicators}
focusedId={focusedId}
onRestore={onRestore}
/>
);
};
const CandlePaneControlsPortal: React.FC<{
manager: ChartManager;
getContainer: () => HTMLElement | null;