From 2a826f8162797defdccdd9939af10945ca61b858 Mon Sep 17 00:00:00 2001 From: Ryan Di Date: Fri, 21 Mar 2025 12:58:34 +1100 Subject: [PATCH] fix panel state --- packages/excalidraw/components/App.tsx | 32 ++++---- .../excalidraw/components/ShapeSwitch.tsx | 80 ++++++++++++------- 2 files changed, 71 insertions(+), 41 deletions(-) diff --git a/packages/excalidraw/components/App.tsx b/packages/excalidraw/components/App.tsx index 6f7e0ed82..15b3617d7 100644 --- a/packages/excalidraw/components/App.tsx +++ b/packages/excalidraw/components/App.tsx @@ -4116,21 +4116,25 @@ class App extends React.Component { if (event.key === KEYS.ESCAPE) { editorJotaiStore.set(shapeSwitchAtom, null); } else if (event.key === KEYS.SLASH || event.key === KEYS.TAB) { - editorJotaiStore.set(shapeSwitchAtom, "panel"); - - 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 (editorJotaiStore.get(shapeSwitchAtom) === "panel") { + 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 }); + } } + + editorJotaiStore.set(shapeSwitchAtom, "panel"); } } diff --git a/packages/excalidraw/components/ShapeSwitch.tsx b/packages/excalidraw/components/ShapeSwitch.tsx index 731d38e62..75dbe1607 100644 --- a/packages/excalidraw/components/ShapeSwitch.tsx +++ b/packages/excalidraw/components/ShapeSwitch.tsx @@ -4,7 +4,7 @@ import clsx from "clsx"; import { pointFrom, pointRotateRads } from "@excalidraw/math"; -import { editorJotaiStore, atom } from "../editor-jotai"; +import { atom, useAtom } from "../editor-jotai"; import { getElementAbsoluteCoords } from "../element"; import { sceneCoordsToViewportCoords } from "../utils"; @@ -34,9 +34,9 @@ const GAP_VERTICAL = 10; export const shapeSwitchAtom = atom<"hint" | "panel" | null>(null); const ShapeSwitch = ({ app }: { app: App }) => { - const shapeSwitchAtomValue = editorJotaiStore.get(shapeSwitchAtom); + const [shapeSwitch, setShapeSwitch] = useAtom(shapeSwitchAtom); - if (!shapeSwitchAtomValue) { + if (!shapeSwitch) { return null; } @@ -47,7 +47,7 @@ const ShapeSwitch = ({ app }: { app: App }) => { const firstElement = selectedElements[0]; if (firstElement && selectedElements.length === 1) { - switch (shapeSwitchAtomValue) { + switch (shapeSwitch) { case "hint": return ; case "panel": @@ -57,10 +57,43 @@ const ShapeSwitch = ({ app }: { app: App }) => { } } + setShapeSwitch(null); return null; }; const Hint = ({ app, element }: { app: App; element: ExcalidrawElement }) => { + const [, setShapeSwitch] = useAtom(shapeSwitchAtom); + const hintRef = useRef(null); + const initialElementRef = useRef(element); + + useEffect(() => { + if (element !== initialElementRef.current) { + setShapeSwitch(null); + } + }, [element, setShapeSwitch]); + + useEffect(() => { + const hint = hintRef.current; + if (!hint) { + return; + } + + const handleAnimationEnd = () => { + hint.classList.remove("animation"); + setShapeSwitch(null); + }; + + hint.addEventListener("animationend", handleAnimationEnd, { once: true }); + + return () => { + hint.removeEventListener("animationend", handleAnimationEnd); + }; + }, [setShapeSwitch]); + + if (element !== initialElementRef.current) { + return null; + } + const [x1, y1, , , cx, cy] = getElementAbsoluteCoords( element, app.scene.getNonDeletedElementsMap(), @@ -80,23 +113,6 @@ const Hint = ({ app, element }: { app: App; element: ExcalidrawElement }) => { app.state, ); - const hintRef = useRef(null); - - useEffect(() => { - const listener = () => { - hintRef.current?.classList.remove("animation"); - }; - - const hint = hintRef.current; - - if (hint) { - hint.addEventListener("animationend", listener); - editorJotaiStore.set(shapeSwitchAtom, null); - } - - return () => hint?.removeEventListener("animationend", listener); - }, [element.id]); - return (
{ }; const Panel = ({ app, element }: { app: App; element: ExcalidrawElement }) => { + const [shapeSwitch, setShapeSwitch] = useAtom(shapeSwitchAtom); + + const isMounted = useRef(true); + useEffect(() => { + isMounted.current = true; + + return () => { + isMounted.current = false; + requestAnimationFrame(() => { + if (!isMounted.current && shapeSwitch === "panel") { + setShapeSwitch(null); + } + }); + }; + }, [shapeSwitch, setShapeSwitch]); + const [x1, , , y2, cx, cy] = getElementAbsoluteCoords( element, app.scene.getNonDeletedElementsMap(), @@ -154,12 +186,6 @@ const Panel = ({ app, element }: { app: App; element: ExcalidrawElement }) => { ["ellipse", "4", EllipseIcon], ]; - useEffect(() => { - return () => { - editorJotaiStore.set(shapeSwitchAtom, null); - }; - }, []); - return (