매매 시그널 알림 수정
This commit is contained in:
+29
-16
@@ -35,6 +35,28 @@
|
||||
--accent-muted: color-mix(in srgb, var(--accent) 22%, transparent);
|
||||
--popup-shadow: 0 8px 32px rgba(0, 0, 0, 0.55);
|
||||
--overlay-scrim: rgba(0, 0, 0, 0.55);
|
||||
|
||||
/* 실시간 수신 형광 연두 — 가상매매·알림·차트 슬롯 공통 (얇은 외곽) */
|
||||
--gc-receive-border: #69f0ae;
|
||||
--gc-receive-shadow:
|
||||
0 0 0 1px color-mix(in srgb, #69f0ae 92%, transparent),
|
||||
0 0 6px 2px #69f0ae50,
|
||||
0 0 14px 4px #b9f6ca26;
|
||||
--gc-receive-shadow-inset:
|
||||
0 0 0 1px color-mix(in srgb, #69f0ae 92%, transparent),
|
||||
0 0 6px 2px #69f0ae50,
|
||||
0 0 14px 4px #b9f6ca26,
|
||||
inset 0 0 28px color-mix(in srgb, #b9f6ca 10%, transparent);
|
||||
--gc-receive-shadow-inset-light:
|
||||
0 0 0 1px color-mix(in srgb, #69f0ae 92%, transparent),
|
||||
0 0 6px 2px #69f0ae50,
|
||||
0 0 14px 4px #b9f6ca26,
|
||||
inset 0 0 20px color-mix(in srgb, #b9f6ca 8%, transparent);
|
||||
--gc-receive-panel-inset: inset 0 0 16px color-mix(in srgb, #b9f6ca 6%, transparent);
|
||||
--gc-receive-dot-shadow:
|
||||
0 0 4px 2px #69f0ae,
|
||||
0 0 10px 4px #69f0ae55,
|
||||
0 0 16px 6px #b9f6ca1a;
|
||||
}
|
||||
|
||||
/* ── Dark Theme: 도쿄 나이트 (THEME_SETTINGS.md §2.2) ───────────── */
|
||||
@@ -537,11 +559,8 @@ html.theme-blue {
|
||||
.ws-dot.receiving {
|
||||
animation: none !important; /* idle 애니메이션 일시 중단 */
|
||||
background: #b9f6ca !important; /* 밝은 연두 */
|
||||
transform: scale(1.4);
|
||||
box-shadow:
|
||||
0 0 6px 3px #69f0ae, /* 내부 glow */
|
||||
0 0 14px 6px #69f0ae66, /* 외부 glow */
|
||||
0 0 24px 10px #b9f6ca22; /* 넓은 음영 */
|
||||
transform: scale(1.35);
|
||||
box-shadow: var(--gc-receive-dot-shadow);
|
||||
}
|
||||
|
||||
@keyframes ws-idle-lime {
|
||||
@@ -4634,12 +4653,8 @@ html.theme-blue {
|
||||
/* 실시간 수신 — 가상투자 vtd-card--receiving 와 동일 형광 연두 */
|
||||
.multi-slot-wrap:has(.chart-slot--receiving),
|
||||
.chart-slot--compact.chart-slot--receiving {
|
||||
border-color: #69f0ae !important;
|
||||
box-shadow:
|
||||
0 0 0 1px #69f0ae,
|
||||
0 0 10px 4px #69f0ae66,
|
||||
0 0 22px 8px #b9f6ca33,
|
||||
inset 0 0 48px color-mix(in srgb, #b9f6ca 14%, transparent);
|
||||
border-color: var(--gc-receive-border) !important;
|
||||
box-shadow: var(--gc-receive-shadow-inset);
|
||||
transition: box-shadow 0.15s ease-out, border-color 0.15s ease-out;
|
||||
}
|
||||
|
||||
@@ -4654,14 +4669,12 @@ html.theme-blue {
|
||||
|
||||
.chart-slot--compact.chart-slot--receiving.active {
|
||||
box-shadow:
|
||||
0 0 0 1px #69f0ae,
|
||||
0 0 10px 4px #69f0ae66,
|
||||
0 0 22px 8px #b9f6ca33,
|
||||
inset 0 0 0 1px color-mix(in srgb, #69f0ae 55%, var(--accent, #2962ff));
|
||||
var(--gc-receive-shadow),
|
||||
inset 0 0 0 1px color-mix(in srgb, #69f0ae 50%, var(--accent, #2962ff));
|
||||
}
|
||||
|
||||
.chart-slot--compact.chart-slot--receiving .slot-chart-wrap--compact {
|
||||
box-shadow: inset 0 0 24px color-mix(in srgb, #b9f6ca 8%, transparent);
|
||||
box-shadow: var(--gc-receive-panel-inset);
|
||||
}
|
||||
|
||||
.slot-header--compact {
|
||||
|
||||
@@ -80,7 +80,7 @@ const TradeNotificationSignalSummary: React.FC<Props> = ({
|
||||
headVariant={embedded ? 'inline-quote' : 'default'}
|
||||
theme={theme}
|
||||
ticker={ticker}
|
||||
chartLiveReceiveHighlight
|
||||
chartLiveReceiveHighlight={!embedded}
|
||||
readOnlyStrategyLabel={strategyLabel}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -58,7 +58,10 @@ const TradeSignalMiniChart: React.FC<Props> = ({
|
||||
pendingBarRef.current = null;
|
||||
mgr.updateBar(pending);
|
||||
const wrap = canvasWrapRef.current;
|
||||
if (wrap && wrap.clientHeight > 0) mgr.resetPaneHeights(wrap.clientHeight);
|
||||
if (wrap && wrap.clientWidth > 0 && wrap.clientHeight > 0) {
|
||||
mgr.resetPaneHeights(wrap.clientHeight);
|
||||
mgr.resize(wrap.clientWidth, wrap.clientHeight);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const applyRealtimeBar = useCallback((bar: OHLCVBar, append: boolean) => {
|
||||
@@ -104,11 +107,15 @@ const TradeSignalMiniChart: React.FC<Props> = ({
|
||||
barsMarketRef.current = barsMarket;
|
||||
const barsReady = bars.length >= 2 && barsMarket === market;
|
||||
|
||||
const applyPaneHeights = useCallback(() => {
|
||||
const syncChartLayout = useCallback(() => {
|
||||
const wrap = canvasWrapRef.current;
|
||||
const mgr = managerRef.current;
|
||||
if (!wrap || wrap.clientHeight <= 0 || !mgr?.hasMainSeries()) return;
|
||||
mgr.resetPaneHeights(wrap.clientHeight);
|
||||
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(() => {
|
||||
@@ -127,8 +134,12 @@ const TradeSignalMiniChart: React.FC<Props> = ({
|
||||
if (!canvasWrapRef.current) return;
|
||||
const h = canvasWrapRef.current.clientHeight;
|
||||
if (h <= 0) return;
|
||||
const w = canvasWrapRef.current.clientWidth;
|
||||
if (managerRef.current?.hasMainSeries()) {
|
||||
if (w > 0) {
|
||||
managerRef.current.resetPaneHeights(h);
|
||||
managerRef.current.resize(w, h);
|
||||
}
|
||||
} else if (!chartReloadTriggeredRef.current && delay >= 600 && barsReady) {
|
||||
chartReloadTriggeredRef.current = true;
|
||||
setChartReloadTick(t => t + 1);
|
||||
@@ -136,13 +147,13 @@ const TradeSignalMiniChart: React.FC<Props> = ({
|
||||
}, delay),
|
||||
);
|
||||
|
||||
const ro = new ResizeObserver(() => applyPaneHeights());
|
||||
const ro = new ResizeObserver(() => syncChartLayout());
|
||||
ro.observe(wrap);
|
||||
return () => {
|
||||
timers.forEach(clearTimeout);
|
||||
ro.disconnect();
|
||||
};
|
||||
}, [market, timeframe, enabled, barsReady, applyPaneHeights, fillHeight]);
|
||||
}, [market, timeframe, enabled, barsReady, syncChartLayout, fillHeight]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!enabled || !barsReady) return;
|
||||
@@ -154,9 +165,9 @@ const TradeSignalMiniChart: React.FC<Props> = ({
|
||||
|
||||
useEffect(() => {
|
||||
if (!enabled) return;
|
||||
const t = setTimeout(() => applyPaneHeights(), 80);
|
||||
const t = setTimeout(() => syncChartLayout(), 80);
|
||||
return () => clearTimeout(t);
|
||||
}, [fillHeight, enabled, applyPaneHeights]);
|
||||
}, [fillHeight, enabled, syncChartLayout]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!enabled || !useUpbit) return;
|
||||
|
||||
@@ -2,11 +2,8 @@
|
||||
|
||||
/* 실시간 WS/STOMP 수신 — 목록 행(.tnl-row) 외곽 아웃라인만 형광 연두 (내부 inset 없음) */
|
||||
.tnl-row.tnl-row--receiving {
|
||||
border-color: #69f0ae !important;
|
||||
box-shadow:
|
||||
0 0 0 1px #69f0ae,
|
||||
0 0 10px 4px #69f0ae66,
|
||||
0 0 22px 8px #b9f6ca33;
|
||||
border-color: var(--gc-receive-border) !important;
|
||||
box-shadow: var(--gc-receive-shadow);
|
||||
transition: box-shadow 0.15s ease-out, border-color 0.15s ease-out;
|
||||
}
|
||||
|
||||
@@ -36,6 +33,20 @@
|
||||
box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent, #7aa2f7) 35%, transparent);
|
||||
}
|
||||
|
||||
/* 수신 시 신호 상세(vtd-card) — 연두 테두리·inset 제거 (목록 행 외곽만 유지) */
|
||||
.tnl-row.tnl-row--receiving .tnl-signal-slot .vtd-card--receiving,
|
||||
.tnl-row.tnl-row--receiving .tnl-signal-slot .vtd-card.vtd-card--live {
|
||||
border-color: color-mix(in srgb, var(--accent, #3f7ef5) 40%, var(--se-border, var(--border))) !important;
|
||||
box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent, #3f7ef5) 12%, transparent) !important;
|
||||
background: var(--se-panel-card-bg, var(--bg2)) !important;
|
||||
}
|
||||
|
||||
.tnl-row.tnl-row--receiving .tnl-signal-slot .vtd-card--receiving .vtd-sig-panel,
|
||||
.tnl-row.tnl-row--receiving .tnl-signal-slot .vtd-card--receiving .vtd-card-chart-panel,
|
||||
.tnl-row.tnl-row--receiving .tnl-signal-slot .vtd-sig-panel-wrap--receiving .vtd-sig-panel {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.tnl-row--gallery {
|
||||
display: block;
|
||||
width: 100%;
|
||||
@@ -206,12 +217,12 @@
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* 신호상세 — 가상매매 vtd-card 슬롯 (이퀄라이저·히트맵·신호등 최소 너비 확보) */
|
||||
/* 신호상세 — 가상매매 vtd-card 슬롯 (차트보다 좁게 · 나머지는 캔들·지표) */
|
||||
.tnl-signal-slot {
|
||||
flex: 1.35 1 300px;
|
||||
flex: 1.05 1 272px;
|
||||
width: auto;
|
||||
min-width: min(100%, 300px);
|
||||
max-width: 42%;
|
||||
min-width: min(100%, 272px);
|
||||
max-width: 34%;
|
||||
height: 280px;
|
||||
max-height: 280px;
|
||||
display: flex;
|
||||
@@ -313,8 +324,9 @@
|
||||
}
|
||||
|
||||
.tnl-charts-track > .tnl-signal-slot {
|
||||
flex: 1.35 1 300px;
|
||||
min-width: min(100%, 300px);
|
||||
flex: 1.05 1 272px;
|
||||
min-width: min(100%, 272px);
|
||||
max-width: 34%;
|
||||
}
|
||||
|
||||
/* 가상매매 투자대상 카드(vtd)와 동일한 3단 구성 */
|
||||
@@ -622,7 +634,7 @@
|
||||
}
|
||||
|
||||
.tnl-charts-track > .tnl-chart-card {
|
||||
flex: 1 1 0;
|
||||
flex: 1.12 1 0;
|
||||
width: 0;
|
||||
min-width: 0;
|
||||
max-width: none;
|
||||
@@ -685,13 +697,16 @@
|
||||
|
||||
.tnl-mini-chart-canvas {
|
||||
position: relative;
|
||||
flex: 0 0 236px;
|
||||
height: 236px;
|
||||
max-height: 236px;
|
||||
flex: 1 1 auto;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
height: 100%;
|
||||
max-height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.tnl-mini-chart-canvas--fill {
|
||||
@@ -721,14 +736,17 @@
|
||||
}
|
||||
|
||||
.tnl-mini-chart-placeholder {
|
||||
flex: 0 0 236px;
|
||||
height: 236px;
|
||||
max-height: 236px;
|
||||
flex: 1 1 auto;
|
||||
width: 100%;
|
||||
min-height: 120px;
|
||||
height: 100%;
|
||||
max-height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 11px;
|
||||
color: var(--text3);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.tnl-mini-chart-placeholder--fill {
|
||||
|
||||
@@ -720,11 +720,8 @@
|
||||
}
|
||||
|
||||
.vtd-focus-wrap--receiving {
|
||||
border-color: #69f0ae !important;
|
||||
box-shadow:
|
||||
0 0 0 1px #69f0ae,
|
||||
0 0 10px 4px #69f0ae66,
|
||||
inset 0 0 32px color-mix(in srgb, #b9f6ca 12%, transparent);
|
||||
border-color: var(--gc-receive-border) !important;
|
||||
box-shadow: var(--gc-receive-shadow-inset-light);
|
||||
}
|
||||
|
||||
.vtd-focus-head {
|
||||
@@ -917,7 +914,7 @@
|
||||
}
|
||||
|
||||
.vtd-sig-panel-wrap--receiving .vtd-sig-panel {
|
||||
box-shadow: inset 0 0 24px color-mix(in srgb, #b9f6ca 8%, transparent);
|
||||
box-shadow: var(--gc-receive-panel-inset);
|
||||
}
|
||||
|
||||
.vtd-focus-sig-meta {
|
||||
@@ -1211,12 +1208,8 @@
|
||||
|
||||
/* 실시간 데이터 수신 — 실시간 차트 ws-dot.receiving 과 동일 형광 연두 음영 */
|
||||
.vtd-card--receiving {
|
||||
border-color: #69f0ae !important;
|
||||
box-shadow:
|
||||
0 0 0 1px #69f0ae,
|
||||
0 0 10px 4px #69f0ae66,
|
||||
0 0 22px 8px #b9f6ca33,
|
||||
inset 0 0 48px color-mix(in srgb, #b9f6ca 14%, transparent);
|
||||
border-color: var(--gc-receive-border) !important;
|
||||
box-shadow: var(--gc-receive-shadow-inset);
|
||||
background: linear-gradient(
|
||||
165deg,
|
||||
color-mix(in srgb, #b9f6ca 18%, var(--vtd-sig-card-gradient)) 0%,
|
||||
@@ -1228,7 +1221,7 @@
|
||||
|
||||
.vtd-card--receiving .vtd-sig-panel,
|
||||
.vtd-card--receiving .vtd-card-chart-panel {
|
||||
box-shadow: inset 0 0 24px color-mix(in srgb, #b9f6ca 8%, transparent);
|
||||
box-shadow: var(--gc-receive-panel-inset);
|
||||
}
|
||||
|
||||
.vtd-card-head {
|
||||
@@ -1535,11 +1528,8 @@
|
||||
.vtd-live-badge-dot.receiving {
|
||||
animation: none !important;
|
||||
background: #b9f6ca !important;
|
||||
transform: scale(1.35);
|
||||
box-shadow:
|
||||
0 0 6px 3px #69f0ae,
|
||||
0 0 14px 6px #69f0ae66,
|
||||
0 0 24px 10px #b9f6ca22;
|
||||
transform: scale(1.3);
|
||||
box-shadow: var(--gc-receive-dot-shadow);
|
||||
}
|
||||
|
||||
.vtd-live-badge--receiving {
|
||||
|
||||
Reference in New Issue
Block a user