전략편집기 수정
This commit is contained in:
@@ -3,9 +3,11 @@ import {
|
|||||||
handlePaletteMouseDown,
|
handlePaletteMouseDown,
|
||||||
handlePalettePointerDown,
|
handlePalettePointerDown,
|
||||||
handlePaletteTouchStart,
|
handlePaletteTouchStart,
|
||||||
|
endHtmlDragMouseTracking,
|
||||||
markPaletteHtmlDragEnd,
|
markPaletteHtmlDragEnd,
|
||||||
needsPointerPaletteDrag,
|
needsPointerPaletteDrag,
|
||||||
shouldSuppressPaletteClick,
|
shouldSuppressPaletteClick,
|
||||||
|
startHtmlDragMouseTracking,
|
||||||
writePaletteHtmlDragData,
|
writePaletteHtmlDragData,
|
||||||
type PaletteDragPayload,
|
type PaletteDragPayload,
|
||||||
} from '../../utils/paletteDragSession';
|
} from '../../utils/paletteDragSession';
|
||||||
@@ -55,11 +57,13 @@ export default function PaletteChip({
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
writePaletteHtmlDragData(e, buildPayload());
|
const payload = buildPayload();
|
||||||
|
writePaletteHtmlDragData(e, payload);
|
||||||
|
startHtmlDragMouseTracking(e.clientX, e.clientY, payload);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onDragEnd = () => {
|
const onDragEnd = () => {
|
||||||
markPaletteHtmlDragEnd();
|
endHtmlDragMouseTracking();
|
||||||
};
|
};
|
||||||
|
|
||||||
const onPointerDown = (e: React.PointerEvent) => {
|
const onPointerDown = (e: React.PointerEvent) => {
|
||||||
|
|||||||
@@ -4,8 +4,9 @@ import type { DefType } from '../../utils/strategyEditorShared';
|
|||||||
import {
|
import {
|
||||||
handlePalettePointerDown,
|
handlePalettePointerDown,
|
||||||
handlePaletteTouchStart,
|
handlePaletteTouchStart,
|
||||||
markPaletteHtmlDragEnd,
|
endHtmlDragMouseTracking,
|
||||||
needsPointerPaletteDrag,
|
needsPointerPaletteDrag,
|
||||||
|
startHtmlDragMouseTracking,
|
||||||
writePaletteHtmlDragData,
|
writePaletteHtmlDragData,
|
||||||
type PaletteDragPayload,
|
type PaletteDragPayload,
|
||||||
} from '../../utils/paletteDragSession';
|
} from '../../utils/paletteDragSession';
|
||||||
@@ -88,11 +89,12 @@ function SidewaysFilterChip({
|
|||||||
label: item.label,
|
label: item.label,
|
||||||
};
|
};
|
||||||
writePaletteHtmlDragData(e, payload as PaletteDragPayload);
|
writePaletteHtmlDragData(e, payload as PaletteDragPayload);
|
||||||
|
startHtmlDragMouseTracking(e.clientX, e.clientY, payload as PaletteDragPayload);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onDragEnd = () => {
|
const onDragEnd = () => {
|
||||||
hideHint();
|
hideHint();
|
||||||
markPaletteHtmlDragEnd();
|
endHtmlDragMouseTracking();
|
||||||
};
|
};
|
||||||
|
|
||||||
const onPointerDown = (e: React.PointerEvent) => {
|
const onPointerDown = (e: React.PointerEvent) => {
|
||||||
|
|||||||
@@ -1308,32 +1308,26 @@ function StrategyEditorCanvasInner({
|
|||||||
if (anchorId) {
|
if (anchorId) {
|
||||||
updatePreviewRef.current(anchorId, flowPos);
|
updatePreviewRef.current(anchorId, flowPos);
|
||||||
lastPreviewAnchorRef.current = anchorId;
|
lastPreviewAnchorRef.current = anchorId;
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (lastPreviewAnchorRef.current != null) {
|
|
||||||
lastPreviewAnchorRef.current = null;
|
|
||||||
lastPreviewKeyRef.current = null;
|
|
||||||
clearPreviewRef.current();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
document.addEventListener('dragover', onDocDragOver, { passive: false });
|
||||||
|
return () => document.removeEventListener('dragover', onDocDragOver);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
const onDocDragEnd = () => {
|
const onDocDragEnd = () => {
|
||||||
lastPreviewAnchorRef.current = null;
|
lastPreviewAnchorRef.current = null;
|
||||||
lastPreviewKeyRef.current = null;
|
lastPreviewKeyRef.current = null;
|
||||||
clearPreviewRef.current();
|
clearPreviewRef.current();
|
||||||
markPaletteHtmlDragEnd();
|
markPaletteHtmlDragEnd();
|
||||||
};
|
};
|
||||||
document.addEventListener('dragover', onDocDragOver, { passive: false });
|
|
||||||
document.addEventListener('drag', onDocDragOver);
|
|
||||||
document.addEventListener('dragend', onDocDragEnd);
|
document.addEventListener('dragend', onDocDragEnd);
|
||||||
return () => {
|
return () => {
|
||||||
document.removeEventListener('dragover', onDocDragOver);
|
|
||||||
document.removeEventListener('drag', onDocDragOver);
|
|
||||||
document.removeEventListener('dragend', onDocDragEnd);
|
document.removeEventListener('dragend', onDocDragEnd);
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!needsPointerPaletteDrag()) return;
|
|
||||||
return subscribePaletteDrag((ev: PaletteDragEvent) => {
|
return subscribePaletteDrag((ev: PaletteDragEvent) => {
|
||||||
if (ev.phase === 'move') {
|
if (ev.phase === 'move') {
|
||||||
const { flowPos, anchorId } = resolveDropFromClientRef.current(ev.x, ev.y);
|
const { flowPos, anchorId } = resolveDropFromClientRef.current(ev.x, ev.y);
|
||||||
|
|||||||
@@ -493,7 +493,6 @@ export default function StrategyListEditor({
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!needsPointerPaletteDrag()) return;
|
|
||||||
return subscribePaletteDrag(ev => {
|
return subscribePaletteDrag(ev => {
|
||||||
if (ev.phase === 'move') {
|
if (ev.phase === 'move') {
|
||||||
if (!isOverListEditor(ev.x, ev.y)) {
|
if (!isOverListEditor(ev.x, ev.y)) {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
bindWindowDrag,
|
bindWindowDrag,
|
||||||
isPrimaryPointerButton,
|
isPrimaryPointerButton,
|
||||||
} from './pointerDrag';
|
} from './pointerDrag';
|
||||||
import { isDesktop } from './platform';
|
import { isDesktop, isMacDesktop } from './platform';
|
||||||
|
|
||||||
export type PaletteDragPayload = Record<string, unknown> & {
|
export type PaletteDragPayload = Record<string, unknown> & {
|
||||||
type: string;
|
type: string;
|
||||||
@@ -54,6 +54,7 @@ let moveRafId: number | null = null;
|
|||||||
let pendingMove: { x: number; y: number } | null = null;
|
let pendingMove: { x: number; y: number } | null = null;
|
||||||
let unbindGesture: (() => void) | null = null;
|
let unbindGesture: (() => void) | null = null;
|
||||||
let lastDragClientXY: { x: number; y: number } | null = null;
|
let lastDragClientXY: { x: number; y: number } | null = null;
|
||||||
|
let htmlDragMouseTrackUnbind: (() => void) | null = null;
|
||||||
let htmlPaletteDragActive = false;
|
let htmlPaletteDragActive = false;
|
||||||
let lastHtmlDragClientXY: { x: number; y: number } | null = null;
|
let lastHtmlDragClientXY: { x: number; y: number } | null = null;
|
||||||
|
|
||||||
@@ -80,6 +81,7 @@ function installHtmlPaletteDragGlobals() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
window.addEventListener('dragend', () => {
|
window.addEventListener('dragend', () => {
|
||||||
|
stopHtmlDragMouseTracking();
|
||||||
htmlPaletteDragActive = false;
|
htmlPaletteDragActive = false;
|
||||||
lastHtmlDragClientXY = null;
|
lastHtmlDragClientXY = null;
|
||||||
});
|
});
|
||||||
@@ -96,8 +98,9 @@ function hasCoarsePointer(): boolean {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** HTML5 DnD 대신 pointer/touch overlay 드래그 (Tauri Desktop 전체 — macOS WKWebView HTML5 DnD 불안정) */
|
/** HTML5 DnD 대신 pointer overlay (Windows Desktop 등). macOS WKWebView는 HTML5 + mousemove preview */
|
||||||
export function needsPointerPaletteDrag(): boolean {
|
export function needsPointerPaletteDrag(): boolean {
|
||||||
|
if (isMacDesktop()) return false;
|
||||||
return isDesktop() || hasCoarsePointer();
|
return isDesktop() || hasCoarsePointer();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -277,8 +280,46 @@ function startPaletteDragGesture(
|
|||||||
finishDrag('end', xy);
|
finishDrag('end', xy);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ preventTouchScroll: true, useCapture: isDesktop() },
|
{ preventTouchScroll: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (gestureCaptureEl) {
|
||||||
|
const el = gestureCaptureEl;
|
||||||
|
const pid = pointerId;
|
||||||
|
const onElMove = (ev: PointerEvent) => {
|
||||||
|
if (ev.pointerId !== pid) return;
|
||||||
|
const xy = { x: ev.clientX, y: ev.clientY };
|
||||||
|
if (!dragging) {
|
||||||
|
const dx = xy.x - startX;
|
||||||
|
const dy = xy.y - startY;
|
||||||
|
if (Math.hypot(dx, dy) < DRAG_THRESHOLD_PX) return;
|
||||||
|
dragging = true;
|
||||||
|
mountDragOverlay(payload, xy, pointerId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
notifyPaletteDragMove(xy.x, xy.y);
|
||||||
|
};
|
||||||
|
const onElEnd = (ev: PointerEvent) => {
|
||||||
|
if (ev.pointerId !== pid) return;
|
||||||
|
el.removeEventListener('pointermove', onElMove);
|
||||||
|
el.removeEventListener('pointerup', onElEnd);
|
||||||
|
el.removeEventListener('pointercancel', onElEnd);
|
||||||
|
unbindGestureListeners();
|
||||||
|
if (dragging && activePayload) {
|
||||||
|
finishDrag('end', { x: ev.clientX, y: ev.clientY });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
el.addEventListener('pointermove', onElMove);
|
||||||
|
el.addEventListener('pointerup', onElEnd);
|
||||||
|
el.addEventListener('pointercancel', onElEnd);
|
||||||
|
const windowUnbind = unbindGesture;
|
||||||
|
unbindGesture = () => {
|
||||||
|
el.removeEventListener('pointermove', onElMove);
|
||||||
|
el.removeEventListener('pointerup', onElEnd);
|
||||||
|
el.removeEventListener('pointercancel', onElEnd);
|
||||||
|
windowUnbind?.();
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function handlePalettePointerDown(
|
export function handlePalettePointerDown(
|
||||||
@@ -336,12 +377,45 @@ export function handlePaletteTouchStart(
|
|||||||
startPaletteDragGesture(touch.clientX, touch.clientY, touch.identifier, payload);
|
startPaletteDragGesture(touch.clientX, touch.clientY, touch.identifier, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function stopHtmlDragMouseTracking(): void {
|
||||||
|
htmlDragMouseTrackUnbind?.();
|
||||||
|
htmlDragMouseTrackUnbind = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** HTML5 drag 중 mousemove 로 preview 좌표 추적 (macOS WKWebView — dragover 좌표/hit-test 불안정) */
|
||||||
|
export function startHtmlDragMouseTracking(
|
||||||
|
clientX: number,
|
||||||
|
clientY: number,
|
||||||
|
payload: PaletteDragPayload,
|
||||||
|
): void {
|
||||||
|
stopHtmlDragMouseTracking();
|
||||||
|
if (clientX !== 0 || clientY !== 0) {
|
||||||
|
lastHtmlDragClientXY = { x: clientX, y: clientY };
|
||||||
|
}
|
||||||
|
htmlDragMouseTrackUnbind = bindWindowDrag(
|
||||||
|
(xy) => {
|
||||||
|
lastHtmlDragClientXY = xy;
|
||||||
|
emit({ phase: 'move', x: xy.x, y: xy.y, payload });
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
/* native HTML5 drag 중 mouseup 은 dragend 전에 올 수 있음 — dragend 에서 정리 */
|
||||||
|
},
|
||||||
|
{ preventTouchScroll: false },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function endHtmlDragMouseTracking(): void {
|
||||||
|
stopHtmlDragMouseTracking();
|
||||||
|
markPaletteHtmlDragEnd();
|
||||||
|
}
|
||||||
|
|
||||||
export function markPaletteHtmlDragStart(): void {
|
export function markPaletteHtmlDragStart(): void {
|
||||||
htmlPaletteDragActive = true;
|
htmlPaletteDragActive = true;
|
||||||
lastHtmlDragClientXY = null;
|
lastHtmlDragClientXY = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function markPaletteHtmlDragEnd(): void {
|
export function markPaletteHtmlDragEnd(): void {
|
||||||
|
stopHtmlDragMouseTracking();
|
||||||
htmlPaletteDragActive = false;
|
htmlPaletteDragActive = false;
|
||||||
lastHtmlDragClientXY = null;
|
lastHtmlDragClientXY = null;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user