Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5542e4528a | |||
| fdd8552637 | |||
| c8370b394c | |||
| 5fcf6a4845 |
+20
-6
@@ -2008,6 +2008,20 @@ class App extends React.Component<AppProps, AppState> {
|
||||
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)) {
|
||||
return;
|
||||
}
|
||||
@@ -2030,12 +2044,6 @@ class App extends React.Component<AppProps, AppState> {
|
||||
? ELEMENT_SHIFT_TRANSLATE_AMOUNT
|
||||
: ELEMENT_TRANSLATE_AMOUNT);
|
||||
|
||||
const selectedElements = getSelectedElements(
|
||||
this.scene.getNonDeletedElements(),
|
||||
this.state,
|
||||
true,
|
||||
);
|
||||
|
||||
let offsetX = 0;
|
||||
let offsetY = 0;
|
||||
|
||||
@@ -2049,6 +2057,12 @@ class App extends React.Component<AppProps, AppState> {
|
||||
offsetY = step;
|
||||
}
|
||||
|
||||
const selectedElements = getSelectedElements(
|
||||
this.scene.getNonDeletedElements(),
|
||||
this.state,
|
||||
true,
|
||||
);
|
||||
|
||||
selectedElements.forEach((element) => {
|
||||
mutateElement(element, {
|
||||
x: element.x + offsetX,
|
||||
|
||||
@@ -230,6 +230,14 @@ export const HelpDialog = ({ onClose }: { onClose?: () => void }) => {
|
||||
label={t("helpDialog.zoomToSelection")}
|
||||
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.zenMode")}
|
||||
|
||||
@@ -325,8 +325,6 @@ export const textWysiwyg = ({
|
||||
whiteSpace = "pre-wrap";
|
||||
wordBreak = "break-word";
|
||||
}
|
||||
const isContainerArrow = isArrowElement(getContainerElement(element));
|
||||
const background = isContainerArrow ? "#fff" : "transparent";
|
||||
Object.assign(editable.style, {
|
||||
position: "absolute",
|
||||
display: "inline-block",
|
||||
@@ -337,7 +335,7 @@ export const textWysiwyg = ({
|
||||
border: 0,
|
||||
outline: 0,
|
||||
resize: "none",
|
||||
background,
|
||||
background: "transparent",
|
||||
overflow: "hidden",
|
||||
// must be specified because in dark mode canvas creates a stacking context
|
||||
zIndex: "var(--zIndex-wysiwyg)",
|
||||
|
||||
@@ -46,12 +46,15 @@ class LocalFileManager extends FileManager {
|
||||
const saveDataStateToLocalStorage = (
|
||||
elements: readonly ExcalidrawElement[],
|
||||
appState: AppState,
|
||||
appStateOnly = false,
|
||||
) => {
|
||||
try {
|
||||
localStorage.setItem(
|
||||
STORAGE_KEYS.LOCAL_STORAGE_ELEMENTS,
|
||||
JSON.stringify(clearElementsForLocalStorage(elements)),
|
||||
);
|
||||
if (!appStateOnly) {
|
||||
localStorage.setItem(
|
||||
STORAGE_KEYS.LOCAL_STORAGE_ELEMENTS,
|
||||
JSON.stringify(clearElementsForLocalStorage(elements)),
|
||||
);
|
||||
}
|
||||
localStorage.setItem(
|
||||
STORAGE_KEYS.LOCAL_STORAGE_APP_STATE,
|
||||
JSON.stringify(clearAppStateForLocalStorage(appState)),
|
||||
@@ -72,8 +75,12 @@ export class LocalData {
|
||||
appState: AppState,
|
||||
files: BinaryFiles,
|
||||
onFilesSaved: () => void,
|
||||
appStateOnly = false,
|
||||
) => {
|
||||
saveDataStateToLocalStorage(elements, appState);
|
||||
saveDataStateToLocalStorage(elements, appState, appStateOnly);
|
||||
if (appStateOnly) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.fileStorage.saveFiles({
|
||||
elements,
|
||||
@@ -97,6 +104,14 @@ export class LocalData {
|
||||
}
|
||||
};
|
||||
|
||||
/** Saves the AppState, only if saving is paused. */
|
||||
static saveAppState = (appState: AppState) => {
|
||||
// we need to make the `isSavePaused` check synchronously (undebounced)
|
||||
if (this.isSavePaused()) {
|
||||
this._save([], appState, {}, () => {}, true);
|
||||
}
|
||||
};
|
||||
|
||||
static flushSave = () => {
|
||||
this._save.flush();
|
||||
};
|
||||
|
||||
@@ -189,7 +189,7 @@ const initializeScene = async (opts: {
|
||||
...restoreAppState(
|
||||
{
|
||||
...scene?.appState,
|
||||
theme: localDataState?.appState?.theme || scene?.appState?.theme,
|
||||
...localDataState?.appState,
|
||||
},
|
||||
excalidrawAPI.getAppState(),
|
||||
),
|
||||
@@ -538,6 +538,8 @@ const ExcalidrawWrapper = () => {
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
LocalData.saveAppState(appState);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -29,6 +29,8 @@ export const KEYS = {
|
||||
ARROW_LEFT: "ArrowLeft",
|
||||
ARROW_RIGHT: "ArrowRight",
|
||||
ARROW_UP: "ArrowUp",
|
||||
PAGE_UP: "PageUp",
|
||||
PAGE_DOWN: "PageDown",
|
||||
BACKSPACE: "Backspace",
|
||||
ALT: "Alt",
|
||||
CTRL_OR_CMD: isDarwin ? "metaKey" : "ctrlKey",
|
||||
|
||||
+3
-1
@@ -312,7 +312,9 @@
|
||||
"view": "View",
|
||||
"zoomToFit": "Zoom to fit all elements",
|
||||
"zoomToSelection": "Zoom to selection",
|
||||
"toggleElementLock": "Lock/unlock selection"
|
||||
"toggleElementLock": "Lock/unlock selection",
|
||||
"movePageUpDown": "Move page up/down",
|
||||
"movePageLeftRight": "Move page left/right"
|
||||
},
|
||||
"clearCanvasDialog": {
|
||||
"title": "Clear canvas"
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Test Linear Elements Test bound text element should match styles for text editor 1`] = `
|
||||
<textarea
|
||||
class="excalidraw-wysiwyg"
|
||||
data-type="wysiwyg"
|
||||
dir="auto"
|
||||
style="position: absolute; display: inline-block; min-height: 1em; margin: 0px; padding: 0px; border: 0px; outline: 0; resize: none; background: transparent; overflow: hidden; z-index: var(--zIndex-wysiwyg); word-break: break-word; white-space: pre-wrap; overflow-wrap: break-word; box-sizing: content-box; width: 0px; height: 0px; left: 40px; top: 20px; transform: translate(0px, 0px) scale(1) rotate(0deg); text-align: center; vertical-align: middle; color: rgb(0, 0, 0); opacity: 1; filter: var(--theme-filter); max-height: -20px; font: Emoji 20px 20px; line-height: 0px; font-family: Virgil, Segoe UI Emoji;"
|
||||
tabindex="0"
|
||||
wrap="off"
|
||||
/>
|
||||
`;
|
||||
@@ -829,6 +829,15 @@ describe("Test Linear Elements", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("should match styles for text editor", () => {
|
||||
createTwoPointerLinearElement("arrow");
|
||||
Keyboard.keyPress(KEYS.ENTER);
|
||||
const editor = document.querySelector(
|
||||
".excalidraw-textEditorContainer > textarea",
|
||||
) as HTMLTextAreaElement;
|
||||
expect(editor).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should bind text to arrow when double clicked", async () => {
|
||||
createTwoPointerLinearElement("arrow");
|
||||
const arrow = h.elements[0] as ExcalidrawLinearElement;
|
||||
|
||||
@@ -6,6 +6,9 @@ import {
|
||||
} from "./test-utils";
|
||||
import { Excalidraw } from "../packages/excalidraw/index";
|
||||
import { API } from "./helpers/api";
|
||||
import { Keyboard } from "./helpers/ui";
|
||||
import { KEYS } from "../keys";
|
||||
import ExcalidrawApp from "../excalidraw-app";
|
||||
|
||||
const { h } = window;
|
||||
|
||||
@@ -50,4 +53,60 @@ describe("appState", () => {
|
||||
});
|
||||
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();
|
||||
});
|
||||
});
|
||||
|
||||
+3
-2
@@ -701,10 +701,11 @@ export const ReactChildrenToObject = <
|
||||
if (
|
||||
React.isValidElement(child) &&
|
||||
typeof child.type !== "string" &&
|
||||
child?.type.name
|
||||
//@ts-ignore
|
||||
child?.type.displayName
|
||||
) {
|
||||
// @ts-ignore
|
||||
acc[child.type.name] = child;
|
||||
acc[child.type.displayName] = child;
|
||||
}
|
||||
return acc;
|
||||
}, {} as Partial<T>);
|
||||
|
||||
Reference in New Issue
Block a user