크로스헤어 세로선 날짜표시
This commit is contained in:
@@ -1,10 +1,9 @@
|
||||
/**
|
||||
* 캔들 pane 하단 시간축 — 거래량·보조지표가 아래에 있을 때 캔들 영역 바로 아래에도 시간 표시.
|
||||
*
|
||||
* 드래그 패닝 중에는 라벨 좌표를 재계산하지 않고 translate3d 로 차트와 동일 픽셀만큼 이동한다.
|
||||
* (setVisibleLogicalRange 직후 LWC 좌표 API가 한 프레임 늦게 갱신되는 문제 회피)
|
||||
* 크로스헤어 세로선 위치에 해당 날짜·시간 배지를 함께 표시한다.
|
||||
*/
|
||||
import React, { useEffect, useCallback, useRef } from 'react';
|
||||
import type { MouseEventParams, Time } from 'lightweight-charts';
|
||||
import type { ChartManager } from '../utils/ChartManager';
|
||||
import { subscribeChartTimeFormat } from '../utils/chartTimeFormat';
|
||||
|
||||
@@ -44,8 +43,36 @@ function applyOverlayLayout(
|
||||
|
||||
const CandlePaneTimeAxis: React.FC<CandlePaneTimeAxisProps> = ({ manager, containerEl }) => {
|
||||
const axisRef = useRef<HTMLDivElement>(null);
|
||||
const crosshairRef = useRef<HTMLDivElement>(null);
|
||||
const labelPoolRef = useRef<HTMLSpanElement[]>([]);
|
||||
|
||||
const updateCrosshairLabel = useCallback((p: MouseEventParams<Time>) => {
|
||||
const el = crosshairRef.current;
|
||||
if (!el) return;
|
||||
|
||||
const band = manager.getCandlePaneTimeAxisBand();
|
||||
if (!band || !p.time || p.point?.x == null) {
|
||||
el.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
const x = p.point.x;
|
||||
if (!Number.isFinite(x) || x < 0 || x > band.plotWidth) {
|
||||
el.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
el.style.display = 'flex';
|
||||
el.style.top = `${band.topY}px`;
|
||||
el.style.height = `${band.height}px`;
|
||||
el.style.left = `${x}px`;
|
||||
el.textContent = manager.formatCrosshairAxisTime(p.time);
|
||||
}, [manager]);
|
||||
|
||||
const hideCrosshairLabel = useCallback(() => {
|
||||
if (crosshairRef.current) crosshairRef.current.style.display = 'none';
|
||||
}, []);
|
||||
|
||||
const renderOverlay = useCallback(() => {
|
||||
const root = axisRef.current;
|
||||
if (!root || !containerEl.isConnected) return;
|
||||
@@ -88,6 +115,10 @@ const CandlePaneTimeAxis: React.FC<CandlePaneTimeAxisProps> = ({ manager, contai
|
||||
const unsubLayout = manager.subscribePaneLayout(renderOverlay);
|
||||
const unsubRange = manager.subscribeViewport(renderOverlay);
|
||||
const unsubFormat = subscribeChartTimeFormat(renderOverlay);
|
||||
const unsubCrosshair = manager.subscribeCrosshair(updateCrosshairLabel);
|
||||
|
||||
const onLeave = () => hideCrosshairLabel();
|
||||
containerEl.addEventListener('mouseleave', onLeave);
|
||||
|
||||
const timers = [0, 50, 150, 400].map(ms => window.setTimeout(renderOverlay, ms));
|
||||
return () => {
|
||||
@@ -95,17 +126,27 @@ const CandlePaneTimeAxis: React.FC<CandlePaneTimeAxisProps> = ({ manager, contai
|
||||
unsubLayout();
|
||||
unsubRange();
|
||||
unsubFormat();
|
||||
unsubCrosshair();
|
||||
containerEl.removeEventListener('mouseleave', onLeave);
|
||||
timers.forEach(clearTimeout);
|
||||
};
|
||||
}, [manager, containerEl, renderOverlay]);
|
||||
}, [manager, containerEl, renderOverlay, updateCrosshairLabel, hideCrosshairLabel]);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={axisRef}
|
||||
className="candle-pane-time-axis"
|
||||
style={{ display: 'none' }}
|
||||
aria-hidden
|
||||
/>
|
||||
<>
|
||||
<div
|
||||
ref={axisRef}
|
||||
className="candle-pane-time-axis"
|
||||
style={{ display: 'none' }}
|
||||
aria-hidden
|
||||
/>
|
||||
<div
|
||||
ref={crosshairRef}
|
||||
className="candle-pane-time-axis__crosshair"
|
||||
style={{ display: 'none' }}
|
||||
aria-hidden
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user