Refactor dragging logic (#523)

* refactor dragging logic

* Remove unnecessary undefined in TableInfo.jsx
This commit is contained in:
Karen Mkrtumyan
2025-07-05 22:37:58 +04:00
committed by GitHub
parent 47caa29f78
commit a5718f8359
3 changed files with 123 additions and 131 deletions

View File

@@ -9,7 +9,6 @@ import {
} from "@douyinfe/semi-icons"; } from "@douyinfe/semi-icons";
import { Tab, Action, ObjectType, State } from "../../data/constants"; import { Tab, Action, ObjectType, State } from "../../data/constants";
import { import {
useCanvas,
useLayout, useLayout,
useSettings, useSettings,
useUndoRedo, useUndoRedo,
@@ -24,15 +23,10 @@ export default function Area({
data, data,
onPointerDown, onPointerDown,
setResize, setResize,
setInitCoords, setInitDimensions,
}) { }) {
const ref = useRef(null); const ref = useRef(null);
const isHovered = useHover(ref); const isHovered = useHover(ref);
const {
pointer: {
spaces: { diagram: pointer },
},
} = useCanvas();
const { layout } = useLayout(); const { layout } = useLayout();
const { settings } = useSettings(); const { settings } = useSettings();
const { setSaveState } = useSaveState(); const { setSaveState } = useSaveState();
@@ -46,13 +40,11 @@ export default function Area({
const handleResize = (e, dir) => { const handleResize = (e, dir) => {
setResize({ id: data.id, dir: dir }); setResize({ id: data.id, dir: dir });
setInitCoords({ setInitDimensions({
x: data.x, x: data.x,
y: data.y, y: data.y,
width: data.width, width: data.width,
height: data.height, height: data.height,
pointerX: pointer.x,
pointerY: pointer.y,
}); });
}; };

View File

@@ -57,11 +57,9 @@ export default function Canvas() {
setBulkSelectedElements, setBulkSelectedElements,
} = useSelect(); } = useSelect();
const notDragging = { const notDragging = {
element: ObjectType.NONE, id: -1,
id: null, type: ObjectType.NONE,
prevX: 0, grabOffset: { x: 0, y: 0 },
prevY: 0,
initialPositions: [],
}; };
const [dragging, setDragging] = useState(notDragging); const [dragging, setDragging] = useState(notDragging);
const [linking, setLinking] = useState(false); const [linking, setLinking] = useState(false);
@@ -75,7 +73,6 @@ export default function Canvas() {
endX: 0, endX: 0,
endY: 0, endY: 0,
}); });
const [grabOffset, setGrabOffset] = useState({ x: 0, y: 0 });
const [hoveredTable, setHoveredTable] = useState({ const [hoveredTable, setHoveredTable] = useState({
tableId: null, tableId: null,
fieldId: null, fieldId: null,
@@ -86,13 +83,11 @@ export default function Canvas() {
cursorStart: { x: 0, y: 0 }, cursorStart: { x: 0, y: 0 },
}); });
const [areaResize, setAreaResize] = useState({ id: -1, dir: "none" }); const [areaResize, setAreaResize] = useState({ id: -1, dir: "none" });
const [initCoords, setInitCoords] = useState({ const [areaInitDimensions, setAreaInitDimensions] = useState({
x: 0, x: 0,
y: 0, y: 0,
width: 0, width: 0,
height: 0, height: 0,
pointerX: 0,
pointerY: 0,
}); });
const [bulkSelectRectPts, setBulkSelectRectPts] = useState({ const [bulkSelectRectPts, setBulkSelectRectPts] = useState({
x1: 0, x1: 0,
@@ -124,6 +119,8 @@ export default function Canvas() {
elements.push({ elements.push({
id: table.id, id: table.id,
type: ObjectType.TABLE, type: ObjectType.TABLE,
currentCoords: { x: table.x, y: table.y },
initialCoords: { x: table.x, y: table.y },
}); });
} }
}); });
@@ -145,6 +142,8 @@ export default function Canvas() {
elements.push({ elements.push({
id: area.id, id: area.id,
type: ObjectType.AREA, type: ObjectType.AREA,
currentCoords: { x: area.x, y: area.y },
initialCoords: { x: area.x, y: area.y },
}); });
} }
}); });
@@ -166,6 +165,8 @@ export default function Canvas() {
elements.push({ elements.push({
id: note.id, id: note.id,
type: ObjectType.NOTE, type: ObjectType.NOTE,
currentCoords: { x: note.x, y: note.y },
initialCoords: { x: note.x, y: note.y },
}); });
} }
}); });
@@ -210,31 +211,41 @@ export default function Canvas() {
return; return;
} }
let prevCoords = { prevX: element.x, prevY: element.y };
setGrabOffset({
x: element.x - pointer.spaces.diagram.x,
y: element.y - pointer.spaces.diagram.y,
});
let newBulkSelectedElements; let newBulkSelectedElements;
if (bulkSelectedElements.some((el) => el.id === id && el.type === type)) { if (bulkSelectedElements.some((el) => el.id === id && el.type === type)) {
newBulkSelectedElements = bulkSelectedElements; newBulkSelectedElements = bulkSelectedElements;
} else { } else {
newBulkSelectedElements = [{ id, type }]; newBulkSelectedElements = [
{
id,
type,
currentCoords: { x: element.x, y: element.y },
initialCoords: { x: element.x, y: element.y },
},
];
setBulkSelectedElements(newBulkSelectedElements); setBulkSelectedElements(newBulkSelectedElements);
} }
setDragging((prev) => ({ setDragging({
...prev,
id, id,
element: type, type,
...prevCoords, grabOffset: {
initialPositions: newBulkSelectedElements.map((el) => { x: pointer.spaces.diagram.x - element.x,
const { x, y } = getElement(el); y: pointer.spaces.diagram.y - element.y,
return { ...el, undo: { x, y } }; },
}), });
}));
}; };
const coordinatesAfterSnappingToGrid = ({ x, y }) => {
if (settings.snapToGrid) {
return {
x: Math.round(x / gridSize) * gridSize,
y: Math.round(y / gridSize) * gridSize,
};
}
return { x, y };
};
/** /**
* @param {PointerEvent} e * @param {PointerEvent} e
*/ */
@@ -258,23 +269,6 @@ export default function Canvas() {
return; return;
} }
const isDragging =
dragging.element !== ObjectType.NONE && dragging.id !== null;
const currentX = pointer.spaces.diagram.x + (isDragging ? grabOffset.x : 0);
const currentY = pointer.spaces.diagram.y + (isDragging ? grabOffset.y : 0);
let finalX = currentX;
let finalY = currentY;
if (settings.snapToGrid) {
finalX = Math.round(currentX / gridSize) * gridSize;
finalY = Math.round(currentY / gridSize) * gridSize;
}
const deltaX = finalX - dragging.prevX;
const deltaY = finalY - dragging.prevY;
if (linking) { if (linking) {
setLinkingLine({ setLinkingLine({
...linkingLine, ...linkingLine,
@@ -284,68 +278,73 @@ export default function Canvas() {
return; return;
} }
if (isDragging) { if (isDragging()) {
for (const el of bulkSelectedElements) { const { x: mainElementFinalX, y: mainElementFinalY } =
const element = getElement(el); coordinatesAfterSnappingToGrid({
const { type } = el; x: pointer.spaces.diagram.x - dragging.grabOffset.x,
if (element.locked) continue; y: pointer.spaces.diagram.y - dragging.grabOffset.y,
const { x, y } = element; });
if (type === ObjectType.TABLE) { const { currentCoords } = bulkSelectedElements.find(
updateTable(el.id, { (el) => el.id === dragging.id && el.type === dragging.type,
x: x + deltaX, );
y: y + deltaY,
});
}
if (type === ObjectType.AREA) {
updateArea(el.id, {
x: x + deltaX,
y: y + deltaY,
});
}
if (type === ObjectType.NOTE) {
updateNote(el.id, {
x: x + deltaX,
y: y + deltaY,
});
}
}
setDragging((prev) => ({ const deltaX = mainElementFinalX - currentCoords.x;
...prev, const deltaY = mainElementFinalY - currentCoords.y;
prevX: finalX,
prevY: finalY, const newBulkSelectedElements = [];
})); bulkSelectedElements.forEach((el) => {
const elementFinalCoords = {
x: el.currentCoords.x + deltaX,
y: el.currentCoords.y + deltaY,
};
if (el.type === ObjectType.TABLE) {
updateTable(el.id, { ...elementFinalCoords });
}
if (el.type === ObjectType.AREA) {
updateArea(el.id, { ...elementFinalCoords });
}
if (el.type === ObjectType.NOTE) {
updateNote(el.id, { ...elementFinalCoords });
}
newBulkSelectedElements.push({
...el,
currentCoords: elementFinalCoords,
});
});
setBulkSelectedElements(newBulkSelectedElements);
return; return;
} }
if (areaResize.id !== -1) { if (areaResize.id !== -1) {
if (areaResize.dir === "none") return; if (areaResize.dir === "none") return;
let newDims = { ...initCoords }; let newDims = { ...areaInitDimensions };
delete newDims.pointerX;
delete newDims.pointerY;
setPanning((old) => ({ ...old, isPanning: false })); setPanning((old) => ({ ...old, isPanning: false }));
const { x, y } = coordinatesAfterSnappingToGrid(pointer.spaces.diagram);
switch (areaResize.dir) { switch (areaResize.dir) {
case "br": case "br":
newDims.width = finalX - initCoords.x; newDims.width = x - areaInitDimensions.x;
newDims.height = finalY - initCoords.y; newDims.height = y - areaInitDimensions.y;
break; break;
case "tl": case "tl":
newDims.x = finalX; newDims.x = x;
newDims.y = finalY; newDims.y = y;
newDims.width = initCoords.width - (finalX - initCoords.x); newDims.width = areaInitDimensions.width - (x - areaInitDimensions.x);
newDims.height = initCoords.height - (finalY - initCoords.y); newDims.height =
areaInitDimensions.height - (y - areaInitDimensions.y);
break; break;
case "tr": case "tr":
newDims.y = finalY; newDims.y = y;
newDims.width = finalX - initCoords.x; newDims.width = x - areaInitDimensions.x;
newDims.height = initCoords.height - (finalY - initCoords.y); newDims.height =
areaInitDimensions.height - (y - areaInitDimensions.y);
break; break;
case "bl": case "bl":
newDims.x = finalX; newDims.x = x;
newDims.width = initCoords.width - (finalX - initCoords.x); newDims.width = areaInitDimensions.width - (x - areaInitDimensions.x);
newDims.height = finalY - initCoords.y; newDims.height = y - areaInitDimensions.y;
break; break;
} }
@@ -356,8 +355,8 @@ export default function Canvas() {
if (bulkSelectRectPts.show) { if (bulkSelectRectPts.show) {
setBulkSelectRectPts((prev) => ({ setBulkSelectRectPts((prev) => ({
...prev, ...prev,
x2: finalX, x2: pointer.spaces.diagram.x,
y2: finalY, y2: pointer.spaces.diagram.y,
})); }));
} }
}; };
@@ -400,27 +399,25 @@ export default function Canvas() {
} }
}; };
const coordsDidUpdate = () => { const isDragging = () => {
const element = { id: dragging.id, type: dragging.element }; return dragging.type !== ObjectType.NONE && dragging.id !== -1;
const elementData = getElement(element); };
const updated = !(
dragging.prevX === elementData.x && dragging.prevY === elementData.y
);
const didDrag = () => {
if (!isDragging()) return false;
// checking any element is sufficient
const { currentCoords, initialCoords } = bulkSelectedElements[0];
return ( return (
updated || currentCoords.x !== initialCoords.x || currentCoords.y !== initialCoords.y
dragging.initialPositions.some(
(el) => !(el.undo.x === elementData.x && el.undo.y === elementData.y),
)
); );
}; };
const didResize = (id) => { const didResize = (id) => {
return !( return !(
areas[id].x === initCoords.x && areas[id].x === areaInitDimensions.x &&
areas[id].y === initCoords.y && areas[id].y === areaInitDimensions.y &&
areas[id].width === initCoords.width && areas[id].width === areaInitDimensions.width &&
areas[id].height === initCoords.height areas[id].height === areaInitDimensions.height
); );
}; };
@@ -438,24 +435,29 @@ export default function Canvas() {
if (!e.isPrimary) return; if (!e.isPrimary) return;
const coordinatesDidUpdate = coordsDidUpdate(); if (didDrag()) {
if (coordinatesDidUpdate) {
setUndoStack((prev) => [ setUndoStack((prev) => [
...prev, ...prev,
{ {
action: Action.MOVE, action: Action.MOVE,
bulk: true, bulk: true,
message: t("bulk_update"), message: t("bulk_update"),
elements: dragging.initialPositions.map((element) => { elements: bulkSelectedElements.map((el) => ({
const { x, y } = getElement(element); id: el.id,
return { ...element, redo: { x, y } }; type: el.type,
}), undo: el.initialCoords,
redo: el.currentCoords,
})),
}, },
]); ]);
setRedoStack([]); setRedoStack([]);
setBulkSelectedElements((prev) =>
prev.map((el) => ({
...el,
initialCoords: { ...el.currentCoords },
})),
);
} }
setDragging(notDragging);
if (bulkSelectRectPts.show) { if (bulkSelectRectPts.show) {
setBulkSelectRectPts((prev) => ({ setBulkSelectRectPts((prev) => ({
@@ -464,10 +466,11 @@ export default function Canvas() {
y2: pointer.spaces.diagram.y, y2: pointer.spaces.diagram.y,
show: false, show: false,
})); }));
if (!coordinatesDidUpdate) { if (!isDragging()) {
collectSelectedElements(); collectSelectedElements();
} }
} }
setDragging(notDragging);
if (panning.isPanning && didPan()) { if (panning.isPanning && didPan()) {
setSaveState(State.SAVING); setSaveState(State.SAVING);
@@ -487,10 +490,10 @@ export default function Canvas() {
aid: areaResize.id, aid: areaResize.id,
undo: { undo: {
...areas[areaResize.id], ...areas[areaResize.id],
x: initCoords.x, x: areaInitDimensions.x,
y: initCoords.y, y: areaInitDimensions.y,
width: initCoords.width, width: areaInitDimensions.width,
height: initCoords.height, height: areaInitDimensions.height,
}, },
redo: areas[areaResize.id], redo: areas[areaResize.id],
message: t("edit_area", { message: t("edit_area", {
@@ -502,13 +505,11 @@ export default function Canvas() {
setRedoStack([]); setRedoStack([]);
} }
setAreaResize({ id: -1, dir: "none" }); setAreaResize({ id: -1, dir: "none" });
setInitCoords({ setAreaInitDimensions({
x: 0, x: 0,
y: 0, y: 0,
width: 0, width: 0,
height: 0, height: 0,
pointerX: 0,
pointerY: 0,
}); });
}; };
@@ -664,7 +665,7 @@ export default function Canvas() {
handlePointerDownOnElement(e, a.id, ObjectType.AREA) handlePointerDownOnElement(e, a.id, ObjectType.AREA)
} }
setResize={setAreaResize} setResize={setAreaResize}
setInitCoords={setInitCoords} setInitDimensions={setAreaInitDimensions}
/> />
))} ))}
{relationships.map((e, i) => ( {relationships.map((e, i) => (

View File

@@ -62,7 +62,6 @@ export default function TableInfo({ data }) {
}); });
setRedoStack([]); setRedoStack([]);
}; };
undefined;
const inheritedFieldNames = const inheritedFieldNames =
Array.isArray(data.inherits) && data.inherits.length > 0 Array.isArray(data.inherits) && data.inherits.length > 0