알림목록 화면오류 수정
This commit is contained in:
@@ -358,6 +358,13 @@ const TradingChart: React.FC<TradingChartProps> = ({
|
|||||||
required = mgr.resetPaneHeights(wrapperH);
|
required = mgr.resetPaneHeights(wrapperH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (paneLayoutClamp) {
|
||||||
|
try {
|
||||||
|
const w = wrapper.clientWidth;
|
||||||
|
if (w > 0 && wrapperH > 0) mgr.resize(w, wrapperH);
|
||||||
|
} catch { /* ok */ }
|
||||||
|
}
|
||||||
|
|
||||||
if (!paneLayoutClamp && required > wrapperH + 4) {
|
if (!paneLayoutClamp && required > wrapperH + 4) {
|
||||||
container.style.height = `${required}px`;
|
container.style.height = `${required}px`;
|
||||||
} else {
|
} else {
|
||||||
@@ -730,7 +737,10 @@ const TradingChart: React.FC<TradingChartProps> = ({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!containerRef.current) return;
|
if (!containerRef.current) return;
|
||||||
|
|
||||||
const mgr = new ChartManager(containerRef.current, theme);
|
const mgr = new ChartManager(containerRef.current, theme, {
|
||||||
|
compactPaneLayout: paneLayoutClamp,
|
||||||
|
autoSize: !paneLayoutClamp,
|
||||||
|
});
|
||||||
managerRef.current = mgr;
|
managerRef.current = mgr;
|
||||||
setChartMgr(mgr);
|
setChartMgr(mgr);
|
||||||
if (!volumeVisibleRef.current) {
|
if (!volumeVisibleRef.current) {
|
||||||
|
|||||||
@@ -141,11 +141,11 @@ const TradeNotificationListRow: React.FC<Props> = ({
|
|||||||
|
|
||||||
const chartExpanded = isListLayout && expandedChartKey != null;
|
const chartExpanded = isListLayout && expandedChartKey != null;
|
||||||
const chartActive = isListLayout && chartsEnabled && panelActive;
|
const chartActive = isListLayout && chartsEnabled && panelActive;
|
||||||
const signalActive = isListLayout && panelActive && !chartExpanded;
|
const signalCardEnabled = isListLayout && panelActive && !chartExpanded;
|
||||||
const ticker = tickers?.get(item.market);
|
const ticker = tickers?.get(item.market);
|
||||||
const expandedIndicator = indicatorCards.find(c => c.id === expandedChartKey);
|
const expandedIndicator = indicatorCards.find(c => c.id === expandedChartKey);
|
||||||
|
|
||||||
const signalSnapshotEnabled = isListLayout && panelActive && item.strategyId != null;
|
const signalSnapshotEnabled = signalCardEnabled && item.strategyId != null;
|
||||||
const { snapshot, loading: signalLoading } = useTradeNotificationSignalSnapshot(
|
const { snapshot, loading: signalLoading } = useTradeNotificationSignalSnapshot(
|
||||||
item.market,
|
item.market,
|
||||||
item.strategyId,
|
item.strategyId,
|
||||||
@@ -356,28 +356,26 @@ const TradeNotificationListRow: React.FC<Props> = ({
|
|||||||
</TradeNotificationHScrollPane>
|
</TradeNotificationHScrollPane>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{signalActive && (
|
<TradeNotificationHScrollPane
|
||||||
<TradeNotificationHScrollPane
|
className="tnl-row-gallery-signal"
|
||||||
className="tnl-row-gallery-signal"
|
label="신호 일치율"
|
||||||
label="신호 일치율"
|
>
|
||||||
>
|
<TradeNotificationSignalCard
|
||||||
<TradeNotificationSignalCard
|
market={item.market}
|
||||||
market={item.market}
|
strategyId={item.strategyId}
|
||||||
strategyId={item.strategyId}
|
strategy={strategy}
|
||||||
strategy={strategy}
|
strategies={strategies}
|
||||||
strategies={strategies}
|
strategyLabel={strategyLabel}
|
||||||
strategyLabel={strategyLabel}
|
meta={strategyLabel}
|
||||||
meta={strategyLabel}
|
candleType={item.candleType}
|
||||||
candleType={item.candleType}
|
theme={theme}
|
||||||
theme={theme}
|
ticker={ticker}
|
||||||
ticker={ticker}
|
enabled={signalCardEnabled}
|
||||||
enabled={signalActive}
|
snapshot={snapshot}
|
||||||
snapshot={snapshot}
|
signalLoading={signalLoading}
|
||||||
signalLoading={signalLoading}
|
onExpandCharts={() => setExpandedChartKey('candle')}
|
||||||
onExpandCharts={() => setExpandedChartKey('candle')}
|
/>
|
||||||
/>
|
</TradeNotificationHScrollPane>
|
||||||
</TradeNotificationHScrollPane>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="tnl-row-gallery-candle" aria-label="캔들 차트">
|
<div className="tnl-row-gallery-candle" aria-label="캔들 차트">
|
||||||
<TradeSignalChartCard
|
<TradeSignalChartCard
|
||||||
|
|||||||
@@ -50,6 +50,17 @@ const TradeSignalMiniChart: React.FC<Props> = ({
|
|||||||
const marketRef = useRef(market);
|
const marketRef = useRef(market);
|
||||||
marketRef.current = market;
|
marketRef.current = market;
|
||||||
|
|
||||||
|
const syncChartLayout = useCallback(() => {
|
||||||
|
const wrap = canvasWrapRef.current;
|
||||||
|
const mgr = managerRef.current;
|
||||||
|
if (!wrap || !mgr?.hasMainSeries()) return;
|
||||||
|
const chartWrap = wrap.querySelector<HTMLElement>('.tv-chart-wrap');
|
||||||
|
const w = chartWrap?.clientWidth ?? wrap.clientWidth;
|
||||||
|
const h = chartWrap?.clientHeight ?? wrap.clientHeight;
|
||||||
|
if (w <= 0 || h <= 0) return;
|
||||||
|
mgr.syncLayout(w, h);
|
||||||
|
}, []);
|
||||||
|
|
||||||
const handleCandlesReady = useCallback(() => {
|
const handleCandlesReady = useCallback(() => {
|
||||||
chartLiveReadyRef.current = true;
|
chartLiveReadyRef.current = true;
|
||||||
const pending = pendingBarRef.current;
|
const pending = pendingBarRef.current;
|
||||||
@@ -57,12 +68,8 @@ const TradeSignalMiniChart: React.FC<Props> = ({
|
|||||||
if (!pending || !mgr || barsMarketRef.current !== marketRef.current) return;
|
if (!pending || !mgr || barsMarketRef.current !== marketRef.current) return;
|
||||||
pendingBarRef.current = null;
|
pendingBarRef.current = null;
|
||||||
mgr.updateBar(pending);
|
mgr.updateBar(pending);
|
||||||
const wrap = canvasWrapRef.current;
|
syncChartLayout();
|
||||||
if (wrap && wrap.clientWidth > 0 && wrap.clientHeight > 0) {
|
}, [syncChartLayout]);
|
||||||
mgr.resetPaneHeights(wrap.clientHeight);
|
|
||||||
mgr.resize(wrap.clientWidth, wrap.clientHeight);
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const applyRealtimeBar = useCallback((bar: OHLCVBar, append: boolean) => {
|
const applyRealtimeBar = useCallback((bar: OHLCVBar, append: boolean) => {
|
||||||
if (barsMarketRef.current !== marketRef.current) return;
|
if (barsMarketRef.current !== marketRef.current) return;
|
||||||
@@ -107,17 +114,6 @@ const TradeSignalMiniChart: React.FC<Props> = ({
|
|||||||
barsMarketRef.current = barsMarket;
|
barsMarketRef.current = barsMarket;
|
||||||
const barsReady = bars.length >= 2 && barsMarket === market;
|
const barsReady = bars.length >= 2 && barsMarket === market;
|
||||||
|
|
||||||
const syncChartLayout = useCallback(() => {
|
|
||||||
const wrap = canvasWrapRef.current;
|
|
||||||
const mgr = managerRef.current;
|
|
||||||
if (!wrap || !mgr?.hasMainSeries()) return;
|
|
||||||
const w = wrap.clientWidth;
|
|
||||||
const h = wrap.clientHeight;
|
|
||||||
if (w <= 0 || h <= 0) return;
|
|
||||||
mgr.resetPaneHeights(h);
|
|
||||||
mgr.resize(w, h);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
chartLiveReadyRef.current = false;
|
chartLiveReadyRef.current = false;
|
||||||
pendingBarRef.current = null;
|
pendingBarRef.current = null;
|
||||||
@@ -132,14 +128,8 @@ const TradeSignalMiniChart: React.FC<Props> = ({
|
|||||||
const timers = [200, 600, 1200].map(delay =>
|
const timers = [200, 600, 1200].map(delay =>
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (!canvasWrapRef.current) return;
|
if (!canvasWrapRef.current) return;
|
||||||
const h = canvasWrapRef.current.clientHeight;
|
|
||||||
if (h <= 0) return;
|
|
||||||
const w = canvasWrapRef.current.clientWidth;
|
|
||||||
if (managerRef.current?.hasMainSeries()) {
|
if (managerRef.current?.hasMainSeries()) {
|
||||||
if (w > 0) {
|
syncChartLayout();
|
||||||
managerRef.current.resetPaneHeights(h);
|
|
||||||
managerRef.current.resize(w, h);
|
|
||||||
}
|
|
||||||
} else if (!chartReloadTriggeredRef.current && delay >= 600 && barsReady) {
|
} else if (!chartReloadTriggeredRef.current && delay >= 600 && barsReady) {
|
||||||
chartReloadTriggeredRef.current = true;
|
chartReloadTriggeredRef.current = true;
|
||||||
setChartReloadTick(t => t + 1);
|
setChartReloadTick(t => t + 1);
|
||||||
|
|||||||
@@ -1160,6 +1160,16 @@ ul.tnl-list.tnl-list--grid {
|
|||||||
min-height: 300px;
|
min-height: 300px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 그리드형 — 상세 메타와 투자전략 구분선 사이 여백 축소 */
|
||||||
|
.tnl-summary-card--grid .tnl-summary-meta {
|
||||||
|
flex: 0 1 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tnl-summary-card--grid .tnl-summary-strategy-field {
|
||||||
|
margin-top: -6px;
|
||||||
|
padding-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
.tnl-row--grid.tnl-row--unread .tnl-summary-card {
|
.tnl-row--grid.tnl-row--unread .tnl-summary-card {
|
||||||
border-color: rgba(122, 162, 247, 0.45);
|
border-color: rgba(122, 162, 247, 0.45);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -258,13 +258,23 @@ export class ChartManager {
|
|||||||
/** applyPaneLayout / resetPaneHeights 에서 측정한 마지막 가용 높이(px) */
|
/** applyPaneLayout / resetPaneHeights 에서 측정한 마지막 가용 높이(px) */
|
||||||
private _lastLayoutAvailableHeight?: number;
|
private _lastLayoutAvailableHeight?: number;
|
||||||
|
|
||||||
constructor(container: HTMLElement, theme: Theme) {
|
/** 알림 목록 미니 차트 등 고정 높이 — pane 최소 px·비율 완화 */
|
||||||
|
private _compactPaneLayout = false;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
container: HTMLElement,
|
||||||
|
theme: Theme,
|
||||||
|
options?: { autoSize?: boolean; compactPaneLayout?: boolean },
|
||||||
|
) {
|
||||||
this.container = container;
|
this.container = container;
|
||||||
|
this._compactPaneLayout = options?.compactPaneLayout ?? false;
|
||||||
|
const autoSize = options?.autoSize ?? !this._compactPaneLayout;
|
||||||
const t = getTheme(theme);
|
const t = getTheme(theme);
|
||||||
this.chart = createChart(container, {
|
this.chart = createChart(container, {
|
||||||
/* autoSize: true → LWC가 내부적으로 ResizeObserver를 사용해 컨테이너 크기 변경을 자동으로 처리.
|
/* autoSize: true → LWC가 내부적으로 ResizeObserver를 사용해 컨테이너 크기 변경을 자동으로 처리.
|
||||||
멀티 레이아웃에서 초기 렌더 시 컨테이너가 0 크기일 때도 차트가 정상 표시됨. */
|
멀티 레이아웃에서 초기 렌더 시 컨테이너가 0 크기일 때도 차트가 정상 표시됨.
|
||||||
autoSize: true,
|
compact(미니) 차트는 autoSize:false + syncLayout() 으로 피드백 루프 방지. */
|
||||||
|
autoSize,
|
||||||
layout: {
|
layout: {
|
||||||
background: { type: ColorType.Solid, color: t.bgColor },
|
background: { type: ColorType.Solid, color: t.bgColor },
|
||||||
textColor: t.textColor,
|
textColor: t.textColor,
|
||||||
@@ -764,10 +774,39 @@ export class ChartManager {
|
|||||||
|
|
||||||
/** 지표 추가·제거 후 pane index 동기화 + stretch 재배분 */
|
/** 지표 추가·제거 후 pane index 동기화 + stretch 재배분 */
|
||||||
private _finalizeIndicatorPaneLayout(): void {
|
private _finalizeIndicatorPaneLayout(): void {
|
||||||
if (this._activeIndicatorPaneIndices().size === 0) return;
|
|
||||||
this._removeOrphanSubPanes();
|
this._removeOrphanSubPanes();
|
||||||
this._resyncIndicatorPaneIndices();
|
if (this._activeIndicatorPaneIndices().size > 0) {
|
||||||
this.resetPaneHeights(this._lastLayoutAvailableHeight);
|
this._resyncIndicatorPaneIndices();
|
||||||
|
} else {
|
||||||
|
this._trimTrailingEmptySubPanes();
|
||||||
|
}
|
||||||
|
const H = this._lastLayoutAvailableHeight ?? this.container.clientHeight;
|
||||||
|
this.resetPaneHeights(H > 0 ? H : undefined);
|
||||||
|
if (this._compactPaneLayout && H > 0) {
|
||||||
|
try {
|
||||||
|
const w = this.container.clientWidth;
|
||||||
|
if (w > 0) this.chart.resize(w, H);
|
||||||
|
} catch { /* ok */ }
|
||||||
|
}
|
||||||
|
this._notifyPaneLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 고정 높이 미니 차트 — pane 비율 재계산 + LWC resize 를 한 번에 수행.
|
||||||
|
* (autoSize:false 일 때 외부 ResizeObserver 가 호출)
|
||||||
|
*/
|
||||||
|
syncLayout(width: number, height: number): void {
|
||||||
|
if (height > 0) this._lastLayoutAvailableHeight = height;
|
||||||
|
this._removeOrphanSubPanes();
|
||||||
|
if (this._activeIndicatorPaneIndices().size > 0) {
|
||||||
|
this._resyncIndicatorPaneIndices();
|
||||||
|
} else {
|
||||||
|
this._trimTrailingEmptySubPanes();
|
||||||
|
}
|
||||||
|
this.resetPaneHeights(height);
|
||||||
|
try {
|
||||||
|
if (width > 0 && height > 0) this.chart.resize(width, height);
|
||||||
|
} catch { /* ok */ }
|
||||||
this._notifyPaneLayout();
|
this._notifyPaneLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3530,7 +3569,7 @@ export class ChartManager {
|
|||||||
|
|
||||||
const volumeShown = this._volumeVisible && !this._candleOnlyLayout;
|
const volumeShown = this._volumeVisible && !this._candleOnlyLayout;
|
||||||
const VOL_FRAC = this._volumeFrac(H);
|
const VOL_FRAC = this._volumeFrac(H);
|
||||||
const MIN_IND_PX = 80;
|
const MIN_IND_PX = this._minIndPx(H);
|
||||||
const ORPHAN_STRETCH = 0.0001;
|
const ORPHAN_STRETCH = 0.0001;
|
||||||
/** 보조지표 pane 은 동일 stretch weight → 항상 같은 높이 */
|
/** 보조지표 pane 은 동일 stretch weight → 항상 같은 높이 */
|
||||||
const IND_EQUAL_WEIGHT = 1;
|
const IND_EQUAL_WEIGHT = 1;
|
||||||
@@ -3569,14 +3608,26 @@ export class ChartManager {
|
|||||||
return totalRequired;
|
return totalRequired;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _minIndPx(H: number): number {
|
||||||
|
if (this._compactPaneLayout) {
|
||||||
|
return Math.min(56, Math.max(28, Math.round(H * 0.2)));
|
||||||
|
}
|
||||||
|
return 80;
|
||||||
|
}
|
||||||
|
|
||||||
private _paneHeightFractions(
|
private _paneHeightFractions(
|
||||||
indicatorPaneCount: number,
|
indicatorPaneCount: number,
|
||||||
volFrac: number,
|
volFrac: number,
|
||||||
|
availableHeight?: number,
|
||||||
): { mainFrac: number; eachIndFrac: number } {
|
): { mainFrac: number; eachIndFrac: number } {
|
||||||
const N = indicatorPaneCount;
|
const N = indicatorPaneCount;
|
||||||
if (N === 0) {
|
if (N === 0) {
|
||||||
return { mainFrac: 1 - volFrac, eachIndFrac: 0 };
|
return { mainFrac: 1 - volFrac, eachIndFrac: 0 };
|
||||||
}
|
}
|
||||||
|
if (this._compactPaneLayout && N === 1 && availableHeight != null && availableHeight < 400) {
|
||||||
|
const mainFrac = 0.52;
|
||||||
|
return { mainFrac, eachIndFrac: Math.max(0.28, 1 - mainFrac - volFrac) };
|
||||||
|
}
|
||||||
if (N <= 3) {
|
if (N <= 3) {
|
||||||
return { mainFrac: 0.5, eachIndFrac: (0.5 - volFrac) / N };
|
return { mainFrac: 0.5, eachIndFrac: (0.5 - volFrac) / N };
|
||||||
}
|
}
|
||||||
@@ -3621,7 +3672,7 @@ export class ChartManager {
|
|||||||
? this._lastLayoutAvailableHeight
|
? this._lastLayoutAvailableHeight
|
||||||
: this.container.clientHeight);
|
: this.container.clientHeight);
|
||||||
const N = this._activeIndicatorPaneIndices().size;
|
const N = this._activeIndicatorPaneIndices().size;
|
||||||
const { mainFrac } = this._paneHeightFractions(N, this._volumeFrac(H));
|
const { mainFrac } = this._paneHeightFractions(N, this._volumeFrac(H), H);
|
||||||
|
|
||||||
return this._applyPaneStretchFactors(mainFrac, availableHeight);
|
return this._applyPaneStretchFactors(mainFrac, availableHeight);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user