목록형 전략평가 수정
This commit is contained in:
@@ -34,6 +34,7 @@ import {
|
|||||||
normalizeStartCombineOp,
|
normalizeStartCombineOp,
|
||||||
updateStartCombineOp,
|
updateStartCombineOp,
|
||||||
updateStartCandleTypes,
|
updateStartCandleTypes,
|
||||||
|
updateBranchRoot,
|
||||||
type EditorConditionState,
|
type EditorConditionState,
|
||||||
} from '../utils/strategyConditionSerde';
|
} from '../utils/strategyConditionSerde';
|
||||||
import { migrateLogicRootForEditor } from '../utils/strategyConditionNormalize';
|
import { migrateLogicRootForEditor } from '../utils/strategyConditionNormalize';
|
||||||
@@ -778,6 +779,7 @@ export default function StrategyEditorPage({
|
|||||||
const resolved = typeof next === 'function'
|
const resolved = typeof next === 'function'
|
||||||
? next(currentEditorStateRef.current)
|
? next(currentEditorStateRef.current)
|
||||||
: next;
|
: next;
|
||||||
|
currentEditorStateRef.current = resolved;
|
||||||
setCurrentRoot(resolved.root);
|
setCurrentRoot(resolved.root);
|
||||||
setCurrentLayout(prev => ({
|
setCurrentLayout(prev => ({
|
||||||
...prev,
|
...prev,
|
||||||
@@ -790,12 +792,12 @@ export default function StrategyEditorPage({
|
|||||||
}, [setCurrentRoot, setCurrentLayout, scheduleStrategyPersist]);
|
}, [setCurrentRoot, setCurrentLayout, scheduleStrategyPersist]);
|
||||||
|
|
||||||
const handleStartCandleTypesChange = useCallback((startId: string, candleTypes: string[]) => {
|
const handleStartCandleTypesChange = useCallback((startId: string, candleTypes: string[]) => {
|
||||||
handleEditorStateChange(updateStartCandleTypes(currentEditorState, startId, candleTypes));
|
handleEditorStateChange(prev => updateStartCandleTypes(prev, startId, candleTypes));
|
||||||
}, [handleEditorStateChange, currentEditorState]);
|
}, [handleEditorStateChange]);
|
||||||
|
|
||||||
const handleStartCombineOpChange = useCallback((op: StartCombineOp) => {
|
const handleStartCombineOpChange = useCallback((op: StartCombineOp) => {
|
||||||
handleEditorStateChange(updateStartCombineOp(currentEditorState, op));
|
handleEditorStateChange(prev => updateStartCombineOp(prev, op));
|
||||||
}, [handleEditorStateChange, currentEditorState]);
|
}, [handleEditorStateChange]);
|
||||||
|
|
||||||
const switchSignalTab = useCallback((tab: 'buy' | 'sell') => {
|
const switchSignalTab = useCallback((tab: 'buy' | 'sell') => {
|
||||||
if (editorMode === 'graph') layoutFlushRef.current?.();
|
if (editorMode === 'graph') layoutFlushRef.current?.();
|
||||||
@@ -1328,9 +1330,9 @@ export default function StrategyEditorPage({
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleAddStartSection = useCallback(() => {
|
const handleAddStartSection = useCallback(() => {
|
||||||
handleEditorStateChange(addExtraStartSection(currentEditorState));
|
handleEditorStateChange(prev => addExtraStartSection(prev));
|
||||||
bumpLayoutSeed(layoutStrategyKey, signalTab);
|
bumpLayoutSeed(layoutStrategyKey, signalTab);
|
||||||
}, [handleEditorStateChange, currentEditorState, bumpLayoutSeed, layoutStrategyKey, signalTab]);
|
}, [handleEditorStateChange, bumpLayoutSeed, layoutStrategyKey, signalTab]);
|
||||||
|
|
||||||
const applyPalette = useCallback((type: string, value: string, _label: string, composite = false) => {
|
const applyPalette = useCallback((type: string, value: string, _label: string, composite = false) => {
|
||||||
if (type === 'start') {
|
if (type === 'start') {
|
||||||
@@ -1338,12 +1340,15 @@ export default function StrategyEditorPage({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const newNode = makeNode(type, value, signalTab, DEF, composite ? { composite: true } : undefined);
|
const newNode = makeNode(type, value, signalTab, DEF, composite ? { composite: true } : undefined);
|
||||||
const root = currentRoot;
|
handleEditorStateChange(prev => {
|
||||||
if (!root) setCurrentRoot(newNode);
|
const root = prev.root;
|
||||||
else if (type === 'operator') setCurrentRoot(mergeAtRoot(root, newNode, true));
|
if (!root) return updateBranchRoot(prev, START_NODE_ID, newNode);
|
||||||
else setCurrentRoot(mergeAtRoot(root, newNode, false));
|
if (type === 'operator') {
|
||||||
scheduleStrategyPersist();
|
return updateBranchRoot(prev, START_NODE_ID, mergeAtRoot(root, newNode, true));
|
||||||
}, [signalTab, DEF, currentRoot, setCurrentRoot, handleAddStartSection, scheduleStrategyPersist]);
|
}
|
||||||
|
return updateBranchRoot(prev, START_NODE_ID, mergeAtRoot(root, newNode, false));
|
||||||
|
});
|
||||||
|
}, [signalTab, DEF, handleEditorStateChange, handleAddStartSection]);
|
||||||
|
|
||||||
const applyPaletteItem = useCallback((item: PaletteItem) => {
|
const applyPaletteItem = useCallback((item: PaletteItem) => {
|
||||||
const stochPair = isStochOverboughtPairPaletteValue(item.value);
|
const stochPair = isStochOverboughtPairPaletteValue(item.value);
|
||||||
@@ -1361,11 +1366,12 @@ export default function StrategyEditorPage({
|
|||||||
? buildStableStrategyTree(item.value, DEF)
|
? buildStableStrategyTree(item.value, DEF)
|
||||||
: makeNode('indicator', item.value, signalTab, DEF, composite ? { composite: true } : undefined);
|
: makeNode('indicator', item.value, signalTab, DEF, composite ? { composite: true } : undefined);
|
||||||
if (!newNode) return;
|
if (!newNode) return;
|
||||||
const root = currentRoot;
|
handleEditorStateChange(prev => {
|
||||||
if (!root) setCurrentRoot(newNode);
|
const root = prev.root;
|
||||||
else setCurrentRoot(mergeAtRoot(root, newNode, false));
|
if (!root) return updateBranchRoot(prev, START_NODE_ID, newNode);
|
||||||
scheduleStrategyPersist();
|
return updateBranchRoot(prev, START_NODE_ID, mergeAtRoot(root, newNode, false));
|
||||||
}, [signalTab, DEF, currentRoot, setCurrentRoot, scheduleStrategyPersist]);
|
});
|
||||||
|
}, [signalTab, DEF, handleEditorStateChange]);
|
||||||
|
|
||||||
const applySidewaysFilter = useCallback((filterId: string) => {
|
const applySidewaysFilter = useCallback((filterId: string) => {
|
||||||
const newNode = buildSidewaysFilterNode(filterId, DEF, sidewaysPalette);
|
const newNode = buildSidewaysFilterNode(filterId, DEF, sidewaysPalette);
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ import {
|
|||||||
setHtmlPaletteDropRouter,
|
setHtmlPaletteDropRouter,
|
||||||
setPaletteDragDropHandler,
|
setPaletteDragDropHandler,
|
||||||
subscribePaletteDrag,
|
subscribePaletteDrag,
|
||||||
|
wasPaletteHtmlDropCommitted,
|
||||||
type PaletteDragPayload,
|
type PaletteDragPayload,
|
||||||
} from '../../utils/paletteDragSession';
|
} from '../../utils/paletteDragSession';
|
||||||
|
|
||||||
@@ -108,12 +109,18 @@ const TreeNodeComp: React.FC<TreeNodeProps> = ({
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
setDragOverKey?.(nodeDropKey);
|
setDragOverKey?.(nodeDropKey);
|
||||||
};
|
};
|
||||||
const handleDragLeave = () => setDragOverKey?.(null);
|
const handleDragLeave = (e: React.DragEvent) => {
|
||||||
|
const rel = e.relatedTarget as Node | null;
|
||||||
|
if (!rel) return;
|
||||||
|
if (e.currentTarget.contains(rel)) return;
|
||||||
|
setDragOverKey?.(null);
|
||||||
|
};
|
||||||
const handleDrop = (e: React.DragEvent) => {
|
const handleDrop = (e: React.DragEvent) => {
|
||||||
if (!isLogic) return;
|
if (!isLogic) return;
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
setDragOverKey?.(null);
|
setDragOverKey?.(null);
|
||||||
|
if (wasPaletteHtmlDropCommitted()) return;
|
||||||
try {
|
try {
|
||||||
const data = JSON.parse(e.dataTransfer.getData('application/json'));
|
const data = JSON.parse(e.dataTransfer.getData('application/json'));
|
||||||
if (data.type === 'start') {
|
if (data.type === 'start') {
|
||||||
@@ -338,6 +345,7 @@ function StartSectionBlock({
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
setDragOverKey(null);
|
setDragOverKey(null);
|
||||||
|
if (wasPaletteHtmlDropCommitted()) return;
|
||||||
const data = readPaletteHtmlDragData(e);
|
const data = readPaletteHtmlDragData(e);
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
applyDrop(null, data);
|
applyDrop(null, data);
|
||||||
@@ -375,7 +383,12 @@ function StartSectionBlock({
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
setDragOverKey(sectionDropKey);
|
setDragOverKey(sectionDropKey);
|
||||||
}}
|
}}
|
||||||
onDragLeave={() => setDragOverKey(null)}
|
onDragLeave={e => {
|
||||||
|
const rel = e.relatedTarget as Node | null;
|
||||||
|
if (!rel) return;
|
||||||
|
if (e.currentTarget.contains(rel)) return;
|
||||||
|
setDragOverKey(null);
|
||||||
|
}}
|
||||||
onDrop={handleSectionDrop}
|
onDrop={handleSectionDrop}
|
||||||
>
|
>
|
||||||
{!root ? (
|
{!root ? (
|
||||||
@@ -561,13 +574,14 @@ export default function StrategyListEditor({
|
|||||||
const handleListDragOver = (e: React.DragEvent) => {
|
const handleListDragOver = (e: React.DragEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.dataTransfer.dropEffect = 'copy';
|
e.dataTransfer.dropEffect = 'copy';
|
||||||
setDragOverKey(LIST_DROP_KEY);
|
setDragOverKeyThrottled(LIST_DROP_KEY);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleListDrop = (e: React.DragEvent) => {
|
const handleListDrop = (e: React.DragEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
setDragOverKey(null);
|
setDragOverKey(null);
|
||||||
|
if (wasPaletteHtmlDropCommitted()) return;
|
||||||
const data = readPaletteHtmlDragData(e);
|
const data = readPaletteHtmlDragData(e);
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
applyDropRef.current(findPaletteDropKeyAtPoint(e.clientX, e.clientY) ?? LIST_DROP_KEY, data);
|
applyDropRef.current(findPaletteDropKeyAtPoint(e.clientX, e.clientY) ?? LIST_DROP_KEY, data);
|
||||||
@@ -578,7 +592,12 @@ export default function StrategyListEditor({
|
|||||||
className={`se-list-editor sp-dropzone${dragOverKey === LIST_DROP_KEY ? ' se-list-editor--over' : ''}`}
|
className={`se-list-editor sp-dropzone${dragOverKey === LIST_DROP_KEY ? ' se-list-editor--over' : ''}`}
|
||||||
data-palette-drop-key={LIST_DROP_KEY}
|
data-palette-drop-key={LIST_DROP_KEY}
|
||||||
onDragOver={handleListDragOver}
|
onDragOver={handleListDragOver}
|
||||||
onDragLeave={() => setDragOverKey(null)}
|
onDragLeave={e => {
|
||||||
|
const rel = e.relatedTarget as Node | null;
|
||||||
|
if (!rel) return;
|
||||||
|
if (e.currentTarget.contains(rel)) return;
|
||||||
|
setDragOverKey(null);
|
||||||
|
}}
|
||||||
onDrop={handleListDrop}
|
onDrop={handleListDrop}
|
||||||
>
|
>
|
||||||
{sections.map(section => (
|
{sections.map(section => (
|
||||||
@@ -591,7 +610,7 @@ export default function StrategyListEditor({
|
|||||||
signalTab={signalTab}
|
signalTab={signalTab}
|
||||||
def={def}
|
def={def}
|
||||||
dragOverKey={dragOverKey}
|
dragOverKey={dragOverKey}
|
||||||
setDragOverKey={setDragOverKey}
|
setDragOverKey={setDragOverKeyThrottled}
|
||||||
onRootChange={root => handleRootChange(section.startId, root)}
|
onRootChange={root => handleRootChange(section.startId, root)}
|
||||||
onCandleTypesChange={types => handleCandleTypesChange(section.startId, types)}
|
onCandleTypesChange={types => handleCandleTypesChange(section.startId, types)}
|
||||||
onDeleteStart={section.canDelete ? () => handleDeleteStart(section.startId) : undefined}
|
onDeleteStart={section.canDelete ? () => handleDeleteStart(section.startId) : undefined}
|
||||||
|
|||||||
@@ -78,6 +78,10 @@ type HtmlPaletteDropRouter = (
|
|||||||
let htmlDropRouter: HtmlPaletteDropRouter | null = null;
|
let htmlDropRouter: HtmlPaletteDropRouter | null = null;
|
||||||
let htmlDropCommitted = false;
|
let htmlDropCommitted = false;
|
||||||
|
|
||||||
|
export function wasPaletteHtmlDropCommitted(): boolean {
|
||||||
|
return htmlDropCommitted;
|
||||||
|
}
|
||||||
|
|
||||||
export function setHtmlPaletteDropRouter(router: HtmlPaletteDropRouter | null): void {
|
export function setHtmlPaletteDropRouter(router: HtmlPaletteDropRouter | null): void {
|
||||||
htmlDropRouter = router;
|
htmlDropRouter = router;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user