feat: rewrite preview to use React.Suspense
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useReducer, useRef, useState } from "react";
|
||||
import React, { useEffect, useReducer, useRef } from "react";
|
||||
import clsx from "clsx";
|
||||
|
||||
import type { ActionManager } from "../actions/manager";
|
||||
@@ -21,7 +21,6 @@ import {
|
||||
FANCY_BACKGROUND_IMAGES,
|
||||
} from "../constants";
|
||||
|
||||
import { canvasToBlob } from "../data/blob";
|
||||
import { nativeFileSystemSupported } from "../data/filesystem";
|
||||
import {
|
||||
ExcalidrawElement,
|
||||
@@ -29,7 +28,7 @@ import {
|
||||
} from "../element/types";
|
||||
import { t } from "../i18n";
|
||||
import { getSelectedElements, isSomeElementSelected } from "../scene";
|
||||
import { exportToCanvas, getScaleToFit } from "../packages/utils";
|
||||
import { getScaleToFit } from "../packages/utils";
|
||||
|
||||
import { copyIcon, downloadIcon, helpIcon } from "./icons";
|
||||
import { Dialog } from "./Dialog";
|
||||
@@ -50,6 +49,7 @@ import {
|
||||
import { getFancyBackgroundPadding } from "../scene/fancyBackground";
|
||||
import { Select } from "./Select";
|
||||
import { Bounds } from "../element/bounds";
|
||||
import { CanvasPreview } from "./ImageExportPreview";
|
||||
|
||||
const supportsContextFilters =
|
||||
"filter" in document.createElement("canvas").getContext("2d")!;
|
||||
@@ -61,18 +61,6 @@ const fancyBackgroundImageOptions = Object.entries(FANCY_BACKGROUND_IMAGES).map(
|
||||
}),
|
||||
);
|
||||
|
||||
export const ErrorCanvasPreview = () => {
|
||||
return (
|
||||
<div>
|
||||
<h3>{t("canvasError.cannotShowPreview")}</h3>
|
||||
<p>
|
||||
<span>{t("canvasError.canvasTooBig")}</span>
|
||||
</p>
|
||||
<em>({t("canvasError.canvasTooBigTip")})</em>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
type ImageExportModalProps = {
|
||||
appState: UIAppState;
|
||||
elements: readonly NonDeletedExcalidrawElement[];
|
||||
@@ -228,7 +216,6 @@ const ImageExportModal = ({
|
||||
const appProps = useAppProps();
|
||||
|
||||
const previewRef = useRef<HTMLDivElement>(null);
|
||||
const [renderError, setRenderError] = useState<Error | null>(null);
|
||||
|
||||
// Upscale exported image when is smaller than preview
|
||||
useEffect(() => {
|
||||
@@ -281,91 +268,17 @@ const ImageExportModal = ({
|
||||
actionManager,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
const previewNode = previewRef.current;
|
||||
if (!previewNode) {
|
||||
return;
|
||||
}
|
||||
const maxWidth = previewNode.offsetWidth;
|
||||
const maxHeight = previewNode.offsetHeight;
|
||||
|
||||
const maxWidthOrHeight = Math.min(maxWidth, maxHeight);
|
||||
|
||||
if (!maxWidth) {
|
||||
return;
|
||||
}
|
||||
|
||||
// when switching between solid/no background and image background, we clear the canvas to prevent flickering
|
||||
const isExportWithFancyBackground =
|
||||
appState.exportBackground && appState.fancyBackgroundImageKey !== "solid";
|
||||
|
||||
if (state.isExportWithFancyBackground !== isExportWithFancyBackground) {
|
||||
const existingCanvas = previewNode.querySelector("canvas");
|
||||
if (existingCanvas) {
|
||||
const context = existingCanvas.getContext("2d");
|
||||
|
||||
context!.clearRect(0, 0, existingCanvas.width, existingCanvas.height);
|
||||
}
|
||||
dispatch({
|
||||
type: "SET_IS_EXPORT_WITH_FANCY_BACKGROUND",
|
||||
isExportWithFancyBackground,
|
||||
});
|
||||
}
|
||||
|
||||
exportToCanvas({
|
||||
elements: state.exportedElements,
|
||||
appState,
|
||||
files,
|
||||
exportPadding: DEFAULT_EXPORT_PADDING,
|
||||
maxWidthOrHeight,
|
||||
})
|
||||
.then((canvas) => {
|
||||
setRenderError(null);
|
||||
// if converting to blob fails, there's some problem that will
|
||||
// likely prevent preview and export (e.g. canvas too big)
|
||||
return canvasToBlob(canvas).then(() => {
|
||||
const existingCanvas = previewNode.querySelector("canvas");
|
||||
if (!existingCanvas) {
|
||||
previewNode.appendChild(canvas);
|
||||
return;
|
||||
}
|
||||
|
||||
existingCanvas.width = canvas.width;
|
||||
existingCanvas.height = canvas.height;
|
||||
|
||||
const context = existingCanvas.getContext("2d");
|
||||
context!.drawImage(canvas, 0, 0);
|
||||
});
|
||||
|
||||
// Get the 2D rendering context of the existing canvas
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
setRenderError(error);
|
||||
});
|
||||
}, [
|
||||
appState,
|
||||
appState.exportBackground,
|
||||
appState.fancyBackgroundImageKey,
|
||||
files,
|
||||
state.exportedElements,
|
||||
state.isExportWithFancyBackground,
|
||||
]);
|
||||
|
||||
return (
|
||||
<div className="ImageExportModal">
|
||||
<h3>{t("imageExportDialog.header")}</h3>
|
||||
<div className="ImageExportModal__preview">
|
||||
<div
|
||||
className={clsx("ImageExportModal__preview__canvas", {
|
||||
"ImageExportModal__preview__canvas--img-bcg":
|
||||
appState.exportBackground &&
|
||||
appState.fancyBackgroundImageKey !== "solid",
|
||||
})}
|
||||
ref={previewRef}
|
||||
>
|
||||
{renderError && <ErrorCanvasPreview />}
|
||||
</div>
|
||||
<React.Suspense fallback={<div>Loading...</div>}>
|
||||
<CanvasPreview
|
||||
appState={appState}
|
||||
files={files}
|
||||
elements={state.exportedElements}
|
||||
/>
|
||||
</React.Suspense>
|
||||
</div>
|
||||
<div className="ImageExportModal__settings">
|
||||
<h3>{t("imageExportDialog.header")}</h3>
|
||||
|
||||
Reference in New Issue
Block a user