Do not trigger modal actions when not visible (#562)

Right now we're running useEffect block which runs getExportCanvasPreview on every state update, even if the modal is not visible (eg: when moving the mouse around!). This puts the modal code in its own component so that it doesn't trigger useEffect when not visible.

The code isn't very elegant as we're forwarding all the props, there's likely a better way to handle it (if anyone is interested, PR would be appreciated), but at least now it no longer double renders the scene.

Fixes #559
This commit is contained in:
Christopher Chedeau 2020-01-25 14:11:26 -08:00 committed by GitHub
parent c697938350
commit ba13f88924
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 122 additions and 94 deletions

View File

@ -28,7 +28,7 @@ type ExportCB = (
scale?: number, scale?: number,
) => void; ) => void;
export function ExportDialog({ function ExportModal({
elements, elements,
appState, appState,
exportPadding = 10, exportPadding = 10,
@ -37,6 +37,7 @@ export function ExportDialog({
onExportToPng, onExportToPng,
onExportToClipboard, onExportToClipboard,
onExportToBackend, onExportToBackend,
onCloseRequest,
}: { }: {
appState: AppState; appState: AppState;
elements: readonly ExcalidrawElement[]; elements: readonly ExcalidrawElement[];
@ -46,10 +47,10 @@ export function ExportDialog({
onExportToPng: ExportCB; onExportToPng: ExportCB;
onExportToClipboard: ExportCB; onExportToClipboard: ExportCB;
onExportToBackend: ExportCB; onExportToBackend: ExportCB;
onCloseRequest: () => void;
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
const someElementIsSelected = elements.some(element => element.isSelected); const someElementIsSelected = elements.some(element => element.isSelected);
const [modalIsShown, setModalIsShown] = useState(false);
const [scale, setScale] = useState(defaultScale); const [scale, setScale] = useState(defaultScale);
const [exportSelected, setExportSelected] = useState(someElementIsSelected); const [exportSelected, setExportSelected] = useState(someElementIsSelected);
const previewRef = useRef<HTMLDivElement>(null); const previewRef = useRef<HTMLDivElement>(null);
@ -76,7 +77,6 @@ export function ExportDialog({
previewNode?.removeChild(canvas); previewNode?.removeChild(canvas);
}; };
}, [ }, [
modalIsShown,
exportedElements, exportedElements,
exportBackground, exportBackground,
exportPadding, exportPadding,
@ -84,25 +84,10 @@ export function ExportDialog({
scale, scale,
]); ]);
function handleClose() {
setModalIsShown(false);
setExportSelected(someElementIsSelected);
}
return ( return (
<>
<ToolButton
onClick={() => setModalIsShown(true)}
icon={exportFile}
type="button"
aria-label="Show export dialog"
title={t("buttons.export")}
/>
{modalIsShown && (
<Modal maxWidth={640} onCloseRequest={handleClose}>
<div className="ExportDialog__dialog"> <div className="ExportDialog__dialog">
<Island padding={4}> <Island padding={4}>
<button className="ExportDialog__close" onClick={handleClose}> <button className="ExportDialog__close" onClick={onCloseRequest}>
</button> </button>
<h2>{t("buttons.export")}</h2> <h2>{t("buttons.export")}</h2>
@ -122,9 +107,7 @@ export function ExportDialog({
icon={clipboard} icon={clipboard}
title={t("buttons.copyToClipboard")} title={t("buttons.copyToClipboard")}
aria-label={t("buttons.copyToClipboard")} aria-label={t("buttons.copyToClipboard")}
onClick={() => onClick={() => onExportToClipboard(exportedElements, scale)}
onExportToClipboard(exportedElements, scale)
}
/> />
)} )}
<ToolButton <ToolButton
@ -174,9 +157,7 @@ export function ExportDialog({
<input <input
type="checkbox" type="checkbox"
checked={exportSelected} checked={exportSelected}
onChange={e => onChange={e => setExportSelected(e.currentTarget.checked)}
setExportSelected(e.currentTarget.checked)
}
/>{" "} />{" "}
{t("labels.onlySelected")} {t("labels.onlySelected")}
</label> </label>
@ -186,6 +167,53 @@ export function ExportDialog({
</div> </div>
</Island> </Island>
</div> </div>
);
}
export function ExportDialog({
elements,
appState,
exportPadding = 10,
actionManager,
syncActionResult,
onExportToPng,
onExportToClipboard,
onExportToBackend,
}: {
appState: AppState;
elements: readonly ExcalidrawElement[];
exportPadding?: number;
actionManager: ActionsManagerInterface;
syncActionResult: UpdaterFn;
onExportToPng: ExportCB;
onExportToClipboard: ExportCB;
onExportToBackend: ExportCB;
}) {
const { t } = useTranslation();
const [modalIsShown, setModalIsShown] = useState(false);
return (
<>
<ToolButton
onClick={() => setModalIsShown(true)}
icon={exportFile}
type="button"
aria-label="Show export dialog"
title={t("buttons.export")}
/>
{modalIsShown && (
<Modal maxWidth={640} onCloseRequest={() => setModalIsShown(false)}>
<ExportModal
elements={elements}
appState={appState}
exportPadding={exportPadding}
actionManager={actionManager}
syncActionResult={syncActionResult}
onExportToPng={onExportToPng}
onExportToClipboard={onExportToClipboard}
onExportToBackend={onExportToBackend}
onCloseRequest={() => setModalIsShown(false)}
/>
</Modal> </Modal>
)} )}
</> </>