custom 툴발 적용
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
* pane 하단: 드래그 핸들 + 펼침메뉴(분리·복사·전체보기·종료)
|
||||
*/
|
||||
import React, { useState, useEffect, useCallback, useRef } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import type { IndicatorConfig } from '../types';
|
||||
import { ChartManager } from '../utils/ChartManager';
|
||||
import { getPaneHostId } from '../utils/indicatorPaneMerge';
|
||||
@@ -35,6 +36,13 @@ export interface ChartRightToolbarProps {
|
||||
onToggleHidden?: (id: string) => void;
|
||||
/** 지표명 툴바 — 설정 모달 */
|
||||
onSettings?: (id: string) => void;
|
||||
/** Custom / Custom1 오버레이 — 적용 여부 (동시 적용 불가, 설정은 슬롯별 분리) */
|
||||
customOverlayActive?: boolean;
|
||||
onCustomOverlayActiveChange?: (active: boolean) => void;
|
||||
onOpenCustomOverlaySettings?: () => void;
|
||||
custom1OverlayActive?: boolean;
|
||||
onCustom1OverlayActiveChange?: (active: boolean) => void;
|
||||
onOpenCustom1OverlaySettings?: () => void;
|
||||
}
|
||||
|
||||
const BTN_SIZE = 32;
|
||||
@@ -159,6 +167,24 @@ const IconEye = ({ hidden }: { hidden?: boolean }) => hidden ? (
|
||||
</svg>
|
||||
);
|
||||
|
||||
const CUSTOM_MENU_ID = '__custom_overlay__';
|
||||
const CUSTOM1_MENU_ID = '__custom1_overlay__';
|
||||
|
||||
const IconCustom = () => (
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"
|
||||
stroke="currentColor" strokeWidth="1.5" strokeLinecap="round">
|
||||
<path d="M2 4h10M2 7h10M2 10h6"/>
|
||||
<circle cx="10.5" cy="10" r="1.2" fill="currentColor" stroke="none"/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
const IconCheck = () => (
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"
|
||||
stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<polyline points="2,7.5 5.5,11 12,3"/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
const IconGear = () => (
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" stroke="currentColor" strokeWidth="1.4">
|
||||
<circle cx="7" cy="7" r="2.3"/>
|
||||
@@ -166,6 +192,62 @@ const IconGear = () => (
|
||||
</svg>
|
||||
);
|
||||
|
||||
const CustomOverlayFlyoutMenu: React.FC<{
|
||||
title: string;
|
||||
flyoutPos: { top: number; left: number };
|
||||
overlayActive: boolean;
|
||||
onToggleActive: () => void;
|
||||
onOpenSettings: () => void;
|
||||
onCancelClose: () => void;
|
||||
onScheduleClose: () => void;
|
||||
onCloseFlyout: () => void;
|
||||
}> = ({
|
||||
title,
|
||||
flyoutPos,
|
||||
overlayActive,
|
||||
onToggleActive,
|
||||
onOpenSettings,
|
||||
onCancelClose,
|
||||
onScheduleClose,
|
||||
onCloseFlyout,
|
||||
}) => createPortal(
|
||||
<div
|
||||
className="tv-side-flyout tv-side-flyout--right-anchor tv-side-flyout--custom-overlay"
|
||||
style={{ top: flyoutPos.top, left: flyoutPos.left }}
|
||||
onMouseEnter={onCancelClose}
|
||||
onMouseLeave={onScheduleClose}
|
||||
>
|
||||
<div className="tv-side-flyout-header">{title}</div>
|
||||
<button
|
||||
type="button"
|
||||
className={`tv-side-flyout-item${overlayActive ? ' active' : ''}`}
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
onToggleActive();
|
||||
onCloseFlyout();
|
||||
}}
|
||||
>
|
||||
<span className="tv-side-flyout-icon"><IconCheck /></span>
|
||||
<span className="tv-side-flyout-title">
|
||||
{overlayActive ? '미적용' : '적용'}
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="tv-side-flyout-item"
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
onOpenSettings();
|
||||
onCloseFlyout();
|
||||
}}
|
||||
>
|
||||
<span className="tv-side-flyout-icon"><IconGear /></span>
|
||||
<span className="tv-side-flyout-title">설정</span>
|
||||
</button>
|
||||
</div>,
|
||||
document.body,
|
||||
);
|
||||
|
||||
interface MenuItemDef {
|
||||
key: string;
|
||||
title: string;
|
||||
@@ -266,6 +348,12 @@ const ChartRightToolbar: React.FC<ChartRightToolbarProps> = ({
|
||||
manager, indicators, chartHeight, paused = false,
|
||||
candleOverlayToggles,
|
||||
onToggleCandleOverlay,
|
||||
customOverlayActive = false,
|
||||
onCustomOverlayActiveChange,
|
||||
onOpenCustomOverlaySettings,
|
||||
custom1OverlayActive = false,
|
||||
onCustom1OverlayActiveChange,
|
||||
onOpenCustom1OverlaySettings,
|
||||
onSplit, onRemove, onDuplicate,
|
||||
onExpand, onRestore, focusedId,
|
||||
onDragStart, onToggleHidden, onSettings,
|
||||
@@ -341,6 +429,10 @@ const ChartRightToolbar: React.FC<ChartRightToolbarProps> = ({
|
||||
|
||||
const candlePane = paneLayouts.find(l => l.paneIndex === 0);
|
||||
const showOverlayIcons = !!(candleOverlayToggles?.length && candlePane && onToggleCandleOverlay);
|
||||
const showCustomBtn = !!(showOverlayIcons && onCustomOverlayActiveChange && onOpenCustomOverlaySettings);
|
||||
const showCustom1Btn = !!(showOverlayIcons && onCustom1OverlayActiveChange && onOpenCustom1OverlaySettings);
|
||||
const customMenuOpen = openMenuId === CUSTOM_MENU_ID;
|
||||
const custom1MenuOpen = openMenuId === CUSTOM1_MENU_ID;
|
||||
|
||||
const focusedLayoutId = focusedId
|
||||
? resolveFocusedPaneLayoutId(focusedId, indicators)
|
||||
@@ -395,6 +487,33 @@ const ChartRightToolbar: React.FC<ChartRightToolbarProps> = ({
|
||||
{OVERLAY_ICONS[item.key]}
|
||||
</button>
|
||||
))}
|
||||
{showCustomBtn && (
|
||||
<button
|
||||
type="button"
|
||||
className={`tv-side-btn tv-side-group-btn chart-right-toolbar-btn chart-right-toolbar-btn--custom${customOverlayActive ? ' active' : ''}${customMenuOpen ? ' active' : ''}`}
|
||||
title="Custom 표시"
|
||||
style={{ marginTop: BTN_GAP }}
|
||||
onMouseEnter={e => openFlyout(CUSTOM_MENU_ID, e.currentTarget)}
|
||||
onMouseLeave={scheduleClose}
|
||||
>
|
||||
<IconCustom />
|
||||
<span className="tv-side-expand-arrow" />
|
||||
</button>
|
||||
)}
|
||||
{showCustom1Btn && (
|
||||
<button
|
||||
type="button"
|
||||
className={`tv-side-btn tv-side-group-btn chart-right-toolbar-btn chart-right-toolbar-btn--custom1${custom1OverlayActive ? ' active' : ''}${custom1MenuOpen ? ' active' : ''}`}
|
||||
title="Custom1 표시"
|
||||
style={{ marginTop: BTN_GAP }}
|
||||
onMouseEnter={e => openFlyout(CUSTOM1_MENU_ID, e.currentTarget)}
|
||||
onMouseLeave={scheduleClose}
|
||||
>
|
||||
<IconCustom />
|
||||
<span className="chart-right-toolbar-custom1-badge">1</span>
|
||||
<span className="tv-side-expand-arrow" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -457,7 +576,33 @@ const ChartRightToolbar: React.FC<ChartRightToolbarProps> = ({
|
||||
})}
|
||||
</div>
|
||||
|
||||
{openItem && openMenuId && (() => {
|
||||
{customMenuOpen && showCustomBtn && (
|
||||
<CustomOverlayFlyoutMenu
|
||||
title="Custom"
|
||||
flyoutPos={flyoutPos}
|
||||
overlayActive={customOverlayActive}
|
||||
onToggleActive={() => onCustomOverlayActiveChange?.(!customOverlayActive)}
|
||||
onOpenSettings={() => onOpenCustomOverlaySettings?.()}
|
||||
onCancelClose={cancelClose}
|
||||
onScheduleClose={scheduleClose}
|
||||
onCloseFlyout={closeFlyout}
|
||||
/>
|
||||
)}
|
||||
|
||||
{custom1MenuOpen && showCustom1Btn && (
|
||||
<CustomOverlayFlyoutMenu
|
||||
title="Custom1"
|
||||
flyoutPos={flyoutPos}
|
||||
overlayActive={custom1OverlayActive}
|
||||
onToggleActive={() => onCustom1OverlayActiveChange?.(!custom1OverlayActive)}
|
||||
onOpenSettings={() => onOpenCustom1OverlaySettings?.()}
|
||||
onCancelClose={cancelClose}
|
||||
onScheduleClose={scheduleClose}
|
||||
onCloseFlyout={closeFlyout}
|
||||
/>
|
||||
)}
|
||||
|
||||
{openItem && openMenuId && openMenuId !== CUSTOM_MENU_ID && openMenuId !== CUSTOM1_MENU_ID && (() => {
|
||||
const menuItems = buildMenuItems(openItem, indicators, paneItems, {
|
||||
focusedId, onSplit, onDuplicate, onExpand, onRestore, onRemove,
|
||||
onToggleHidden, onSettings,
|
||||
|
||||
Reference in New Issue
Block a user