fix: remove legacy React.render() from the editor (#5893)
This commit is contained in:
parent
96a5d6548b
commit
8ed0fc2c87
@ -5917,6 +5917,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
},
|
},
|
||||||
type: "canvas" | "element",
|
type: "canvas" | "element",
|
||||||
) => {
|
) => {
|
||||||
|
trackEvent("contextMenu", "openContextMenu", type);
|
||||||
if (this.state.showHyperlinkPopup) {
|
if (this.state.showHyperlinkPopup) {
|
||||||
this.setState({ showHyperlinkPopup: false });
|
this.setState({ showHyperlinkPopup: false });
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { render, unmountComponentAtNode } from "react-dom";
|
import { createRoot, Root } from "react-dom/client";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import { Popover } from "./Popover";
|
import { Popover } from "./Popover";
|
||||||
import { t } from "../i18n";
|
import { t } from "../i18n";
|
||||||
@ -89,22 +89,30 @@ const ContextMenu = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const contextMenuNodeByContainer = new WeakMap<HTMLElement, HTMLDivElement>();
|
const contextMenuRoots = new WeakMap<HTMLElement, Root>();
|
||||||
|
|
||||||
const getContextMenuNode = (container: HTMLElement): HTMLDivElement => {
|
const getContextMenuRoot = (container: HTMLElement): Root => {
|
||||||
let contextMenuNode = contextMenuNodeByContainer.get(container);
|
let contextMenuRoot = contextMenuRoots.get(container);
|
||||||
if (contextMenuNode) {
|
if (contextMenuRoot) {
|
||||||
return contextMenuNode;
|
return contextMenuRoot;
|
||||||
}
|
}
|
||||||
contextMenuNode = document.createElement("div");
|
contextMenuRoot = createRoot(
|
||||||
container
|
container.querySelector(".excalidraw-contextMenuContainer")!,
|
||||||
.querySelector(".excalidraw-contextMenuContainer")!
|
);
|
||||||
.appendChild(contextMenuNode);
|
contextMenuRoots.set(container, contextMenuRoot);
|
||||||
contextMenuNodeByContainer.set(container, contextMenuNode);
|
return contextMenuRoot;
|
||||||
return contextMenuNode;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type ContextMenuParams = {
|
const handleClose = (container: HTMLElement) => {
|
||||||
|
const contextMenuRoot = contextMenuRoots.get(container);
|
||||||
|
if (contextMenuRoot) {
|
||||||
|
contextMenuRoot.unmount();
|
||||||
|
contextMenuRoots.delete(container);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
push(params: {
|
||||||
options: (ContextMenuOption | false | null | undefined)[];
|
options: (ContextMenuOption | false | null | undefined)[];
|
||||||
top: ContextMenuProps["top"];
|
top: ContextMenuProps["top"];
|
||||||
left: ContextMenuProps["left"];
|
left: ContextMenuProps["left"];
|
||||||
@ -112,19 +120,7 @@ type ContextMenuParams = {
|
|||||||
appState: Readonly<AppState>;
|
appState: Readonly<AppState>;
|
||||||
container: HTMLElement;
|
container: HTMLElement;
|
||||||
elements: readonly NonDeletedExcalidrawElement[];
|
elements: readonly NonDeletedExcalidrawElement[];
|
||||||
};
|
}) {
|
||||||
|
|
||||||
const handleClose = (container: HTMLElement) => {
|
|
||||||
const contextMenuNode = contextMenuNodeByContainer.get(container);
|
|
||||||
if (contextMenuNode) {
|
|
||||||
unmountComponentAtNode(contextMenuNode);
|
|
||||||
contextMenuNode.remove();
|
|
||||||
contextMenuNodeByContainer.delete(container);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default {
|
|
||||||
push(params: ContextMenuParams) {
|
|
||||||
const options = Array.of<ContextMenuOption>();
|
const options = Array.of<ContextMenuOption>();
|
||||||
params.options.forEach((option) => {
|
params.options.forEach((option) => {
|
||||||
if (option) {
|
if (option) {
|
||||||
@ -132,7 +128,7 @@ export default {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (options.length) {
|
if (options.length) {
|
||||||
render(
|
getContextMenuRoot(params.container).render(
|
||||||
<ContextMenu
|
<ContextMenu
|
||||||
top={params.top}
|
top={params.top}
|
||||||
left={params.left}
|
left={params.left}
|
||||||
@ -142,7 +138,6 @@ export default {
|
|||||||
appState={params.appState}
|
appState={params.appState}
|
||||||
elements={params.elements}
|
elements={params.elements}
|
||||||
/>,
|
/>,
|
||||||
getContextMenuNode(params.container),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
import React, { useEffect, useRef, useState } from "react";
|
import React, { useEffect, useRef, useState } from "react";
|
||||||
import { render, unmountComponentAtNode } from "react-dom";
|
|
||||||
import { probablySupportsClipboardBlob } from "../clipboard";
|
import { probablySupportsClipboardBlob } from "../clipboard";
|
||||||
import { canvasToBlob } from "../data/blob";
|
import { canvasToBlob } from "../data/blob";
|
||||||
import { NonDeletedExcalidrawElement } from "../element/types";
|
import { NonDeletedExcalidrawElement } from "../element/types";
|
||||||
import { CanvasError } from "../errors";
|
|
||||||
import { t } from "../i18n";
|
import { t } from "../i18n";
|
||||||
import { getSelectedElements, isSomeElementSelected } from "../scene";
|
import { getSelectedElements, isSomeElementSelected } from "../scene";
|
||||||
import { exportToCanvas } from "../scene/export";
|
import { exportToCanvas } from "../scene/export";
|
||||||
@ -33,19 +31,6 @@ export const ErrorCanvasPreview = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderPreview = (
|
|
||||||
content: HTMLCanvasElement | Error,
|
|
||||||
previewNode: HTMLDivElement,
|
|
||||||
) => {
|
|
||||||
unmountComponentAtNode(previewNode);
|
|
||||||
previewNode.innerHTML = "";
|
|
||||||
if (content instanceof HTMLCanvasElement) {
|
|
||||||
previewNode.appendChild(content);
|
|
||||||
} else {
|
|
||||||
render(<ErrorCanvasPreview />, previewNode);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ExportCB = (
|
export type ExportCB = (
|
||||||
elements: readonly NonDeletedExcalidrawElement[],
|
elements: readonly NonDeletedExcalidrawElement[],
|
||||||
scale?: number,
|
scale?: number,
|
||||||
@ -99,6 +84,7 @@ const ImageExportModal = ({
|
|||||||
const [exportSelected, setExportSelected] = useState(someElementIsSelected);
|
const [exportSelected, setExportSelected] = useState(someElementIsSelected);
|
||||||
const previewRef = useRef<HTMLDivElement>(null);
|
const previewRef = useRef<HTMLDivElement>(null);
|
||||||
const { exportBackground, viewBackgroundColor } = appState;
|
const { exportBackground, viewBackgroundColor } = appState;
|
||||||
|
const [renderError, setRenderError] = useState<Error | null>(null);
|
||||||
|
|
||||||
const exportedElements = exportSelected
|
const exportedElements = exportSelected
|
||||||
? getSelectedElements(elements, appState, true)
|
? getSelectedElements(elements, appState, true)
|
||||||
@ -119,15 +105,16 @@ const ImageExportModal = ({
|
|||||||
exportPadding,
|
exportPadding,
|
||||||
})
|
})
|
||||||
.then((canvas) => {
|
.then((canvas) => {
|
||||||
|
setRenderError(null);
|
||||||
// if converting to blob fails, there's some problem that will
|
// if converting to blob fails, there's some problem that will
|
||||||
// likely prevent preview and export (e.g. canvas too big)
|
// likely prevent preview and export (e.g. canvas too big)
|
||||||
return canvasToBlob(canvas).then(() => {
|
return canvasToBlob(canvas).then(() => {
|
||||||
renderPreview(canvas, previewNode);
|
previewNode.replaceChildren(canvas);
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
renderPreview(new CanvasError(), previewNode);
|
setRenderError(error);
|
||||||
});
|
});
|
||||||
}, [
|
}, [
|
||||||
appState,
|
appState,
|
||||||
@ -140,7 +127,9 @@ const ImageExportModal = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="ExportDialog">
|
<div className="ExportDialog">
|
||||||
<div className="ExportDialog__preview" ref={previewRef} />
|
<div className="ExportDialog__preview" ref={previewRef}>
|
||||||
|
{renderError && <ErrorCanvasPreview />}
|
||||||
|
</div>
|
||||||
{supportsContextFilters &&
|
{supportsContextFilters &&
|
||||||
actionManager.renderAction("exportWithDarkMode")}
|
actionManager.renderAction("exportWithDarkMode")}
|
||||||
<div style={{ display: "grid", gridTemplateColumns: "1fr" }}>
|
<div style={{ display: "grid", gridTemplateColumns: "1fr" }}>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user