fix popup component lifecycle

This commit is contained in:
dwelle 2025-04-30 13:50:46 +02:00
parent 86281329b5
commit 865328457f
5 changed files with 43 additions and 33 deletions

View file

@ -25,8 +25,6 @@ export const actionToggleShapeSwitch = register({
});
return {
appState,
commitToHistory: false,
captureUpdate: CaptureUpdateAction.NEVER,
};
},

View file

@ -431,7 +431,7 @@ import {
} from "../components/hyperlink/Hyperlink";
import { Fonts } from "../fonts";
import { editorJotaiStore } from "../editor-jotai";
import { editorJotaiStore, type WritableAtom } from "../editor-jotai";
import { ImageSceneDataError } from "../errors";
import {
getSnapLinesAtPointer,
@ -821,6 +821,15 @@ class App extends React.Component<AppProps, AppState> {
);
}
updateEditorAtom = <Value, Args extends unknown[], Result>(
atom: WritableAtom<Value, Args, Result>,
...args: Args
): Result => {
const result = editorJotaiStore.set(atom, ...args);
this.triggerRender();
return result;
};
private onWindowMessage(event: MessageEvent) {
if (
event.origin !== "https://player.vimeo.com" &&
@ -2148,7 +2157,7 @@ class App extends React.Component<AppProps, AppState> {
};
private openEyeDropper = ({ type }: { type: "stroke" | "background" }) => {
editorJotaiStore.set(activeEyeDropperAtom, {
this.updateEditorAtom(activeEyeDropperAtom, {
swapPreviewOnAlt: true,
colorPickerType:
type === "stroke" ? "elementStroke" : "elementBackground",
@ -4169,7 +4178,7 @@ class App extends React.Component<AppProps, AppState> {
// Shape switching
if (event.key === KEYS.ESCAPE) {
editorJotaiStore.set(shapeSwitchAtom, null);
this.updateEditorAtom(shapeSwitchAtom, null);
} else if (
event.key === KEYS.TAB &&
(document.activeElement === this.excalidrawContainerRef?.current ||
@ -4193,10 +4202,9 @@ class App extends React.Component<AppProps, AppState> {
}
}
if (switchCategory) {
editorJotaiStore.set(shapeSwitchAtom, {
this.updateEditorAtom(shapeSwitchAtom, {
type: "panel",
});
this.triggerRender();
}
}
@ -4658,7 +4666,7 @@ class App extends React.Component<AppProps, AppState> {
event[KEYS.CTRL_OR_CMD] &&
(event.key === KEYS.BACKSPACE || event.key === KEYS.DELETE)
) {
editorJotaiStore.set(activeConfirmDialogAtom, "clearCanvas");
this.updateEditorAtom(activeConfirmDialogAtom, "clearCanvas");
}
// eye dropper
@ -6407,11 +6415,11 @@ class App extends React.Component<AppProps, AppState> {
focus: false,
})),
}));
editorJotaiStore.set(searchItemInFocusAtom, null);
this.updateEditorAtom(searchItemInFocusAtom, null);
}
if (editorJotaiStore.get(shapeSwitchAtom)) {
editorJotaiStore.set(shapeSwitchAtom, null);
this.updateEditorAtom(shapeSwitchAtom, null);
}
// since contextMenu options are potentially evaluated on each render,

View file

@ -68,7 +68,7 @@ import {
sceneCoordsToViewportCoords,
} from "..";
import { trackEvent } from "../analytics";
import { atom, editorJotaiStore, useAtom } from "../editor-jotai";
import { atom, editorJotaiStore, useSetAtom } from "../editor-jotai";
import "./ShapeSwitch.scss";
import { ToolButton } from "./ToolButton";
@ -94,6 +94,7 @@ export const shapeSwitchAtom = atom<{
type: "panel";
} | null>(null);
// NOTE doesn't need to be an atom. Review once we integrate with properties panel.
export const shapeSwitchFontSizeAtom = atom<{
[id: string]: {
fontSize: number;
@ -101,6 +102,7 @@ export const shapeSwitchFontSizeAtom = atom<{
};
} | null>(null);
// NOTE doesn't need to be an atom. Review once we integrate with properties panel.
export const shapeSwitchLinearAtom = atom<{
[id: string]: {
properties:
@ -111,18 +113,19 @@ export const shapeSwitchLinearAtom = atom<{
} | null>(null);
const ShapeSwitch = ({ app }: { app: App }) => {
const [shapeSwitch, setShapeSwitch] = useAtom(shapeSwitchAtom);
const [, setShapeSwitchFontSize] = useAtom(shapeSwitchFontSizeAtom);
const [, setShapeSwitchLinear] = useAtom(shapeSwitchLinearAtom);
const setShapeSwitchFontSize = useSetAtom(shapeSwitchFontSizeAtom);
const setShapeSwitchLinear = useSetAtom(shapeSwitchLinearAtom);
const selectedElements = useMemo(
() => app.scene.getSelectedElements(app.state),
[app.scene, app.state],
);
const selectedElements = app.scene.getSelectedElements(app.state);
const elementsCategoryRef = useRef<SwitchShapeCategory>(null);
// close shape switch panel if selecting different "types" of elements
useEffect(() => {
if (selectedElements.length === 0) {
app.updateEditorAtom(shapeSwitchAtom, null);
return;
}
const switchCategory = getSwitchCategoryFromElements(selectedElements);
if (switchCategory && !elementsCategoryRef.current) {
@ -132,22 +135,17 @@ const ShapeSwitch = ({ app }: { app: App }) => {
(elementsCategoryRef.current &&
switchCategory !== elementsCategoryRef.current)
) {
setShapeSwitch(null);
app.updateEditorAtom(shapeSwitchAtom, null);
elementsCategoryRef.current = null;
}
}, [selectedElements, app.state.selectedElementIds, setShapeSwitch]);
}, [selectedElements, app]);
// clear if not active
if (!shapeSwitch) {
setShapeSwitchFontSize(null);
setShapeSwitchLinear(null);
return null;
}
if (selectedElements.length === 0) {
setShapeSwitch(null);
return null;
}
useEffect(() => {
return () => {
setShapeSwitchFontSize(null);
setShapeSwitchLinear(null);
};
}, [setShapeSwitchFontSize, setShapeSwitchLinear]);
return <Panel app={app} elements={selectedElements} />;
};

View file

@ -1,10 +1,15 @@
// eslint-disable-next-line no-restricted-imports
import { atom, createStore, type PrimitiveAtom } from "jotai";
import {
atom,
createStore,
type PrimitiveAtom,
type WritableAtom,
} from "jotai";
import { createIsolation } from "jotai-scope";
const jotai = createIsolation();
export { atom, PrimitiveAtom };
export { atom, PrimitiveAtom, WritableAtom };
export const { useAtom, useSetAtom, useAtomValue, useStore } = jotai;
export const EditorJotaiProvider: ReturnType<
typeof createIsolation

View file

@ -714,6 +714,7 @@ export type AppClassProperties = {
excalidrawContainerValue: App["excalidrawContainerValue"];
onPointerUpEmitter: App["onPointerUpEmitter"];
updateEditorAtom: App["updateEditorAtom"];
};
export type PointerDownState = Readonly<{