전략편집기 수정

This commit is contained in:
Macbook
2026-06-14 22:17:53 +09:00
parent dec812089d
commit 76ffa5ac52
6 changed files with 343 additions and 52 deletions
@@ -29,6 +29,13 @@ import {
type DefType,
} from '../../utils/strategyEditorShared';
import { buildSidewaysFilterNode } from '../../utils/sidewaysFilterPaletteStorage';
import {
isPaletteHtmlDrag,
needsPointerPaletteDrag,
readPaletteHtmlDragData,
subscribePaletteDrag,
type PaletteDragEvent,
} from '../../utils/paletteDragSession';
import { setStochPairSecondary } from '../../utils/stochOverboughtPair';
import {
setPriceExtremePairFilterLevel,
@@ -1190,8 +1197,6 @@ function StrategyEditorCanvasInner({
));
}, [root, orphans, extraRoots, applyResolvedConnection, setEdges]);
const isPaletteDrag = (e: React.DragEvent) => e.dataTransfer.types.includes('application/json');
const resolvePaletteDrop = useCallback((flowPos: { x: number; y: number }) => {
const hit = findNodeAtFlowPosition(flowPos, nodesRef.current);
if (hit && isPaletteConnectTarget(hit.id, root, orphans)) {
@@ -1200,24 +1205,63 @@ function StrategyEditorCanvasInner({
return { mode: 'orphan' as const };
}, [root, orphans]);
const isPaletteDrag = (e: React.DragEvent) => isPaletteHtmlDrag(e);
const applyPaletteDropAt = useCallback((
data: { type: string; value: string; label: string; composite?: boolean },
clientX: number,
clientY: number,
) => {
const flowPos = screenToFlowPosition({ x: clientX, y: clientY });
if (data.type === 'start') {
addStartAt(flowPos);
return;
}
const drop = resolvePaletteDrop(flowPos);
if (drop.mode === 'connect') {
applyDropWithAttach(drop.anchorId, data, flowPos);
} else {
addOrphanAt(data, flowPos);
}
}, [screenToFlowPosition, applyDropWithAttach, addOrphanAt, addStartAt, resolvePaletteDrop]);
const onPaneDrop = useCallback((e: React.DragEvent) => {
e.preventDefault();
clearDropPreview();
try {
const data = JSON.parse(e.dataTransfer.getData('application/json'));
const flowPos = screenToFlowPosition({ x: e.clientX, y: e.clientY });
if (data.type === 'start') {
addStartAt(flowPos);
const data = readPaletteHtmlDragData(e);
if (!data) return;
applyPaletteDropAt(data, e.clientX, e.clientY);
}, [clearDropPreview, applyPaletteDropAt]);
useEffect(() => {
if (!needsPointerPaletteDrag()) return;
return subscribePaletteDrag((ev: PaletteDragEvent) => {
if (ev.phase === 'move') {
const flowPos = screenToFlowPosition({ x: ev.x, y: ev.y });
const drop = resolvePaletteDrop(flowPos);
if (drop.mode === 'connect') {
updateDropPreview(drop.anchorId, flowPos);
} else {
clearDropPreview();
}
return;
}
const drop = resolvePaletteDrop(flowPos);
if (drop.mode === 'connect') {
applyDropWithAttach(drop.anchorId, data, flowPos);
} else {
addOrphanAt(data, flowPos);
if (ev.phase === 'end') {
clearDropPreview();
applyPaletteDropAt(ev.payload, ev.x, ev.y);
return;
}
} catch { /* ignore */ }
}, [clearDropPreview, screenToFlowPosition, applyDropWithAttach, addOrphanAt, addStartAt, resolvePaletteDrop]);
if (ev.phase === 'cancel') {
clearDropPreview();
}
});
}, [
screenToFlowPosition,
resolvePaletteDrop,
updateDropPreview,
clearDropPreview,
applyPaletteDropAt,
]);
const onPaneDragOver = useCallback((e: React.DragEvent) => {
e.preventDefault();