/** * 보조지표 설정 — 스타일 탭 (플롯·수평선 색상/굵기/타입) * 타이틀 열 너비를 섹션 내 최장 라벨에 맞춰 컨트롤 상하 정렬 */ import React, { useLayoutEffect, useRef } from 'react'; import type { PlotDef, HLineDef, HLineStyle } from '../utils/indicatorRegistry'; import { getHLineLabel } from '../utils/indicatorRegistry'; import { getPlotLabel } from '../utils/indicatorLabels'; import { getHlinePriceSpec } from '../utils/indicatorParamSpec'; import NumericParamInput from './NumericParamInput'; import { ColorInput } from './IndicatorSettingsSections'; import { LINE_WIDTH_OPTIONS, LINE_STYLE_OPTIONS, resolveHistogramUpColor, resolveHistogramDownColor, } from '../utils/plotColorUtils'; import { IchimokuCloudSection } from './IndicatorSettingsSections'; import { getIchimokuPlotTitle, type IchimokuCloudColors } from '../utils/ichimokuConfig'; /** 스타일 탭 선 유형 드롭다운 (업비트 표기) */ const LINE_STYLE_TAB_LABELS: Record = { solid: '라인', dashed: '점선', dotted: '도트', }; export interface IndicatorSettingsStyleSectionProps { indicatorType: string; plots: PlotDef[]; hlines: HLineDef[]; cloudColors?: IchimokuCloudColors; showIchimokuCloud?: boolean; onPlotStyle: (idx: number, patch: Partial) => void; onHLineStyle: (idx: number, patch: Partial) => void; onHLinePrice?: (idx: number, price: number) => void; onCloudColors?: (patch: Partial) => void; } function measureTitleColumn(sectionEl: HTMLElement | null, measureEl: HTMLElement | null) { if (!sectionEl || !measureEl) return; let maxW = 0; measureEl.querySelectorAll('.ism-plot-title').forEach(el => { maxW = Math.max(maxW, el.offsetWidth); }); if (maxW > 0) { sectionEl.style.setProperty('--ism-plot-title-width', `${Math.ceil(maxW)}px`); } } const PlotStyleRow: React.FC<{ label: string; plot: PlotDef; disabled?: boolean; onStyle: (patch: Partial) => void; }> = ({ label, plot, disabled, onStyle }) => { const isHistogram = plot.type === 'histogram'; const lineWidth = plot.lineWidth ?? 1; const lineStyle = plot.lineStyle ?? 'solid'; return (
{label}
색상 onStyle({ color: v })} />
굵기 {isHistogram ? ( ) : ( )}
타입 {isHistogram ? ( 히스토그램 ) : ( )}
); }; const HLineStyleRow: React.FC<{ indicatorType: string; label: string; hl: HLineDef; onStyle: (patch: Partial) => void; onPrice?: (price: number) => void; }> = ({ indicatorType, label, hl, onStyle, onPrice }) => { const isOn = hl.visible !== false; const lineWidth = hl.lineWidth ?? 1; const lineStyle = hl.lineStyle ?? 'dashed'; return (
{label}
{onPrice != null ? ( ) : ( {hl.price} )}
onStyle({ color: v })} />
); }; const IndicatorSettingsStyleSection: React.FC = ({ indicatorType, plots, hlines, cloudColors, showIchimokuCloud, onPlotStyle, onHLineStyle, onHLinePrice, onCloudColors, }) => { const sectionRef = useRef(null); const measureRef = useRef(null); const hlinePrices = hlines.map(h => h.price); const plotLabel = (p: PlotDef) => indicatorType === 'IchimokuCloud' ? getIchimokuPlotTitle(p.id) : getPlotLabel(p.title); const titleLabels = [ ...plots.map(plotLabel), ...hlines.map(h => h.label ?? getHLineLabel(h.price, hlinePrices)), ]; useLayoutEffect(() => { measureTitleColumn(sectionRef.current, measureRef.current); }, [titleLabels.join('\0'), plots.length, hlines.length]); return (
{titleLabels.map((t, i) => ( {t} ))}
{plots.map((plot, idx) => ( indicatorType === 'MACD' && plot.type === 'histogram' ? (
{plotLabel(plot)}
0선 위 (양수)
색상 onPlotStyle(idx, { histogramUpColor: v, color: v })} />
0선 아래 (음수)
색상 onPlotStyle(idx, { histogramDownColor: v })} />
) : ( onPlotStyle(idx, patch)} /> ) ))} {hlines.length > 0 && ( <>
이름 가격 색상 · 굵기 · 타입
{hlines.map((hl, idx) => ( onHLineStyle(idx, patch)} onPrice={onHLinePrice ? v => onHLinePrice(idx, v) : undefined} /> ))} )}
{showIchimokuCloud && cloudColors && onCloudColors && ( )}
); }; export default IndicatorSettingsStyleSection;