Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"dev": "vite",
"build": "vite build",
"lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview"
"preview": "vite preview",
"test": "node --test"
},
"dependencies": {
"@dagrejs/dagre": "^3.1.1",
Expand Down
13 changes: 4 additions & 9 deletions src/components/EditorCanvas/Canvas.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { useTranslation } from "react-i18next";
import { useEventListener } from "usehooks-ts";
import { areFieldsCompatible, getTableHeight } from "../../utils/utils";
import { getRectFromEndpoints, isInsideRect } from "../../utils/rect";
import { getWheelPanDelta } from "../../utils/wheel";
import { State, noteWidth } from "../../data/constants";
import { nanoid } from "nanoid";

Expand Down Expand Up @@ -697,20 +698,14 @@ export default function Canvas() {
},
zoom: e.deltaY <= 0 ? prev.zoom * 1.05 : prev.zoom / 1.05,
}));
} else if (e.shiftKey) {
setTransform((prev) => ({
...prev,
pan: {
...prev.pan,
x: prev.pan.x + e.deltaY / prev.zoom,
},
}));
} else {
const delta = getWheelPanDelta(e);
setTransform((prev) => ({
...prev,
pan: {
...prev.pan,
y: prev.pan.y + e.deltaY / prev.zoom,
x: prev.pan.x + delta.x / prev.zoom,
y: prev.pan.y + delta.y / prev.zoom,
},
}));
}
Expand Down
17 changes: 17 additions & 0 deletions src/utils/wheel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* Resolve the canvas pan movement produced by a wheel event.
*
* Browsers disagree on how Shift + wheel is reported: Chromium on macOS
* translates the vertical wheel input into a horizontal event (deltaX with
* deltaY === 0) while other platforms keep the movement in deltaY. Native
* horizontal wheels and trackpads also report deltaX without Shift.
*
* @param {WheelEvent} wheelEvent
* @returns {{x: number, y: number}} Pan delta in diagram units before zoom scaling.
*/
export function getWheelPanDelta(wheelEvent) {
if (wheelEvent.shiftKey) {
return { x: wheelEvent.deltaX || wheelEvent.deltaY, y: 0 };
}
return { x: wheelEvent.deltaX, y: wheelEvent.deltaY };
}
40 changes: 40 additions & 0 deletions src/utils/wheel.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import assert from "node:assert/strict";
import { test } from "node:test";

import { getWheelPanDelta } from "./wheel.js";

test("Shift + wheel keeps vertical delta on browsers without axis translation", () => {
const delta = getWheelPanDelta({ shiftKey: true, deltaX: 0, deltaY: 120 });

assert.deepEqual(delta, { x: 120, y: 0 });
});

test("Shift + wheel uses deltaX on browsers that translate the axis", () => {
const delta = getWheelPanDelta({ shiftKey: true, deltaX: 120, deltaY: 0 });

assert.deepEqual(delta, { x: 120, y: 0 });
});

test("wheel pans horizontally when Shift is not held", () => {
const delta = getWheelPanDelta({ shiftKey: false, deltaX: 120, deltaY: 0 });

assert.deepEqual(delta, { x: 120, y: 0 });
});

test("wheel pans vertically when Shift is not held", () => {
const delta = getWheelPanDelta({ shiftKey: false, deltaX: 0, deltaY: 120 });

assert.deepEqual(delta, { x: 0, y: 120 });
});

test("wheel pans diagonally when both axes report movement", () => {
const delta = getWheelPanDelta({ shiftKey: false, deltaX: 40, deltaY: 80 });

assert.deepEqual(delta, { x: 40, y: 80 });
});

test("wheel with no movement produces no pan", () => {
const delta = getWheelPanDelta({ shiftKey: false, deltaX: 0, deltaY: 0 });

assert.deepEqual(delta, { x: 0, y: 0 });
});
Loading