125 lines
4.0 KiB
TypeScript
125 lines
4.0 KiB
TypeScript
import React, { useState, useEffect, useCallback, useRef } from 'react';
|
|
import type { IndicatorConfig } from '../types';
|
|
import { getIndicatorDef, enrichIndicatorConfig } from '../utils/indicatorRegistry';
|
|
import IndicatorSettingsForm from './IndicatorSettingsForm';
|
|
import { resetConfigToDefaults } from '../utils/indicatorSettingsEditor';
|
|
import { useDraggablePanel } from '../hooks/useDraggablePanel';
|
|
|
|
interface IndicatorSettingsModalProps {
|
|
config: IndicatorConfig;
|
|
chartMarket?: string;
|
|
onSave: (updated: IndicatorConfig) => void;
|
|
onCancel: () => void;
|
|
}
|
|
|
|
/** 차트·설정 화면 공통 — 입력·스타일·가시성 단일 화면 (탭 없음) */
|
|
const IndicatorSettingsModal: React.FC<IndicatorSettingsModalProps> = ({
|
|
config,
|
|
chartMarket = '',
|
|
onSave,
|
|
onCancel,
|
|
}) => {
|
|
const def = getIndicatorDef(config.type);
|
|
const draftRef = useRef(enrichIndicatorConfig(config));
|
|
const [formRevision, setFormRevision] = useState(0);
|
|
const [chartHidden, setChartHidden] = useState(() => draftRef.current.hidden ?? false);
|
|
|
|
const {
|
|
panelRef,
|
|
dragging,
|
|
onHeaderPointerDown, headerTouchStyle,
|
|
panelStyle,
|
|
headerCursor,
|
|
} = useDraggablePanel({ centerOnMount: true });
|
|
|
|
const openedIdRef = React.useRef<string | null>(null);
|
|
useEffect(() => {
|
|
if (openedIdRef.current !== config.id) {
|
|
openedIdRef.current = config.id;
|
|
draftRef.current = enrichIndicatorConfig(config);
|
|
setChartHidden(draftRef.current.hidden ?? false);
|
|
setFormRevision(r => r + 1);
|
|
}
|
|
}, [config]);
|
|
|
|
const title = def
|
|
? `${def.koreanName} (${def.name})`
|
|
: config.type;
|
|
|
|
const handleSave = useCallback(() => {
|
|
onSave(enrichIndicatorConfig(draftRef.current));
|
|
}, [onSave]);
|
|
|
|
const handleDefaults = useCallback(() => {
|
|
draftRef.current = resetConfigToDefaults(draftRef.current);
|
|
setChartHidden(draftRef.current.hidden ?? false);
|
|
setFormRevision(r => r + 1);
|
|
}, []);
|
|
|
|
const handleHidden = useCallback((showOnChart: boolean) => {
|
|
draftRef.current = { ...draftRef.current, hidden: !showOnChart };
|
|
setChartHidden(!showOnChart);
|
|
}, []);
|
|
|
|
const handleDraftRef = useCallback((updated: IndicatorConfig) => {
|
|
draftRef.current = updated;
|
|
}, []);
|
|
|
|
return (
|
|
<div
|
|
className="ism-overlay"
|
|
onMouseDown={e => { if (e.target === e.currentTarget) onCancel(); }}
|
|
>
|
|
<div
|
|
ref={panelRef}
|
|
className="ism-dialog ism-dialog-unified"
|
|
style={{
|
|
...panelStyle,
|
|
zIndex: 5001,
|
|
cursor: dragging ? 'grabbing' : undefined,
|
|
}}
|
|
onMouseDown={e => e.stopPropagation()}
|
|
onPointerDown={e => e.stopPropagation()}
|
|
>
|
|
<div
|
|
className="gc-popup-header"
|
|
onPointerDown={onHeaderPointerDown}
|
|
style={{ cursor: headerCursor, ...headerTouchStyle }}
|
|
>
|
|
<span className="gc-popup-title">{title}</span>
|
|
<button type="button" className="gc-popup-close" onClick={onCancel}>✕</button>
|
|
</div>
|
|
|
|
<div
|
|
className="ism-body ism-body-unified"
|
|
onMouseDown={e => e.stopPropagation()}
|
|
onPointerDown={e => e.stopPropagation()}
|
|
>
|
|
<IndicatorSettingsForm
|
|
key={`${config.id}-${formRevision}`}
|
|
config={draftRef.current}
|
|
chartMarket={chartMarket}
|
|
variant="modal"
|
|
deferParentSync
|
|
chartHidden={chartHidden}
|
|
onChartHiddenChange={handleHidden}
|
|
onChange={handleDraftRef}
|
|
/>
|
|
</div>
|
|
|
|
<div className="ism-footer">
|
|
<button type="button" className="ism-btn-defaults" onClick={handleDefaults}>
|
|
기본값 <span style={{ opacity: 0.6, fontSize: 11 }}>▾</span>
|
|
</button>
|
|
<div className="ism-footer-right">
|
|
<button type="button" className="ism-btn-cancel" onClick={onCancel}>취소</button>
|
|
<button type="button" className="ism-btn-ok" onClick={handleSave}>확인</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default IndicatorSettingsModal;
|