전략편집기 메뉴 추가

This commit is contained in:
Macbook
2026-05-24 06:20:18 +09:00
parent bbe82e510b
commit ea39f7df27
24 changed files with 4897 additions and 29 deletions
@@ -0,0 +1,121 @@
import React from 'react';
import type { LogicNode } from '../../utils/strategyTypes';
import { nodeToText, type DefType } from '../../utils/strategyEditorShared';
import { getIndicatorPaletteCategory } from './paletteCategories';
interface Props {
buyCondition: LogicNode | null;
sellCondition: LogicNode | null;
orphanCount: number;
def: DefType;
}
function renderCondition(node: LogicNode, def: DefType): React.ReactNode {
const full = nodeToText(node, def);
const ind = node.condition?.indicatorType ?? '';
const tail = ind && full.startsWith(`${ind} -`) ? full.slice(ind.length) : full;
const cat = getIndicatorPaletteCategory(ind);
return (
<>
<span className="se-formula-punc">(</span>
{ind ? (
<>
<span className={`se-formula-ind se-formula-ind--${cat}`}>{ind}</span>
{tail ? <span className="se-formula-detail">{tail}</span> : null}
</>
) : (
<span className="se-formula-detail">{full || '?'}</span>
)}
<span className="se-formula-punc">)</span>
</>
);
}
function renderNode(node: LogicNode, def: DefType): React.ReactNode {
if (node.type === 'CONDITION') return renderCondition(node, def);
if (node.type === 'AND') {
const children = node.children ?? [];
if (children.length === 0) return <span className="se-formula-empty">( AND)</span>;
return (
<>
<span className="se-formula-punc">(</span>
{children.map((child, i) => (
<React.Fragment key={child.id}>
{i > 0 ? <span className="se-formula-logic se-formula-logic--and"> AND </span> : null}
{renderNode(child, def)}
</React.Fragment>
))}
<span className="se-formula-punc">)</span>
</>
);
}
if (node.type === 'OR') {
const children = node.children ?? [];
if (children.length === 0) return <span className="se-formula-empty">( OR)</span>;
return (
<>
<span className="se-formula-punc">(</span>
{children.map((child, i) => (
<React.Fragment key={child.id}>
{i > 0 ? <span className="se-formula-logic se-formula-logic--or"> OR </span> : null}
{renderNode(child, def)}
</React.Fragment>
))}
<span className="se-formula-punc">)</span>
</>
);
}
if (node.type === 'NOT') {
const child = node.children?.[0];
return child ? (
<>
<span className="se-formula-logic se-formula-logic--not">NOT </span>
{renderNode(child, def)}
</>
) : (
<span className="se-formula-empty">( NOT)</span>
);
}
return null;
}
export default function LogicExpressionPreview({
buyCondition,
sellCondition,
orphanCount,
def,
}: Props) {
const hasBody = buyCondition || sellCondition;
return (
<div className="se-terminal-text se-terminal-text--rich">
{!hasBody && (
<span className="se-formula-empty">( )</span>
)}
{buyCondition && (
<div className="se-formula-line">
<span className="se-formula-signal se-formula-signal--buy">[]</span>
{' '}
{renderNode(buyCondition, def)}
</div>
)}
{sellCondition && (
<div className="se-formula-line">
<span className="se-formula-signal se-formula-signal--sell">[]</span>
{' '}
{renderNode(sellCondition, def)}
</div>
)}
{orphanCount > 0 && (
<div className="se-formula-line se-formula-comment">
{'// 미연결 요소 '}{orphanCount}{'개 — 전략 코드 미포함'}
</div>
)}
</div>
);
}