import React, { useCallback, useMemo, useState } from 'react'; import PaletteChip from './PaletteChip'; import PaletteItemModal from './PaletteItemModal'; import type { DefType } from '../../utils/strategyEditorShared'; import { getCompositeDefaultPeriods } from '../../utils/compositeIndicators'; import { isStochOverboughtPairPaletteValue } from '../../utils/stochOverboughtPair'; import { isPriceExtremeBreakoutPairPaletteValue } from '../../utils/priceExtremeBreakoutPair'; import { isInflection33PaletteValue } from '../../utils/inflection33Strategy'; import { isStableStrategyPaletteValue, STABLE_STRATEGY_PALETTE_ITEMS } from '../../utils/stableStrategyPairs'; import { getDefaultIndicatorPeriod } from '../../utils/conditionPeriods'; import { getStrategyIndicatorDisplayName } from '../../utils/strategyPaletteStorage'; import { getIndicatorPeriodLabel } from '../../utils/strategyFlowLayout'; import { createPaletteItemId, savePaletteItems, type PaletteItem, type PaletteItemKind, } from '../../utils/strategyPaletteStorage'; /** 팔레트 카드 기간 표시 — 항상 보조지표 설정(DEF) 기준 */ function periodLabel(item: PaletteItem, def: DefType): string { if (item.kind === 'composite' && isStochOverboughtPairPaletteValue(item.value)) { const sec = item.secondaryIndicator ?? 'CCI'; return `Stoch + ${getStrategyIndicatorDisplayName(sec)}`; } if (item.kind === 'composite' && isPriceExtremeBreakoutPairPaletteValue(item.value)) { const s = item.shortPeriod ?? 9; const l = item.longPeriod ?? 20; return `${s} / ${l}일`; } if (item.kind === 'composite' && isInflection33PaletteValue(item.value)) { return `${item.period ?? 33}일`; } if (item.kind === 'composite' && isStableStrategyPaletteValue(item.value)) { const meta = STABLE_STRATEGY_PALETTE_ITEMS.find(i => i.value === item.value); return meta?.periodHint ?? ''; } if (item.kind === 'composite') { const d = getCompositeDefaultPeriods(item.value, def); return `${d.short} / ${d.long}`; } return getIndicatorPeriodLabel(item.value, def) || String(getDefaultIndicatorPeriod(item.value, def)); } interface Props { kind: PaletteItemKind; items: PaletteItem[]; onItemsChange: (items: PaletteItem[]) => void; def: DefType; searchQuery: string; selectedItemId: string | null; onSelectItem: (id: string | null) => void; onAddToCanvas: (item: PaletteItem) => void; } export default function IndicatorPaletteTab({ kind, items, onItemsChange, def, searchQuery, selectedItemId, onSelectItem, onAddToCanvas, }: Props) { const [modalOpen, setModalOpen] = useState(false); const [modalMode, setModalMode] = useState<'add' | 'edit'>('add'); const [editTarget, setEditTarget] = useState(null); const q = searchQuery.trim().toLowerCase(); const filtered = useMemo(() => { if (!q) return items; return items.filter( i => i.label.toLowerCase().includes(q) || i.desc.toLowerCase().includes(q) || i.value.toLowerCase().includes(q), ); }, [items, q]); const selected = items.find(i => i.id === selectedItemId) ?? null; const persist = useCallback((next: PaletteItem[]) => { onItemsChange(next); savePaletteItems(kind, next); }, [kind, onItemsChange]); const openAdd = () => { setEditTarget(null); setModalMode('add'); setModalOpen(true); }; const openEdit = () => { if (!selected) { window.alert('수정할 지표 카드를 먼저 선택해 주세요.'); return; } setEditTarget(selected); setModalMode('edit'); setModalOpen(true); }; const handleDelete = () => { if (!selected) { window.alert('삭제할 지표 카드를 먼저 선택해 주세요.'); return; } if (!window.confirm(`「${selected.label}」을(를) 목록에서 삭제할까요?`)) return; const next = items.filter(i => i.id !== selected.id); persist(next); onSelectItem(null); }; const handleSave = (draft: Omit & { id?: string }) => { if (modalMode === 'add') { const item: PaletteItem = { id: createPaletteItemId(), kind, value: draft.value, label: draft.label, desc: draft.desc, period: draft.period, shortPeriod: draft.shortPeriod, longPeriod: draft.longPeriod, }; persist([...items, item]); onSelectItem(item.id); return; } const id = draft.id ?? editTarget?.id; if (!id) return; persist(items.map(i => (i.id === id ? { ...i, label: draft.label, desc: draft.desc, period: draft.period, shortPeriod: draft.shortPeriod, longPeriod: draft.longPeriod, } : i))); }; return (
{kind === 'auxiliary' ? '보조지표' : '복합지표'}
{filtered.map(item => ( onSelectItem(item.id)} onAdd={() => onAddToCanvas(item)} /> ))}
{filtered.length === 0 && (

검색 결과가 없습니다.

)} setModalOpen(false)} onSave={handleSave} />
); }