맥 os 전략편집기 지표 드래그 문제해결
This commit is contained in:
@@ -4,18 +4,27 @@ import {
|
||||
getLastHtmlPaletteDragClientXY,
|
||||
isPaletteHtmlDragActive,
|
||||
} from './paletteDragSession';
|
||||
import { getNodeDimensions } from './strategyFlowLayout';
|
||||
|
||||
type FlowProjector = Pick<ReactFlowInstance, 'screenToFlowPosition' | 'getViewport'>;
|
||||
|
||||
const DRAG_LAYER_SELECTOR = '.se-palette-drag-overlay, .se-palette-drag-ghost';
|
||||
|
||||
function getFlowProjectionHost(containerEl: HTMLElement | null): HTMLElement | null {
|
||||
if (!containerEl) return null;
|
||||
return containerEl.querySelector('.react-flow__viewport') as HTMLElement | null
|
||||
?? containerEl.querySelector('.react-flow__renderer') as HTMLElement | null
|
||||
?? containerEl.querySelector('.react-flow') as HTMLElement | null
|
||||
?? containerEl;
|
||||
}
|
||||
|
||||
function manualProject(
|
||||
clientX: number,
|
||||
clientY: number,
|
||||
rf: FlowProjector,
|
||||
containerEl: HTMLElement | null,
|
||||
): { x: number; y: number } {
|
||||
const host = (containerEl?.querySelector('.react-flow') as HTMLElement | null) ?? containerEl;
|
||||
const host = getFlowProjectionHost(containerEl);
|
||||
if (!host) return { x: 0, y: 0 };
|
||||
|
||||
const rect = host.getBoundingClientRect();
|
||||
@@ -159,12 +168,73 @@ export interface PaletteDropResolution {
|
||||
anchorId: string | null;
|
||||
}
|
||||
|
||||
/** WKWebView HTML5 drag — dragover client 좌표가 0,0 으로 오는 경우 보정 */
|
||||
export function resolvePaletteDragClientPoint(clientX: number, clientY: number): { x: number; y: number } {
|
||||
if (clientX !== 0 || clientY !== 0) return { x: clientX, y: clientY };
|
||||
if (!isPaletteHtmlDragActive()) return { x: clientX, y: clientY };
|
||||
const last = getLastHtmlPaletteDragClientXY();
|
||||
return last ?? { x: clientX, y: clientY };
|
||||
function projectFlowToScreen(
|
||||
flowX: number,
|
||||
flowY: number,
|
||||
rf: FlowProjector | null,
|
||||
containerEl: HTMLElement | null,
|
||||
): { x: number; y: number } {
|
||||
const host = getFlowProjectionHost(containerEl);
|
||||
if (!host || !rf) return { x: flowX, y: flowY };
|
||||
|
||||
const rect = host.getBoundingClientRect();
|
||||
const { x: vx, y: vy, zoom } = rf.getViewport();
|
||||
const z = zoom || 1;
|
||||
return {
|
||||
x: rect.left + vx + flowX * z,
|
||||
y: rect.top + vy + flowY * z,
|
||||
};
|
||||
}
|
||||
|
||||
/** WKWebView HTML5 drag — dragover client 0,0 보정 (Web 전용) */
|
||||
export function resolvePaletteDragClientPoint(
|
||||
clientX: number,
|
||||
clientY: number,
|
||||
): { x: number; y: number } {
|
||||
if (clientX !== 0 || clientY !== 0) {
|
||||
return { x: clientX, y: clientY };
|
||||
}
|
||||
if (!isPaletteHtmlDragActive() && !getActivePaletteDrag()) {
|
||||
return { x: clientX, y: clientY };
|
||||
}
|
||||
const fromHtml = getLastHtmlPaletteDragClientXY();
|
||||
if (fromHtml && (fromHtml.x !== 0 || fromHtml.y !== 0)) return fromHtml;
|
||||
return { x: clientX, y: clientY };
|
||||
}
|
||||
|
||||
/** flow 노드 screen rect hit-test (DOM / elementsFromPoint 불필요 — WKWebView HTML5 drag 대응) */
|
||||
function findConnectTargetAtClientPoint(
|
||||
clientX: number,
|
||||
clientY: number,
|
||||
rf: FlowProjector | null,
|
||||
containerEl: HTMLElement | null,
|
||||
nodes: { id: string; position: { x: number; y: number } }[],
|
||||
isConnectTarget: (nodeId: string) => boolean,
|
||||
): string | null {
|
||||
const pad = 12;
|
||||
for (let i = nodes.length - 1; i >= 0; i--) {
|
||||
const n = nodes[i];
|
||||
if (!isConnectTarget(n.id)) continue;
|
||||
const dim = getNodeDimensions(n.id);
|
||||
const tl = projectFlowToScreen(n.position.x, n.position.y, rf, containerEl);
|
||||
const br = projectFlowToScreen(
|
||||
n.position.x + dim.w,
|
||||
n.position.y + dim.h,
|
||||
rf,
|
||||
containerEl,
|
||||
);
|
||||
const left = Math.min(tl.x, br.x) - pad;
|
||||
const right = Math.max(tl.x, br.x) + pad;
|
||||
const top = Math.min(tl.y, br.y) - pad;
|
||||
const bottom = Math.max(tl.y, br.y) + pad;
|
||||
if (
|
||||
clientX >= left && clientX <= right
|
||||
&& clientY >= top && clientY <= bottom
|
||||
) {
|
||||
return n.id;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** client 좌표 → flow 위치 + 연결 대상 노드 (DOM 우선, flow hit 보조) */
|
||||
@@ -183,6 +253,32 @@ export function resolvePaletteDropClientPoint(
|
||||
const { x: px, y: py } = resolvePaletteDragClientPoint(clientX, clientY);
|
||||
const flowPos = projectScreenToFlowPosition(px, py, rf, containerEl);
|
||||
|
||||
// 1) DOM rect — pointer overlay 에서 가장 정확
|
||||
for (const node of nodes) {
|
||||
if (!isConnectTarget(node.id)) continue;
|
||||
const escaped = typeof CSS !== 'undefined' && CSS.escape
|
||||
? CSS.escape(node.id)
|
||||
: node.id.replace(/"/g, '\\"');
|
||||
const el = document.querySelector(`.react-flow__node[data-id="${escaped}"]`)
|
||||
?? document.getElementById(`react-flow__node-${node.id}`);
|
||||
if (!el) continue;
|
||||
const rect = el.getBoundingClientRect();
|
||||
const pad = 12;
|
||||
if (
|
||||
px >= rect.left - pad && px <= rect.right + pad
|
||||
&& py >= rect.top - pad && py <= rect.bottom + pad
|
||||
) {
|
||||
return { flowPos, anchorId: node.id };
|
||||
}
|
||||
}
|
||||
|
||||
const flowRectId = findConnectTargetAtClientPoint(
|
||||
px, py, rf, containerEl, nodes, isConnectTarget,
|
||||
);
|
||||
if (flowRectId) {
|
||||
return { flowPos, anchorId: flowRectId };
|
||||
}
|
||||
|
||||
const domId = findFlowNodeIdAtClientPoint(px, py);
|
||||
if (domId && nodes.some(n => n.id === domId) && isConnectTarget(domId)) {
|
||||
return { flowPos, anchorId: domId };
|
||||
@@ -195,23 +291,6 @@ export function resolvePaletteDropClientPoint(
|
||||
}
|
||||
}
|
||||
|
||||
for (const node of nodes) {
|
||||
if (!isConnectTarget(node.id)) continue;
|
||||
const escaped = typeof CSS !== 'undefined' && CSS.escape
|
||||
? CSS.escape(node.id)
|
||||
: node.id.replace(/"/g, '\\"');
|
||||
const el = document.querySelector(`.react-flow__node[data-id="${escaped}"]`)
|
||||
?? document.getElementById(`react-flow__node-${node.id}`);
|
||||
if (!el) continue;
|
||||
const rect = el.getBoundingClientRect();
|
||||
if (
|
||||
px >= rect.left && px <= rect.right
|
||||
&& py >= rect.top && py <= rect.bottom
|
||||
) {
|
||||
return { flowPos, anchorId: node.id };
|
||||
}
|
||||
}
|
||||
|
||||
const flowHit = findAtFlow(flowPos, nodes);
|
||||
if (flowHit && isConnectTarget(flowHit.id)) {
|
||||
return { flowPos, anchorId: flowHit.id };
|
||||
|
||||
Reference in New Issue
Block a user