diff --git a/src/actions/actionDeleteSelected.tsx b/src/actions/actionDeleteSelected.tsx index dfba34df79..a1820a330b 100644 --- a/src/actions/actionDeleteSelected.tsx +++ b/src/actions/actionDeleteSelected.tsx @@ -72,13 +72,22 @@ export const actionDeleteSelected = register({ if (!element) { return false; } - if ( - // case: no point selected → delete whole element - selectedPointsIndices == null || - // case: deleting last remaining point - element.points.length < 2 - ) { - const nextElements = elements.filter((el) => el.id !== element.id); + // case: no point selected → do nothing, as deleting the whole element + // is most likely a mistake, where you wanted to delete a specific point + // but failed to select it (or you thought it's selected, while it was + // only in a hover state) + if (selectedPointsIndices == null) { + return false; + } + + // case: deleting last remaining point + if (element.points.length < 2) { + const nextElements = elements.map((el) => { + if (el.id === element.id) { + return newElementWith(el, { isDeleted: true }); + } + return el; + }); const nextAppState = handleGroupEditingState(appState, nextElements); return { diff --git a/src/actions/shortcuts.ts b/src/actions/shortcuts.ts index 4d53f47afa..2594c53b36 100644 --- a/src/actions/shortcuts.ts +++ b/src/actions/shortcuts.ts @@ -38,7 +38,7 @@ export type ShortcutName = | "imageExport"; const shortcutMap: Record = { - toggleTheme: [getShortcutKey("Shit+Alt+D")], + toggleTheme: [getShortcutKey("Shift+Alt+D")], saveScene: [getShortcutKey("CtrlOrCmd+S")], loadScene: [getShortcutKey("CtrlOrCmd+O")], imageExport: [getShortcutKey("CtrlOrCmd+Shift+E")], diff --git a/src/components/Actions.tsx b/src/components/Actions.tsx index 2da4a151d9..972c7ba8a4 100644 --- a/src/components/Actions.tsx +++ b/src/components/Actions.tsx @@ -237,7 +237,7 @@ export const ShapesSwitcher = ({ keyBindingLabel={`${numberKey}`} aria-label={capitalizeString(label)} aria-keyshortcuts={shortcut} - data-testid={value} + data-testid={`toolbar-${value}`} onPointerDown={({ pointerType }) => { if (!appState.penDetected && pointerType === "pen") { setAppState({ diff --git a/src/components/App.tsx b/src/components/App.tsx index 5d2dac5222..3d5e6a36dc 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -4836,10 +4836,6 @@ class App extends React.Component { } else { this.setState((prevState) => ({ draggingElement: null, - selectedElementIds: { - ...prevState.selectedElementIds, - [draggingElement.id]: true, - }, })); } } diff --git a/src/components/LayerUI.tsx b/src/components/LayerUI.tsx index 9057df24fe..d2aa27865b 100644 --- a/src/components/LayerUI.tsx +++ b/src/components/LayerUI.tsx @@ -213,7 +213,8 @@ const LayerUI = ({ padding={2} style={{ zIndex: 1 }} > - {actionManager.renderAction("loadScene")} + {!appState.viewModeEnabled && + actionManager.renderAction("loadScene")} {/* // TODO barnabasmolnar/editor-redesign */} {/* is this fine here? */} {UIOptions.canvasActions.saveToActiveFile && @@ -237,7 +238,8 @@ const LayerUI = ({ /> )} {actionManager.renderAction("toggleShortcuts", undefined, true)} - {actionManager.renderAction("clearCanvas")} + {!appState.viewModeEnabled && + actionManager.renderAction("clearCanvas")} @@ -252,14 +254,16 @@ const LayerUI = ({
-
-
- {t("labels.canvasBackground")} -
-
- {actionManager.renderAction("changeViewBackgroundColor")} + {!appState.viewModeEnabled && ( +
+
+ {t("labels.canvasBackground")} +
+
+ {actionManager.renderAction("changeViewBackgroundColor")} +
-
+ )}
@@ -302,12 +306,12 @@ const LayerUI = ({ return ( {renderWelcomeScreen && !appState.isLoading && ( - + )}
@@ -408,7 +412,9 @@ const LayerUI = ({ /> )} {renderTopRightUI?.(device.isMobile, appState)} - + {!appState.viewModeEnabled && ( + + )}
diff --git a/src/components/LockButton.tsx b/src/components/LockButton.tsx index bb5b6d9f0f..cbcf2b33a4 100644 --- a/src/components/LockButton.tsx +++ b/src/components/LockButton.tsx @@ -39,6 +39,7 @@ export const LockButton = (props: LockIconProps) => { onChange={props.onChange} checked={props.checked} aria-label={props.title} + data-testid="toolbar-lock" />
{props.checked ? ICONS.CHECKED : ICONS.UNCHECKED} diff --git a/src/components/MobileMenu.tsx b/src/components/MobileMenu.tsx index ac44ea3034..5848207382 100644 --- a/src/components/MobileMenu.tsx +++ b/src/components/MobileMenu.tsx @@ -75,7 +75,7 @@ export const MobileMenu = ({ return ( {renderWelcomeScreen && !appState.isLoading && ( - + )}
{(heading: React.ReactNode) => ( @@ -127,11 +127,13 @@ export const MobileMenu = ({ title={t("toolBar.lock")} isMobile /> - + {!appState.viewModeEnabled && ( + + )}
@@ -187,7 +189,7 @@ export const MobileMenu = ({ } return ( <> - {actionManager.renderAction("loadScene")} + {!appState.viewModeEnabled && actionManager.renderAction("loadScene")} {renderJSONExportDialog()} {renderImageExportDialog()} )} {actionManager.renderAction("toggleShortcuts", undefined, true)} - {actionManager.renderAction("clearCanvas")} + {!appState.viewModeEnabled && actionManager.renderAction("clearCanvas")} -
-
- {t("labels.canvasBackground")} -
-
- {actionManager.renderAction("changeViewBackgroundColor")} + {!appState.viewModeEnabled && ( +
+
+ {t("labels.canvasBackground")} +
+
+ {actionManager.renderAction("changeViewBackgroundColor")} +
-
+ )} {actionManager.renderAction("toggleTheme")} ); diff --git a/src/components/WelcomeScreen.tsx b/src/components/WelcomeScreen.tsx index 03eb05855c..6649346dfb 100644 --- a/src/components/WelcomeScreen.tsx +++ b/src/components/WelcomeScreen.tsx @@ -5,6 +5,7 @@ import { getShortcutFromShortcutName } from "../actions/shortcuts"; import { COOKIES } from "../constants"; import { collabDialogShownAtom } from "../excalidraw-app/collab/Collab"; import { t } from "../i18n"; +import { AppState } from "../types"; import { ExcalLogo, HelpIcon, @@ -60,7 +61,13 @@ const WelcomeScreenItem = ({ ); }; -const WelcomeScreen = ({ actionManager }: { actionManager: ActionManager }) => { +const WelcomeScreen = ({ + appState, + actionManager, +}: { + appState: AppState; + actionManager: ActionManager; +}) => { const [, setCollabDialogShown] = useAtom(collabDialogShownAtom); let subheadingJSX; @@ -68,12 +75,13 @@ const WelcomeScreen = ({ actionManager }: { actionManager: ActionManager }) => { if (isExcalidrawPlusSignedUser) { subheadingJSX = t("welcomeScreen.switchToPlusApp") .split(/(Excalidraw\+)/) - .map((bit) => { + .map((bit, idx) => { if (bit === "Excalidraw+") { return ( Excalidraw+ @@ -94,15 +102,17 @@ const WelcomeScreen = ({ actionManager }: { actionManager: ActionManager }) => { {subheadingJSX}
- actionManager.executeAction(actionLoadScene)} - shortcut={getShortcutFromShortcutName("loadScene")} - icon={LoadIcon} - /> + {!appState.viewModeEnabled && ( + actionManager.executeAction(actionLoadScene)} + shortcut={getShortcutFromShortcutName("loadScene")} + icon={LoadIcon} + /> + )} void) => { handler(event); }; - document.addEventListener("mousedown", listener); + + document.addEventListener("pointerdown", listener); document.addEventListener("touchstart", listener); return () => { - document.removeEventListener("mousedown", listener); + document.removeEventListener("pointerdown", listener); document.removeEventListener("touchstart", listener); }; }, diff --git a/src/tests/queries/toolQueries.ts b/src/tests/queries/toolQueries.ts index 1d680af66c..5b0ff937e6 100644 --- a/src/tests/queries/toolQueries.ts +++ b/src/tests/queries/toolQueries.ts @@ -1,6 +1,7 @@ import { queries, buildQueries } from "@testing-library/react"; const toolMap = { + lock: "lock", selection: "selection", rectangle: "rectangle", diamond: "diamond", @@ -15,7 +16,7 @@ export type ToolName = keyof typeof toolMap; const _getAllByToolName = (container: HTMLElement, tool: string) => { const toolTitle = toolMap[tool as ToolName]; - return queries.getAllByTestId(container, toolTitle); + return queries.getAllByTestId(container, `toolbar-${toolTitle}`); }; const getMultipleError = (_container: any, tool: any) => diff --git a/src/tests/selection.test.tsx b/src/tests/selection.test.tsx index e2bcd1dbef..6c6547c77f 100644 --- a/src/tests/selection.test.tsx +++ b/src/tests/selection.test.tsx @@ -11,7 +11,8 @@ import * as Renderer from "../renderer/renderScene"; import { KEYS } from "../keys"; import { reseed } from "../random"; import { API } from "./helpers/api"; -import { Keyboard, Pointer } from "./helpers/ui"; +import { Keyboard, Pointer, UI } from "./helpers/ui"; +import { SHAPES } from "../shapes"; // Unmount ReactDOM from root ReactDOM.unmountComponentAtNode(document.getElementById("root")!); @@ -380,3 +381,19 @@ describe("select single element on the scene", () => { h.elements.forEach((element) => expect(element).toMatchSnapshot()); }); }); + +describe("tool locking & selection", () => { + it("should not select newly created element while tool is locked", async () => { + await render(); + + UI.clickTool("lock"); + expect(h.state.activeTool.locked).toBe(true); + + for (const { value } of Object.values(SHAPES)) { + if (value !== "image" && value !== "selection") { + const element = UI.createElement(value); + expect(h.state.selectedElementIds[element.id]).not.toBe(true); + } + } + }); +});