Panning with middle click, persistent bulk selection (#498)

* panning with middle click, persistant bulk selection

* remove unused variables
This commit is contained in:
Karen Mkrtumyan
2025-06-21 19:48:15 +04:00
committed by GitHub
parent e56f6409ff
commit 3cdf908ffd
3 changed files with 47 additions and 81 deletions

View File

@@ -5,8 +5,6 @@ import {
Constraint, Constraint,
darkBgTheme, darkBgTheme,
ObjectType, ObjectType,
tableFieldHeight,
tableHeaderHeight,
gridSize, gridSize,
gridCircleRadius, gridCircleRadius,
} from "../../data/constants"; } from "../../data/constants";
@@ -29,7 +27,7 @@ import {
} from "../../hooks"; } from "../../hooks";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useEventListener } from "usehooks-ts"; import { useEventListener } from "usehooks-ts";
import { areFieldsCompatible } from "../../utils/utils"; import { areFieldsCompatible, getTableHeight } from "../../utils/utils";
import { getRectFromEndpoints, isInsideRect } from "../../utils/rect"; import { getRectFromEndpoints, isInsideRect } from "../../utils/rect";
import { noteWidth, State } from "../../data/constants"; import { noteWidth, State } from "../../data/constants";
@@ -117,8 +115,7 @@ export default function Canvas() {
x: table.x, x: table.x,
y: table.y, y: table.y,
width: settings.tableWidth, width: settings.tableWidth,
height: height: getTableHeight(table),
table.fields.length * tableFieldHeight + tableHeaderHeight + 7,
}, },
rect, rect,
) )
@@ -230,14 +227,7 @@ export default function Canvas() {
prevCoords = { prevX: note.x, prevY: note.y }; prevCoords = { prevX: note.x, prevY: note.y };
} }
if (locked) { if (!locked) {
setPanning({
isPanning: true,
panStart: transform.pan,
cursorStart: pointer.spaces.screen,
});
pointer.setStyle("grabbing");
} else {
setDragging((prev) => ({ setDragging((prev) => ({
...prev, ...prev,
id, id,
@@ -274,6 +264,21 @@ export default function Canvas() {
if (!e.isPrimary) return; if (!e.isPrimary) return;
if (panning.isPanning) {
setTransform((prev) => ({
...prev,
pan: {
x:
panning.panStart.x +
(panning.cursorStart.x - pointer.spaces.screen.x) / transform.zoom,
y:
panning.panStart.y +
(panning.cursorStart.y - pointer.spaces.screen.y) / transform.zoom,
},
}));
return;
}
const isDragging = const isDragging =
dragging.element !== ObjectType.NONE && dragging.id !== null; dragging.element !== ObjectType.NONE && dragging.id !== null;
@@ -300,26 +305,38 @@ export default function Canvas() {
} else if ( } else if (
dragging.element !== ObjectType.NONE && dragging.element !== ObjectType.NONE &&
dragging.id !== null && dragging.id !== null &&
bulkSelectedElements.length bulkSelectedElements.length &&
bulkSelectedElements.some(
(element) =>
element.id === dragging.id && element.type === dragging.element,
)
) { ) {
for (const element of bulkSelectedElements) { for (const element of bulkSelectedElements) {
if (element.type === ObjectType.TABLE) { if (element.type === ObjectType.TABLE) {
const { x, y } = tables.find((e) => e.id === element.id); const table = tables.find((e) => e.id === element.id);
if (table.locked) continue;
const { x, y } = table;
updateTable(element.id, { updateTable(element.id, {
x: x + deltaX, x: x + deltaX,
y: y + deltaY, y: y + deltaY,
}); });
} }
if (element.type === ObjectType.AREA) { if (element.type === ObjectType.AREA) {
const area = areas[element.id];
if (area.locked) continue;
const { x, y } = area;
updateArea(element.id, { updateArea(element.id, {
x: areas[element.id].x + deltaX, x: x + deltaX,
y: areas[element.id].y + deltaY, y: y + deltaY,
}); });
} }
if (element.type === ObjectType.NOTE) { if (element.type === ObjectType.NOTE) {
const note = notes[element.id];
if (note.locked) continue;
const { x, y } = note;
updateNote(element.id, { updateNote(element.id, {
x: notes[element.id].x + deltaX, x: x + deltaX,
y: notes[element.id].y + deltaY, y: y + deltaY,
}); });
} }
} }
@@ -329,25 +346,6 @@ export default function Canvas() {
prevX: finalX, prevX: finalX,
prevY: finalY, prevY: finalY,
})); }));
} else if (
panning.isPanning &&
dragging.element === ObjectType.NONE &&
areaResize.id === -1
) {
if (!settings.panning) {
return;
}
setTransform((prev) => ({
...prev,
pan: {
x:
panning.panStart.x +
(panning.cursorStart.x - pointer.spaces.screen.x) / transform.zoom,
y:
panning.panStart.y +
(panning.cursorStart.y - pointer.spaces.screen.y) / transform.zoom,
},
}));
} else if (dragging.element === ObjectType.TABLE && dragging.id !== null) { } else if (dragging.element === ObjectType.TABLE && dragging.id !== null) {
const table = tables.find((t) => t.id === dragging.id); const table = tables.find((t) => t.id === dragging.id);
if (table.locked) return; if (table.locked) return;
@@ -430,7 +428,10 @@ export default function Canvas() {
) )
return; return;
if (!settings.panning) { const isMouseLeftButton = e.button === 0;
const isMouseMiddleButton = e.button === 1;
if (isMouseLeftButton) {
setBulkSelectRectPts({ setBulkSelectRectPts({
x1: pointer.spaces.diagram.x, x1: pointer.spaces.diagram.x,
y1: pointer.spaces.diagram.y, y1: pointer.spaces.diagram.y,
@@ -439,7 +440,7 @@ export default function Canvas() {
show: true, show: true,
}); });
pointer.setStyle("crosshair"); pointer.setStyle("crosshair");
} else { } else if (isMouseMiddleButton) {
setPanning({ setPanning({
isPanning: true, isPanning: true,
panStart: transform.pan, panStart: transform.pan,
@@ -488,6 +489,8 @@ export default function Canvas() {
if (!e.isPrimary) return; if (!e.isPrimary) return;
let bulkMoved = false;
if (coordsDidUpdate({ id: dragging.id, type: dragging.element })) { if (coordsDidUpdate({ id: dragging.id, type: dragging.element })) {
if (bulkSelectedElements.length) { if (bulkSelectedElements.length) {
setUndoStack((prev) => [ setUndoStack((prev) => [
@@ -505,12 +508,7 @@ export default function Canvas() {
})), })),
}, },
]); ]);
setSelectedElement((prev) => ({ bulkMoved = true;
...prev,
element: ObjectType.NONE,
id: -1,
open: false,
}));
} else { } else {
const element = getElement({ const element = getElement({
id: dragging.id, id: dragging.id,
@@ -553,18 +551,13 @@ export default function Canvas() {
y2: pointer.spaces.diagram.y, y2: pointer.spaces.diagram.y,
show: false, show: false,
})); }));
collectSelectedElements(); if (!bulkMoved) {
collectSelectedElements();
}
} }
if (panning.isPanning && didPan()) { if (panning.isPanning && didPan()) {
setSaveState(State.SAVING); setSaveState(State.SAVING);
setSelectedElement((prev) => ({
...prev,
element: ObjectType.NONE,
id: -1,
open: false,
}));
setBulkSelectedElements([]);
} }
setPanning((old) => ({ ...old, isPanning: false })); setPanning((old) => ({ ...old, isPanning: false }));
pointer.setStyle("default"); pointer.setStyle("default");

View File

@@ -1396,15 +1396,6 @@ export default function ControlPanel({
function: () => function: () =>
setSettings((prev) => ({ ...prev, autosave: !prev.autosave })), setSettings((prev) => ({ ...prev, autosave: !prev.autosave })),
}, },
panning: {
state: settings.panning ? (
<i className="bi bi-toggle-on" />
) : (
<i className="bi bi-toggle-off" />
),
function: () =>
setSettings((prev) => ({ ...prev, panning: !prev.panning })),
},
table_width: { table_width: {
function: () => setModal(MODAL.TABLE_WIDTH), function: () => setModal(MODAL.TABLE_WIDTH),
}, },
@@ -1603,23 +1594,6 @@ export default function ControlPanel({
<i className="fa-solid fa-magnifying-glass-minus" /> <i className="fa-solid fa-magnifying-glass-minus" />
</button> </button>
</Tooltip> </Tooltip>
<Tooltip
content={settings.panning ? t("multiselect") : t("panning")}
position="bottom"
>
<button
className="py-1 px-2 hover-2 rounded-sm text-lg w-10"
onClick={() =>
setSettings((prev) => ({ ...prev, panning: !prev.panning }))
}
>
{settings.panning ? (
<i className="fa-solid fa-expand" />
) : (
<i className="fa-regular fa-hand"></i>
)}
</button>
</Tooltip>
<Divider layout="vertical" margin="8px" /> <Divider layout="vertical" margin="8px" />
<Tooltip content={t("undo")} position="bottom"> <Tooltip content={t("undo")} position="bottom">
<button <button

View File

@@ -9,7 +9,6 @@ const defaultSettings = {
showDataTypes: true, showDataTypes: true,
mode: "light", mode: "light",
autosave: true, autosave: true,
panning: true,
showCardinality: true, showCardinality: true,
showRelationshipLabels: true, showRelationshipLabels: true,
tableWidth: tableWidth, tableWidth: tableWidth,