feat: Scroll using PageUp and PageDown (#6038)

* feat: Scroll using PageUp and PageDown

* support x-axis via `shift` & enable in viewMode

* tweak test

Co-authored-by: dwelle <luzar.david@gmail.com>
This commit is contained in:
DanielJGeiger 2022-12-31 15:54:37 -06:00 committed by GitHub
parent c8370b394c
commit fdd8552637
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 92 additions and 7 deletions

View File

@ -2008,6 +2008,20 @@ class App extends React.Component<AppProps, AppState> {
return; return;
} }
if (event.key === KEYS.PAGE_UP || event.key === KEYS.PAGE_DOWN) {
let offset =
(event.shiftKey ? this.state.width : this.state.height) /
this.state.zoom.value;
if (event.key === KEYS.PAGE_DOWN) {
offset = -offset;
}
if (event.shiftKey) {
this.setState((state) => ({ scrollX: state.scrollX + offset }));
} else {
this.setState((state) => ({ scrollY: state.scrollY + offset }));
}
}
if (this.actionManager.handleKeyDown(event)) { if (this.actionManager.handleKeyDown(event)) {
return; return;
} }
@ -2030,12 +2044,6 @@ class App extends React.Component<AppProps, AppState> {
? ELEMENT_SHIFT_TRANSLATE_AMOUNT ? ELEMENT_SHIFT_TRANSLATE_AMOUNT
: ELEMENT_TRANSLATE_AMOUNT); : ELEMENT_TRANSLATE_AMOUNT);
const selectedElements = getSelectedElements(
this.scene.getNonDeletedElements(),
this.state,
true,
);
let offsetX = 0; let offsetX = 0;
let offsetY = 0; let offsetY = 0;
@ -2049,6 +2057,12 @@ class App extends React.Component<AppProps, AppState> {
offsetY = step; offsetY = step;
} }
const selectedElements = getSelectedElements(
this.scene.getNonDeletedElements(),
this.state,
true,
);
selectedElements.forEach((element) => { selectedElements.forEach((element) => {
mutateElement(element, { mutateElement(element, {
x: element.x + offsetX, x: element.x + offsetX,

View File

@ -230,6 +230,14 @@ export const HelpDialog = ({ onClose }: { onClose?: () => void }) => {
label={t("helpDialog.zoomToSelection")} label={t("helpDialog.zoomToSelection")}
shortcuts={["Shift+2"]} shortcuts={["Shift+2"]}
/> />
<Shortcut
label={t("helpDialog.movePageUpDown")}
shortcuts={["PgUp/PgDn"]}
/>
<Shortcut
label={t("helpDialog.movePageLeftRight")}
shortcuts={["Shift+PgUp/PgDn"]}
/>
<Shortcut label={t("buttons.fullScreen")} shortcuts={["F"]} /> <Shortcut label={t("buttons.fullScreen")} shortcuts={["F"]} />
<Shortcut <Shortcut
label={t("buttons.zenMode")} label={t("buttons.zenMode")}

View File

@ -29,6 +29,8 @@ export const KEYS = {
ARROW_LEFT: "ArrowLeft", ARROW_LEFT: "ArrowLeft",
ARROW_RIGHT: "ArrowRight", ARROW_RIGHT: "ArrowRight",
ARROW_UP: "ArrowUp", ARROW_UP: "ArrowUp",
PAGE_UP: "PageUp",
PAGE_DOWN: "PageDown",
BACKSPACE: "Backspace", BACKSPACE: "Backspace",
ALT: "Alt", ALT: "Alt",
CTRL_OR_CMD: isDarwin ? "metaKey" : "ctrlKey", CTRL_OR_CMD: isDarwin ? "metaKey" : "ctrlKey",

View File

@ -312,7 +312,9 @@
"view": "View", "view": "View",
"zoomToFit": "Zoom to fit all elements", "zoomToFit": "Zoom to fit all elements",
"zoomToSelection": "Zoom to selection", "zoomToSelection": "Zoom to selection",
"toggleElementLock": "Lock/unlock selection" "toggleElementLock": "Lock/unlock selection",
"movePageUpDown": "Move page up/down",
"movePageLeftRight": "Move page left/right"
}, },
"clearCanvasDialog": { "clearCanvasDialog": {
"title": "Clear canvas" "title": "Clear canvas"

View File

@ -6,6 +6,9 @@ import {
} from "./test-utils"; } from "./test-utils";
import { Excalidraw } from "../packages/excalidraw/index"; import { Excalidraw } from "../packages/excalidraw/index";
import { API } from "./helpers/api"; import { API } from "./helpers/api";
import { Keyboard } from "./helpers/ui";
import { KEYS } from "../keys";
import ExcalidrawApp from "../excalidraw-app";
const { h } = window; const { h } = window;
@ -50,4 +53,60 @@ describe("appState", () => {
}); });
restoreOriginalGetBoundingClientRect(); restoreOriginalGetBoundingClientRect();
}); });
it("moving by page up/down/left/right", async () => {
mockBoundingClientRect();
await render(<ExcalidrawApp />, {});
const scrollTest = () => {
const initialScrollY = h.state.scrollY;
const initialScrollX = h.state.scrollX;
const pageStepY = h.state.height / h.state.zoom.value;
const pageStepX = h.state.width / h.state.zoom.value;
// Assert the following assertions have meaning
expect(pageStepY).toBeGreaterThan(0);
expect(pageStepX).toBeGreaterThan(0);
// Assert we scroll up
Keyboard.keyPress(KEYS.PAGE_UP);
expect(h.state.scrollY).toBe(initialScrollY + pageStepY);
// x-axis unchanged
expect(h.state.scrollX).toBe(initialScrollX);
// Assert we scroll down
Keyboard.keyPress(KEYS.PAGE_DOWN);
Keyboard.keyPress(KEYS.PAGE_DOWN);
expect(h.state.scrollY).toBe(initialScrollY - pageStepY);
// x-axis unchanged
expect(h.state.scrollX).toBe(initialScrollX);
// Assert we scroll left
Keyboard.withModifierKeys({ shift: true }, () => {
Keyboard.keyPress(KEYS.PAGE_UP);
});
expect(h.state.scrollX).toBe(initialScrollX + pageStepX);
// y-axis unchanged
expect(h.state.scrollY).toBe(initialScrollY - pageStepY);
// Assert we scroll right
Keyboard.withModifierKeys({ shift: true }, () => {
Keyboard.keyPress(KEYS.PAGE_DOWN);
Keyboard.keyPress(KEYS.PAGE_DOWN);
});
expect(h.state.scrollX).toBe(initialScrollX - pageStepX);
// y-axis unchanged
expect(h.state.scrollY).toBe(initialScrollY - pageStepY);
};
const zoom = h.state.zoom.value;
// Assert we scroll properly when zoomed in
h.setState({ zoom: { value: (zoom * 1.1) as typeof zoom } });
scrollTest();
// Assert we scroll properly when zoomed out
h.setState({ zoom: { value: (zoom * 0.9) as typeof zoom } });
scrollTest();
// Assert we scroll properly with normal zoom
h.setState({ zoom: { value: zoom } });
scrollTest();
restoreOriginalGetBoundingClientRect();
});
}); });