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 { return {
appState,
commitToHistory: false,
captureUpdate: CaptureUpdateAction.NEVER, captureUpdate: CaptureUpdateAction.NEVER,
}; };
}, },

View file

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

View file

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

View file

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

View file

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