custom 툴발 적용

This commit is contained in:
Macbook
2026-06-03 01:09:06 +09:00
parent 76e5b12d19
commit 88fe92d8c3
8 changed files with 1110 additions and 22 deletions
+101
View File
@@ -51,6 +51,15 @@ import {
DEFAULT_CHART_OVERLAY_VISIBILITY,
listChartOverlayToggleItems,
} from '../utils/chartOverlayVisibility';
import {
type ChartCustomOverlaySelection,
type ChartCustomOverlaySlotId,
createDefaultChartCustomOverlaySelection,
isCustomOverlaySlotActive,
resolveActiveCustomOverlaySlot,
syncChartManagerCustomOverlaysByActiveSlot,
} from '../utils/chartCustomOverlay';
import ChartCustomOverlaySettingsModal from './ChartCustomOverlaySettingsModal';
import { saveSlot } from '../utils/backendApi';
import type {
OHLCVBar, ChartType, Theme, Timeframe,
@@ -672,6 +681,73 @@ const ChartSlot = forwardRef<ChartSlotHandle, ChartSlotProps>(function ChartSlot
[overlayVisibility],
);
const [customOverlaySelection, setCustomOverlaySelection] = useState<ChartCustomOverlaySelection>(
() => createDefaultChartCustomOverlaySelection(),
);
const [custom1OverlaySelection, setCustom1OverlaySelection] = useState<ChartCustomOverlaySelection>(
() => createDefaultChartCustomOverlaySelection(),
);
const [activeCustomOverlaySlot, setActiveCustomOverlaySlot] = useState<ChartCustomOverlaySlotId | null>(null);
const [customOverlaySettingsSlot, setCustomOverlaySettingsSlot] = useState<ChartCustomOverlaySlotId | null>(null);
const customOverlayActive = isCustomOverlaySlotActive(activeCustomOverlaySlot, 'custom');
const custom1OverlayActive = isCustomOverlaySlotActive(activeCustomOverlaySlot, 'custom1');
const syncCustomOverlaysToManager = useCallback((mgr: ChartManager) => {
syncChartManagerCustomOverlaysByActiveSlot(
mgr,
activeCustomOverlaySlot,
customOverlaySelection,
custom1OverlaySelection,
);
}, [activeCustomOverlaySlot, customOverlaySelection, custom1OverlaySelection]);
const handleCustomOverlayActiveChange = useCallback((active: boolean) => {
const nextSlot = resolveActiveCustomOverlaySlot('custom', active, activeCustomOverlaySlot);
setActiveCustomOverlaySlot(nextSlot);
const mgr = managerRef.current;
if (mgr) {
syncChartManagerCustomOverlaysByActiveSlot(
mgr,
nextSlot,
customOverlaySelection,
custom1OverlaySelection,
);
}
}, [activeCustomOverlaySlot, customOverlaySelection, custom1OverlaySelection]);
const handleCustom1OverlayActiveChange = useCallback((active: boolean) => {
const nextSlot = resolveActiveCustomOverlaySlot('custom1', active, activeCustomOverlaySlot);
setActiveCustomOverlaySlot(nextSlot);
const mgr = managerRef.current;
if (mgr) {
syncChartManagerCustomOverlaysByActiveSlot(
mgr,
nextSlot,
customOverlaySelection,
custom1OverlaySelection,
);
}
}, [activeCustomOverlaySlot, customOverlaySelection, custom1OverlaySelection]);
const handleSaveCustomOverlaySelection = useCallback((
slot: ChartCustomOverlaySlotId,
selection: ChartCustomOverlaySelection,
) => {
const nextCustomSel = slot === 'custom' ? selection : customOverlaySelection;
const nextCustom1Sel = slot === 'custom1' ? selection : custom1OverlaySelection;
if (slot === 'custom') setCustomOverlaySelection(selection);
else setCustom1OverlaySelection(selection);
setCustomOverlaySettingsSlot(null);
const mgr = managerRef.current;
if (!mgr) return;
syncChartManagerCustomOverlaysByActiveSlot(
mgr,
activeCustomOverlaySlot,
nextCustomSel,
nextCustom1Sel,
);
}, [activeCustomOverlaySlot, customOverlaySelection, custom1OverlaySelection]);
const handleToggleCandleOverlay = useCallback((key: ChartOverlayToggleKey) => {
setOverlayVisibility(prev => {
const next = { ...prev, [key]: !prev[key] };
@@ -937,6 +1013,7 @@ const ChartSlot = forwardRef<ChartSlotHandle, ChartSlotProps>(function ChartSlot
mgr.setVolumeVisible(chartVolumeVisible);
if (chartPaneSeparator) mgr.setPaneSeparatorOptions(chartPaneSeparator);
mgr.setChartOverlayVisibility(overlayVisibilityRef.current);
syncCustomOverlaysToManager(mgr);
// Time sync: 가시 시간 범위 변화 구독
mgr.subscribeVisibleTimeRange(r => {
if (!r || isSyncingTimeRef.current) return;
@@ -962,6 +1039,7 @@ const ChartSlot = forwardRef<ChartSlotHandle, ChartSlotProps>(function ChartSlot
const mgr = managerRef.current;
if (!mgr) return;
mgr.setChartOverlayVisibility(overlayVisibilityRef.current);
syncCustomOverlaysToManager(mgr);
if (skipSyncApplyRef.current) {
skipSyncApplyRef.current = false;
const fb = mgr.hasIchimoku() ? 28 : 0;
@@ -1016,8 +1094,31 @@ const ChartSlot = forwardRef<ChartSlotHandle, ChartSlotProps>(function ChartSlot
onFullView={onFullView ? () => onFullView(symbolRef.current) : undefined}
candleOverlayToggles={compactMode ? undefined : candleOverlayToggles}
onToggleCandleOverlay={compactMode ? undefined : handleToggleCandleOverlay}
customOverlayActive={compactMode ? undefined : customOverlayActive}
onCustomOverlayActiveChange={compactMode ? undefined : handleCustomOverlayActiveChange}
onOpenCustomOverlaySettings={compactMode ? undefined : () => setCustomOverlaySettingsSlot('custom')}
custom1OverlayActive={compactMode ? undefined : custom1OverlayActive}
onCustom1OverlayActiveChange={compactMode ? undefined : handleCustom1OverlayActiveChange}
onOpenCustom1OverlaySettings={compactMode ? undefined : () => setCustomOverlaySettingsSlot('custom1')}
/>
{customOverlaySettingsSlot === 'custom' && !compactMode && (
<ChartCustomOverlaySettingsModal
title="Custom 표시 설정"
selection={customOverlaySelection}
onSave={sel => handleSaveCustomOverlaySelection('custom', sel)}
onCancel={() => setCustomOverlaySettingsSlot(null)}
/>
)}
{customOverlaySettingsSlot === 'custom1' && !compactMode && (
<ChartCustomOverlaySettingsModal
title="Custom1 표시 설정"
selection={custom1OverlaySelection}
onSave={sel => handleSaveCustomOverlaySelection('custom1', sel)}
onCancel={() => setCustomOverlaySettingsSlot(null)}
/>
)}
{/* 컨텍스트 툴바 */}
{ctxToolbar && (() => {
const cfg = ctxToolbar.entryId !== '__main__'