diff --git a/packages/excalidraw/appState.ts b/packages/excalidraw/appState.ts index 0a5eca937..acda8468b 100644 --- a/packages/excalidraw/appState.ts +++ b/packages/excalidraw/appState.ts @@ -121,7 +121,6 @@ export const getDefaultAppState = (): Omit< isCropping: false, croppingElementId: null, searchMatches: [], - showShapeSwitchPanel: false, }; }; @@ -246,7 +245,6 @@ const APP_STATE_STORAGE_CONF = (< isCropping: { browser: false, export: false, server: false }, croppingElementId: { browser: false, export: false, server: false }, searchMatches: { browser: false, export: false, server: false }, - showShapeSwitchPanel: { browser: false, export: false, server: false }, }); const _clearAppStateForStorage = < diff --git a/packages/excalidraw/components/App.tsx b/packages/excalidraw/components/App.tsx index 1107ae631..6f7e0ed82 100644 --- a/packages/excalidraw/components/App.tsx +++ b/packages/excalidraw/components/App.tsx @@ -391,7 +391,7 @@ import { getMinTextElementWidth, } from "../element/textMeasurements"; -import ShapeSwitch from "./ShapeSwitch"; +import ShapeSwitch, { shapeSwitchAtom } from "./ShapeSwitch"; import { activeConfirmDialogAtom } from "./ActiveConfirmDialog"; import BraveMeasureTextError from "./BraveMeasureTextError"; @@ -4113,47 +4113,25 @@ class App extends React.Component { selectedElements.length === 1 && (isGenericSwitchable || isLinearSwitchable) ) { - if (this.state.showShapeSwitchPanel && event.key === KEYS.ESCAPE) { - this.setState({ - showShapeSwitchPanel: false, - }); + if (event.key === KEYS.ESCAPE) { + editorJotaiStore.set(shapeSwitchAtom, null); + } else if (event.key === KEYS.SLASH || event.key === KEYS.TAB) { + editorJotaiStore.set(shapeSwitchAtom, "panel"); - return; - } - - if (event.key === KEYS.SLASH || event.key === KEYS.TAB) { - if (!this.state.showShapeSwitchPanel) { - flushSync(() => - this.setState({ - showShapeSwitchPanel: true, - }), + if (isGenericSwitchable) { + const index = ["rectangle", "diamond", "ellipse"].indexOf( + selectedElements[0].type, ); + const nextType = ["rectangle", "diamond", "ellipse"][ + (index + 1) % 3 + ] as ToolType; + this.setActiveTool({ type: nextType }); + } else if (isLinearSwitchable) { + const index = ["arrow", "line"].indexOf(selectedElements[0].type); + const nextType = ["arrow", "line"][(index + 1) % 2] as ToolType; + this.setActiveTool({ type: nextType }); } - - if (this.state.showShapeSwitchPanel) { - if (isGenericSwitchable) { - const index = ["rectangle", "diamond", "ellipse"].indexOf( - selectedElements[0].type, - ); - const nextType = ["rectangle", "diamond", "ellipse"][ - (index + 1) % 3 - ] as ToolType; - this.setActiveTool({ type: nextType }); - } else if (isLinearSwitchable) { - const index = ["arrow", "line"].indexOf( - selectedElements[0].type, - ); - const nextType = ["arrow", "line"][(index + 1) % 2] as ToolType; - this.setActiveTool({ type: nextType }); - } - } - - return; } - - this.setState({ - showShapeSwitchPanel: false, - }); } if ( @@ -4717,6 +4695,8 @@ class App extends React.Component { canvasOffsets: this.getEditorUIOffsets(), }); } + + editorJotaiStore.set(shapeSwitchAtom, "hint"); } this.flowChartCreator.clear(); @@ -8985,7 +8965,6 @@ class App extends React.Component { cursorButton: "up", snapLines: updateStable(prevState.snapLines, []), originSnapOffset: null, - showShapeSwitchPanel: false, })); this.lastPointerMoveCoords = null; diff --git a/packages/excalidraw/components/ShapeSwitch.tsx b/packages/excalidraw/components/ShapeSwitch.tsx index dd6343c7a..731d38e62 100644 --- a/packages/excalidraw/components/ShapeSwitch.tsx +++ b/packages/excalidraw/components/ShapeSwitch.tsx @@ -4,16 +4,13 @@ import clsx from "clsx"; import { pointFrom, pointRotateRads } from "@excalidraw/math"; +import { editorJotaiStore, atom } from "../editor-jotai"; + import { getElementAbsoluteCoords } from "../element"; import { sceneCoordsToViewportCoords } from "../utils"; import { getSelectedElements } from "../scene"; import { trackEvent } from "../analytics"; -import { - isArrowElement, - isGenericSwitchableElement, - isLinearElement, - isLinearSwitchableElement, -} from "../element/typeChecks"; +import { isArrowElement, isLinearElement } from "../element/typeChecks"; import { t } from "../i18n"; import "./ShapeSwitch.scss"; @@ -34,48 +31,29 @@ import type { ToolType } from "../types"; const GAP_HORIZONTAL = 8; const GAP_VERTICAL = 10; +export const shapeSwitchAtom = atom<"hint" | "panel" | null>(null); + const ShapeSwitch = ({ app }: { app: App }) => { + const shapeSwitchAtomValue = editorJotaiStore.get(shapeSwitchAtom); + + if (!shapeSwitchAtomValue) { + return null; + } + const selectedElements = getSelectedElements( app.scene.getNonDeletedElementsMap(), app.state, ); const firstElement = selectedElements[0]; - useEffect(() => { - return app.setState({ showShapeSwitchPanel: false }); - }, [app]); - - const isGeneric = firstElement && isGenericSwitchableElement(firstElement); - const isLinear = firstElement && isLinearSwitchableElement(firstElement); - const isGenericInChart = - isGeneric && - app.scene - .getNonDeletedElements() - .some( - (el) => - el.type === "arrow" && - (el.startBinding?.elementId === firstElement.id || - el.endBinding?.elementId === firstElement.id), - ); - const isLinearInChart = - isLinear && - isArrowElement(firstElement) && - (firstElement.startBinding || firstElement.endBinding); - if (firstElement && selectedElements.length === 1) { - if (isGeneric) { - return app.state.showShapeSwitchPanel ? ( - - ) : isGenericInChart ? ( - - ) : null; - } - if (isLinear) { - return app.state.showShapeSwitchPanel ? ( - - ) : isLinearInChart ? ( - - ) : null; + switch (shapeSwitchAtomValue) { + case "hint": + return ; + case "panel": + return ; + default: + return null; } } @@ -109,9 +87,14 @@ const Hint = ({ app, element }: { app: App; element: ExcalidrawElement }) => { hintRef.current?.classList.remove("animation"); }; - if (hintRef.current) { - hintRef.current.addEventListener("animationend", listener); + const hint = hintRef.current; + + if (hint) { + hint.addEventListener("animationend", listener); + editorJotaiStore.set(shapeSwitchAtom, null); } + + return () => hint?.removeEventListener("animationend", listener); }, [element.id]); return ( @@ -171,6 +154,12 @@ const Panel = ({ app, element }: { app: App; element: ExcalidrawElement }) => { ["ellipse", "4", EllipseIcon], ]; + useEffect(() => { + return () => { + editorJotaiStore.set(shapeSwitchAtom, null); + }; + }, []); + return (