전략편집기 복합지표 기능 반영

This commit is contained in:
Macbook
2026-05-25 01:34:52 +09:00
parent ca249ad318
commit 20dd257c29
21 changed files with 1781 additions and 183 deletions
@@ -0,0 +1,123 @@
import React, { useEffect, useRef } from 'react';
import type { ConditionDSL } from '../../utils/strategyTypes';
import type { DefType } from '../../utils/strategyEditorShared';
import {
getConditionRightPeriod,
getConditionThreshold,
getConditionValuePeriod,
getCompositePeriodPresetOptions,
getPeriodPresetOptions,
getThresholdBounds,
getThresholdPresetOptions,
hasEditableThreshold,
setConditionRightPeriod,
setConditionThreshold,
setConditionValuePeriod,
usesValuePeriodField,
} from '../../utils/conditionPeriods';
import { compositeDisplayName, compositeElementLabel } from '../../utils/compositeIndicators';
import ComboNumberInput from './ComboNumberInput';
interface Props {
condition: ConditionDSL;
def: DefType;
onChange: (c: ConditionDSL) => void;
onClose: () => void;
popoverClassName?: string;
}
export default function ConditionNodeSettings({
condition, def, onChange, onClose, popoverClassName = 'se-flow-settings-pop',
}: Props) {
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
const onDoc = (e: MouseEvent) => {
if (ref.current && !ref.current.contains(e.target as Node)) onClose();
};
document.addEventListener('mousedown', onDoc);
return () => document.removeEventListener('mousedown', onDoc);
}, [onClose]);
const rightThreshold = getConditionThreshold(condition, 'right');
const showThreshold = hasEditableThreshold(condition) && rightThreshold != null;
const periodPresets = getPeriodPresetOptions(condition.indicatorType, def);
const thresholdPresets = getThresholdPresetOptions(condition.indicatorType);
const thresholdBounds = getThresholdBounds(condition.indicatorType);
return (
<div
ref={ref}
className={popoverClassName}
role="dialog"
aria-label="지표 설정"
onClick={e => e.stopPropagation()}
>
<div className="se-flow-settings-head">
<span> </span>
<button
type="button"
className="se-flow-settings-close"
aria-label="닫기"
title="닫기"
onClick={onClose}
>
×
</button>
</div>
{condition.composite ? (
<>
<label className="se-flow-settings-field">
<span>{compositeElementLabel(condition.indicatorType, 1)} (, )</span>
<ComboNumberInput
key={`composite-left-${condition.indicatorType}-${getConditionValuePeriod(condition, def)}`}
value={getConditionValuePeriod(condition, def)}
options={getCompositePeriodPresetOptions(condition.indicatorType, def, 'left')}
min={1}
max={500}
onChange={p => onChange(setConditionValuePeriod(condition, p, def))}
/>
</label>
<label className="se-flow-settings-field">
<span>{compositeElementLabel(condition.indicatorType, 2)} (, )</span>
<ComboNumberInput
key={`composite-right-${condition.indicatorType}-${getConditionRightPeriod(condition, def)}`}
value={getConditionRightPeriod(condition, def)}
options={getCompositePeriodPresetOptions(condition.indicatorType, def, 'right')}
min={1}
max={500}
onChange={p => onChange(setConditionRightPeriod(condition, p))}
/>
</label>
</>
) : usesValuePeriodField(condition) ? (
<label className="se-flow-settings-field">
<span>{condition.indicatorType} ()</span>
<ComboNumberInput
value={getConditionValuePeriod(condition, def)}
options={periodPresets}
min={1}
max={500}
onChange={p => onChange(setConditionValuePeriod(condition, p, def))}
/>
</label>
) : null}
{showThreshold && rightThreshold != null && (
<label className="se-flow-settings-field">
<span></span>
<ComboNumberInput
value={rightThreshold}
options={thresholdPresets}
min={thresholdBounds.min}
max={thresholdBounds.max}
allowDecimal
onChange={v => onChange(setConditionThreshold(condition, 'right', v))}
/>
</label>
)}
{condition.composite && (
<p className="se-flow-settings-hint">{compositeDisplayName(condition.indicatorType)}</p>
)}
</div>
);
}