실시간 차트 캔들차트 높이 조절

This commit is contained in:
Macbook
2026-06-05 21:28:11 +09:00
parent 8c82c029e6
commit a0c8600607
4 changed files with 174 additions and 19 deletions
+95 -1
View File
@@ -250,6 +250,8 @@ const TradingChart: React.FC<TradingChartProps> = ({
const candleOnlyModeRef = useRef(false);
const paneAreaRatioRef = useRef(paneAreaRatio);
paneAreaRatioRef.current = paneAreaRatio;
/** 사용자 드래그로 설정한 pane 비율 — prop 변경 시 초기화 */
const userDraggedRatioRef = useRef<{ candle: number; aux: number } | null>(null);
const lastCrosshairPriceRef = useRef<number | null>(null);
const onTradeOrderRequestRef = useRef(onTradeOrderRequest);
onTradeOrderRequestRef.current = onTradeOrderRequest;
@@ -276,6 +278,13 @@ const TradingChart: React.FC<TradingChartProps> = ({
const indicatorSyncInFlightRef = useRef(false);
const [paneLegendPaused, setPaneLegendPaused] = useState(false);
const [chartBodyHeight, setChartBodyHeight] = useState(0);
/** 캔들 pane 하단 Y 위치 (드래그 핸들용) — null 이면 보조지표 없음 */
const [candlePaneBottomY, setCandlePaneBottomY] = useState<number | null>(null);
const paneResizeDragRef = useRef<{
startClientY: number;
startCandleH: number;
wrapperH: number;
} | null>(null);
const pendingIndicatorResyncRef = useRef(false);
const indicatorReloadGenRef = useRef(0);
const prevMarket = useRef<string>(market);
@@ -324,6 +333,74 @@ const TradingChart: React.FC<TradingChartProps> = ({
return () => ro.disconnect();
}, [chartMgr]);
/** 캔들 pane 하단 Y 추적 — 보조지표 있을 때만 드래그 핸들 표시 */
useEffect(() => {
const mgr = managerRef.current;
if (!mgr || !chartMgr) return;
const updateHandle = () => {
const layouts = mgr.getPaneLayouts();
const candle = layouts.find(l => l.paneIndex === 0);
const hasAux = layouts.some(l => l.paneIndex >= 2 && l.height > 20);
if (candle && candle.height > 20 && hasAux) {
setCandlePaneBottomY(candle.topY + candle.height);
} else {
setCandlePaneBottomY(null);
}
};
updateHandle();
return mgr.subscribePaneLayout(updateHandle);
}, [chartMgr]);
/** 캔들/보조지표 영역 분리선 드래그 핸들러 */
const onPaneResizePointerDown = useCallback((e: React.PointerEvent<HTMLDivElement>) => {
const mgr = managerRef.current;
const wrapper = wrapperRef.current;
if (!mgr || !wrapper) return;
e.preventDefault();
e.stopPropagation();
(e.currentTarget as HTMLDivElement).setPointerCapture(e.pointerId);
const layouts = mgr.getPaneLayouts();
const candle = layouts.find(l => l.paneIndex === 0);
paneResizeDragRef.current = {
startClientY: e.clientY,
startCandleH: candle?.height ?? 200,
wrapperH: wrapper.clientHeight,
};
}, []);
const onPaneResizePointerMove = useCallback((e: React.PointerEvent<HTMLDivElement>) => {
const drag = paneResizeDragRef.current;
const mgr = managerRef.current;
const wrapper = wrapperRef.current;
const container = containerRef.current;
if (!drag || !mgr || !wrapper || !container) return;
const dy = e.clientY - drag.startClientY;
const H = drag.wrapperH;
const MIN_CANDLE = 100;
const MIN_AUX = 60;
const newCandleH = Math.max(MIN_CANDLE, Math.min(H - MIN_AUX, drag.startCandleH + dy));
const newCandle = newCandleH / H;
const newAux = 1 - newCandle;
mgr.setPaneAreaRatio(newCandle, newAux);
// applyPaneLayout 이 ResizeObserver 로 재실행돼도 이 비율 유지
userDraggedRatioRef.current = { candle: newCandle, aux: newAux };
const required = mgr.resetPaneHeights(H);
// 스크롤 영역이 필요한 경우 컨테이너 확장
const newContainerH = required > H + 4 ? required : H;
if (container.style.height !== `${newContainerH}px`) {
container.style.height = `${newContainerH}px`;
}
}, []);
const onPaneResizePointerUp = useCallback(() => {
paneResizeDragRef.current = null;
}, []);
const toggleCandleOnly = useCallback(() => {
if (candleOnlyLocked) return;
setCandleOnlyMode(v => !v);
@@ -410,7 +487,8 @@ const TradingChart: React.FC<TradingChartProps> = ({
if (mgr.isCandleOnlyLayout()) {
mgr.restoreFromCandleFullscreen(wrapperH);
}
const ratio = paneAreaRatioRef.current;
// 사용자 드래그 비율 우선, 없으면 prop 기반 비율
const ratio = userDraggedRatioRef.current ?? paneAreaRatioRef.current;
if (ratio && ratio.aux > 0) {
mgr.setPaneAreaRatio(ratio.candle, ratio.aux);
} else {
@@ -443,6 +521,11 @@ const TradingChart: React.FC<TradingChartProps> = ({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [paneLayoutClamp, paneAreaRatio]);
/** paneAreaRatio prop 이 외부에서 변경되면 드래그 비율 초기화 (전략 변경 등) */
useEffect(() => {
userDraggedRatioRef.current = null;
}, [paneAreaRatio]);
/** pane 레이아웃 + 가격·시간축 정상화 (느린 네트워크·레이아웃 지연 후 복구용) */
const normalizeChartViewport = useCallback((mgr: ChartManager) => {
applyPaneLayout(mgr);
@@ -1614,6 +1697,17 @@ const TradingChart: React.FC<TradingChartProps> = ({
ref={containerRef}
className="chart-container"
/>
{/* 캔들 pane 하단 드래그 핸들 — 캔들/보조지표 높이 비율 조절 */}
{candlePaneBottomY != null && !candleOnlyMode && !paneLayoutClamp && chartPaintReady && (
<div
className="chart-pane-resize-handle"
style={{ top: candlePaneBottomY }}
onPointerDown={onPaneResizePointerDown}
onPointerMove={onPaneResizePointerMove}
onPointerUp={onPaneResizePointerUp}
onPointerCancel={onPaneResizePointerUp}
/>
)}
{chartMgr && !candleOnlyMode && showPaneLegend && chartPaintReady && (
<ChartRightToolbar
manager={chartMgr}