알림팝업 전체닫기 오류 수정
This commit is contained in:
@@ -17,6 +17,7 @@ import type { TradeOrderFillRequest } from '../types';
|
||||
import PaperMiniChart from './paper/PaperMiniChart';
|
||||
import PaperIndicatorPanel from './paper/PaperIndicatorPanel';
|
||||
import PaperCompactOrderbook from './paper/PaperCompactOrderbook';
|
||||
import type { Theme } from '../types';
|
||||
|
||||
const COIN_COLORS: Record<string, string> = {
|
||||
BTC: '#f7931a', ETH: '#627eea', XRP: '#23292f', SOL: '#9945ff',
|
||||
@@ -28,6 +29,7 @@ type HistoryTab = 'open' | 'recent';
|
||||
type RightTab = 'trade' | 'orderbook';
|
||||
|
||||
interface Props {
|
||||
theme?: Theme;
|
||||
tickers?: Map<string, { tradePrice: number | null }>;
|
||||
onGoChart?: (market: string) => void;
|
||||
refreshKey?: number;
|
||||
@@ -66,6 +68,7 @@ function coinIcon(code: string): string {
|
||||
}
|
||||
|
||||
const PaperTradingPage: React.FC<Props> = ({
|
||||
theme = 'dark',
|
||||
tickers,
|
||||
refreshKey = 0,
|
||||
defaultMarket = 'KRW-BTC',
|
||||
@@ -264,7 +267,7 @@ const PaperTradingPage: React.FC<Props> = ({
|
||||
|
||||
<main className="ptd-col ptd-col--center">
|
||||
<div className="ptd-card ptd-chart-card">
|
||||
<PaperMiniChart market={selectedMarket} />
|
||||
<PaperMiniChart market={selectedMarket} theme={theme} />
|
||||
<PaperIndicatorPanel market={selectedMarket} />
|
||||
</div>
|
||||
</main>
|
||||
|
||||
@@ -93,8 +93,17 @@ export const TradeSignalSnackbar: React.FC<Props> = ({
|
||||
setPageIndex(p => Math.min(totalPages - 1, p + 1));
|
||||
}, [totalPages]);
|
||||
|
||||
const dismissAll = useCallback(() => {
|
||||
dismissAllToasts();
|
||||
setPageIndex(0);
|
||||
positionsRef.current = {};
|
||||
}, [dismissAllToasts]);
|
||||
|
||||
const dismissCurrentPage = useCallback(() => {
|
||||
dismissToasts(pageItems.map(i => i.id));
|
||||
positionsRef.current = Object.fromEntries(
|
||||
Object.entries(positionsRef.current).filter(([id]) => !pageItems.some(p => p.id === id)),
|
||||
);
|
||||
}, [dismissToasts, pageItems]);
|
||||
|
||||
const handleDragStart = useCallback((e: React.PointerEvent, key: string) => {
|
||||
@@ -219,7 +228,7 @@ export const TradeSignalSnackbar: React.FC<Props> = ({
|
||||
<button
|
||||
type="button"
|
||||
className="tsn-dismiss-all"
|
||||
onClick={dismissAllToasts}
|
||||
onClick={dismissAll}
|
||||
title={`대기 중인 알림 ${toastNotifications.length}건 전체 닫기`}
|
||||
>
|
||||
전체닫기 ({toastNotifications.length})
|
||||
|
||||
@@ -99,10 +99,10 @@ const BacktestAssetChart: React.FC<Props> = memo(({ curve, markers }) => {
|
||||
|
||||
{[0.25, 0.5, 0.75].map(r => {
|
||||
const y = pad.t + innerH * (1 - r);
|
||||
return <line key={r} x1={pad.l} y1={y} x2={W - pad.r} y2={y} stroke="rgba(201,162,39,0.07)" strokeWidth="1" />;
|
||||
return <line key={r} x1={pad.l} y1={y} x2={W - pad.r} y2={y} className="btd-chart-grid" />;
|
||||
})}
|
||||
|
||||
<text x={pad.l - 8} y={pad.t - 6} textAnchor="start" fill="#6272a4" fontSize="8">자본금</text>
|
||||
<text x={pad.l - 8} y={pad.t - 6} textAnchor="start" className="btd-chart-label" fontSize="8">자본금</text>
|
||||
|
||||
{chart.yTicks.map(v => (
|
||||
<text
|
||||
@@ -111,7 +111,7 @@ const BacktestAssetChart: React.FC<Props> = memo(({ curve, markers }) => {
|
||||
y={chart.toY(v)}
|
||||
textAnchor="end"
|
||||
dominantBaseline="middle"
|
||||
fill="#6272a4"
|
||||
className="btd-chart-label"
|
||||
fontSize="8.5"
|
||||
>
|
||||
{fmtY(v)}
|
||||
@@ -124,7 +124,7 @@ const BacktestAssetChart: React.FC<Props> = memo(({ curve, markers }) => {
|
||||
key={i}
|
||||
d={seg.d}
|
||||
fill="none"
|
||||
stroke={seg.up ? '#22c55e' : '#ef4444'}
|
||||
className={seg.up ? 'btd-chart-line-up' : 'btd-chart-line-down'}
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
@@ -152,7 +152,7 @@ const BacktestAssetChart: React.FC<Props> = memo(({ curve, markers }) => {
|
||||
))}
|
||||
|
||||
{chart.xLabels.map((l, i) => (
|
||||
<text key={i} x={l.x} y={H - 5} textAnchor="middle" fill="#6272a4" fontSize="8">
|
||||
<text key={i} x={l.x} y={H - 5} textAnchor="middle" className="btd-chart-label" fontSize="8">
|
||||
{l.label}
|
||||
</text>
|
||||
))}
|
||||
|
||||
@@ -1,44 +1,84 @@
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { createChart, CandlestickSeries, type IChartApi, type ISeriesApi, type Time, ColorType } from 'lightweight-charts';
|
||||
import type { Timeframe } from '../../types';
|
||||
import type { Theme, Timeframe } from '../../types';
|
||||
import { fetchUpbitCandles } from '../../utils/upbitApi';
|
||||
|
||||
const TF_OPTIONS: Timeframe[] = ['1m', '5m', '15m', '1h', '4h', '1D'];
|
||||
|
||||
interface Props {
|
||||
market: string;
|
||||
theme?: Theme;
|
||||
wsLabel?: string;
|
||||
}
|
||||
|
||||
const PaperMiniChart: React.FC<Props> = ({ market, wsLabel = 'WebSocket <100ms' }) => {
|
||||
function readChartTheme(container: HTMLElement | null) {
|
||||
const root = container?.closest('.app') ?? document.documentElement;
|
||||
const s = getComputedStyle(root);
|
||||
const v = (name: string, fallback: string) => s.getPropertyValue(name).trim() || fallback;
|
||||
return {
|
||||
bgColor: v('--bg3', '#1f2335'),
|
||||
textColor: v('--text', '#c0caf5'),
|
||||
gridColor: v('--sep', 'rgba(122, 162, 247, 0.15)'),
|
||||
borderColor: v('--border', 'rgba(122, 162, 247, 0.15)'),
|
||||
upColor: v('--up', '#ff6b6b'),
|
||||
downColor: v('--down', '#4dabf7'),
|
||||
};
|
||||
}
|
||||
|
||||
const PaperMiniChart: React.FC<Props> = ({ market, theme = 'dark', wsLabel = 'WebSocket <100ms' }) => {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const chartRef = useRef<IChartApi | null>(null);
|
||||
const seriesRef = useRef<ISeriesApi<'Candlestick'> | null>(null);
|
||||
const [timeframe, setTimeframe] = useState<Timeframe>('1h');
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
if (!containerRef.current) return undefined;
|
||||
const chart = createChart(containerRef.current, {
|
||||
const applyTheme = useCallback((chart: IChartApi, series: ISeriesApi<'Candlestick'>) => {
|
||||
const t = readChartTheme(containerRef.current);
|
||||
chart.applyOptions({
|
||||
layout: {
|
||||
background: { type: ColorType.Solid, color: '#121520' },
|
||||
textColor: '#9aa5ce',
|
||||
background: { type: ColorType.Solid, color: t.bgColor },
|
||||
textColor: t.textColor,
|
||||
},
|
||||
grid: {
|
||||
vertLines: { color: 'rgba(122,162,247,0.06)' },
|
||||
horzLines: { color: 'rgba(122,162,247,0.06)' },
|
||||
vertLines: { color: t.gridColor },
|
||||
horzLines: { color: t.gridColor },
|
||||
},
|
||||
rightPriceScale: { borderColor: 'rgba(122,162,247,0.12)' },
|
||||
timeScale: { borderColor: 'rgba(122,162,247,0.12)' },
|
||||
rightPriceScale: { borderColor: t.borderColor },
|
||||
timeScale: { borderColor: t.borderColor },
|
||||
});
|
||||
series.applyOptions({
|
||||
upColor: t.upColor,
|
||||
downColor: t.downColor,
|
||||
borderUpColor: t.upColor,
|
||||
borderDownColor: t.downColor,
|
||||
wickUpColor: t.upColor,
|
||||
wickDownColor: t.downColor,
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!containerRef.current) return undefined;
|
||||
const t = readChartTheme(containerRef.current);
|
||||
const chart = createChart(containerRef.current, {
|
||||
layout: {
|
||||
background: { type: ColorType.Solid, color: t.bgColor },
|
||||
textColor: t.textColor,
|
||||
},
|
||||
grid: {
|
||||
vertLines: { color: t.gridColor },
|
||||
horzLines: { color: t.gridColor },
|
||||
},
|
||||
rightPriceScale: { borderColor: t.borderColor },
|
||||
timeScale: { borderColor: t.borderColor },
|
||||
crosshair: { mode: 1 },
|
||||
});
|
||||
const series = chart.addSeries(CandlestickSeries, {
|
||||
upColor: '#22c55e',
|
||||
downColor: '#ef4444',
|
||||
borderUpColor: '#22c55e',
|
||||
borderDownColor: '#ef4444',
|
||||
wickUpColor: '#22c55e',
|
||||
wickDownColor: '#ef4444',
|
||||
upColor: t.upColor,
|
||||
downColor: t.downColor,
|
||||
borderUpColor: t.upColor,
|
||||
borderDownColor: t.downColor,
|
||||
wickUpColor: t.upColor,
|
||||
wickDownColor: t.downColor,
|
||||
});
|
||||
chartRef.current = chart;
|
||||
seriesRef.current = series;
|
||||
@@ -61,6 +101,12 @@ const PaperMiniChart: React.FC<Props> = ({ market, wsLabel = 'WebSocket <100ms'
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (chartRef.current && seriesRef.current) {
|
||||
applyTheme(chartRef.current, seriesRef.current);
|
||||
}
|
||||
}, [theme, applyTheme]);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
setLoading(true);
|
||||
|
||||
Reference in New Issue
Block a user