diff --git a/src/components/App.tsx b/src/components/App.tsx index 83c9a0dd8317..4f41ff542659 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -2468,8 +2468,11 @@ class App extends React.Component { private setActiveTool = ( tool: - | { type: typeof SHAPES[number]["value"] | "eraser" | "hand" } - | { type: "custom"; customType: string }, + | { + type: typeof SHAPES[number]["value"] | "eraser" | "hand"; + locked?: boolean; + } + | { type: "custom"; customType: string; locked?: boolean }, ) => { const nextActiveTool = updateActiveTool(this.state, tool); if (nextActiveTool.type === "hand") { diff --git a/src/excalidraw-app/index.tsx b/src/excalidraw-app/index.tsx index c6b69713ce6e..d52b56747d27 100644 --- a/src/excalidraw-app/index.tsx +++ b/src/excalidraw-app/index.tsx @@ -1,6 +1,6 @@ import polyfill from "../polyfill"; import LanguageDetector from "i18next-browser-languagedetector"; -import { useEffect, useRef, useState } from "react"; +import { useCallback, useEffect, useRef, useState } from "react"; import { trackEvent } from "../analytics"; import { getDefaultAppState } from "../appState"; import { ErrorDialog } from "../components/ErrorDialog"; @@ -21,7 +21,13 @@ import { } from "../element/types"; import { useCallbackRefState } from "../hooks/useCallbackRefState"; import { t } from "../i18n"; -import { Excalidraw, defaultLang } from "../packages/excalidraw/index"; +import { + Excalidraw, + defaultLang, + Footer, + Button, + Sidebar, +} from "../packages/excalidraw/index"; import { AppState, LibraryItems, @@ -76,7 +82,6 @@ import { reconcileElements } from "./collab/reconciliation"; import { parseLibraryTokensFromUrl, useHandleLibrary } from "../data/library"; import { AppMainMenu } from "./components/AppMainMenu"; import { AppWelcomeScreen } from "./components/AppWelcomeScreen"; -import { AppFooter } from "./components/AppFooter"; import { atom, Provider, useAtom, useAtomValue } from "jotai"; import { useAtomWithInitialValue } from "../jotai"; import { appJotaiStore } from "./app-jotai"; @@ -600,6 +605,70 @@ const ExcalidrawWrapper = () => { const isOffline = useAtomValue(isOfflineAtom); + const [activeToolType, setActiveToolType] = useState(null); + const prevActiveTool = useRef<{ + type: string; + locked?: boolean; + prevLockState?: boolean; + }>(); + + const toggleCommentTool = useCallback(() => { + const nextType = + excalidrawAPI?.getAppState().activeTool?.customType === "comment" + ? "selection" + : "comment"; + excalidrawAPI?.setActiveTool( + nextType === "comment" + ? { + type: "custom", + customType: "comment", + locked: true, + } + : { type: "selection" }, + ); + }, [excalidrawAPI]); + + useEffect(() => { + if (excalidrawAPI) { + const unsubOnChange = excalidrawAPI.onChange((_, appState) => { + const type = appState.activeTool.customType || appState.activeTool.type; + if ( + prevActiveTool.current?.type === "comment" && + type !== "comment" && + !prevActiveTool.current?.prevLockState + ) { + excalidrawAPI.setActiveTool({ + ...appState.activeTool, + locked: false, + }); + } + setActiveToolType(type); + prevActiveTool.current = { + type, + locked: appState.activeTool.locked, + prevLockState: + prevActiveTool.current?.type !== "comment" + ? prevActiveTool.current?.locked + : prevActiveTool.current?.prevLockState, + }; + }); + + // on C keypress + const onKeyDown = (event: KeyboardEvent) => { + if (event.code === "KeyC") { + toggleCommentTool(); + } + }; + + window.addEventListener("keydown", onKeyDown); + + return () => { + window.removeEventListener("keydown", onKeyDown); + unsubOnChange(); + }; + } + }, [excalidrawAPI, toggleCommentTool]); + return (
{ isCollaborating={isCollaborating} /> - + test +
+
+ sidebar + +
+
{isCollaborating && isOffline && (
{t("alerts.collabOfflineWarning")} diff --git a/src/utils.ts b/src/utils.ts index b5a65cae211c..7b70b079fada 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -292,8 +292,11 @@ export const distance = (x: number, y: number) => Math.abs(x - y); export const updateActiveTool = ( appState: Pick, data: ( - | { type: typeof SHAPES[number]["value"] | "eraser" | "hand" } - | { type: "custom"; customType: string } + | { + type: typeof SHAPES[number]["value"] | "eraser" | "hand"; + locked?: boolean; + } + | { type: "custom"; customType: string; locked?: boolean } ) & { lastActiveToolBeforeEraser?: LastActiveTool }, ): AppState["activeTool"] => { if (data.type === "custom") { @@ -301,6 +304,7 @@ export const updateActiveTool = ( ...appState.activeTool, type: "custom", customType: data.customType, + locked: data.locked ?? appState.activeTool.locked, }; } @@ -312,6 +316,7 @@ export const updateActiveTool = ( : data.lastActiveToolBeforeEraser, type: data.type, customType: null, + locked: data.locked ?? appState.activeTool.locked, }; };