mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
feat: pass localElements to restore and restoreElement API's and bump versions of duplicate elements on import (#3797)
This commit is contained in:
parent
038e9c13dd
commit
097362662d
14 changed files with 164 additions and 61 deletions
|
@ -1,6 +1,7 @@
|
|||
import { cleanAppStateForExport } from "../appState";
|
||||
import { EXPORT_DATA_TYPES } from "../constants";
|
||||
import { clearElementsForExport } from "../element";
|
||||
import { ExcalidrawElement } from "../element/types";
|
||||
import { CanvasError } from "../errors";
|
||||
import { t } from "../i18n";
|
||||
import { calculateScrollCenter } from "../scene";
|
||||
|
@ -83,6 +84,7 @@ export const loadFromBlob = async (
|
|||
blob: Blob,
|
||||
/** @see restore.localAppState */
|
||||
localAppState: AppState | null,
|
||||
localElements: readonly ExcalidrawElement[] | null,
|
||||
) => {
|
||||
const contents = await parseFileContents(blob);
|
||||
try {
|
||||
|
@ -103,6 +105,7 @@ export const loadFromBlob = async (
|
|||
},
|
||||
},
|
||||
localAppState,
|
||||
localElements,
|
||||
);
|
||||
|
||||
return result;
|
||||
|
|
|
@ -49,7 +49,10 @@ export const saveAsJSON = async (
|
|||
return { fileHandle };
|
||||
};
|
||||
|
||||
export const loadFromJSON = async (localAppState: AppState) => {
|
||||
export const loadFromJSON = async (
|
||||
localAppState: AppState,
|
||||
localElements: readonly ExcalidrawElement[] | null,
|
||||
) => {
|
||||
const blob = await fileOpen({
|
||||
description: "Excalidraw files",
|
||||
// ToDo: Be over-permissive until https://bugs.webkit.org/show_bug.cgi?id=34442
|
||||
|
@ -64,7 +67,7 @@ export const loadFromJSON = async (localAppState: AppState) => {
|
|||
],
|
||||
*/
|
||||
});
|
||||
return loadFromBlob(blob, localAppState);
|
||||
return loadFromBlob(blob, localAppState, localElements);
|
||||
};
|
||||
|
||||
export const isValidExcalidrawData = (data?: {
|
||||
|
|
|
@ -18,7 +18,7 @@ class Library {
|
|||
};
|
||||
|
||||
restoreLibraryItem = (libraryItem: LibraryItem): LibraryItem | null => {
|
||||
const elements = getNonDeletedElements(restoreElements(libraryItem));
|
||||
const elements = getNonDeletedElements(restoreElements(libraryItem, null));
|
||||
return elements.length ? elements : null;
|
||||
};
|
||||
|
||||
|
|
|
@ -5,7 +5,11 @@ import {
|
|||
} from "../element/types";
|
||||
import { AppState, NormalizedZoomValue } from "../types";
|
||||
import { ImportedDataState } from "./types";
|
||||
import { getNormalizedDimensions, isInvisiblySmallElement } from "../element";
|
||||
import {
|
||||
getElementMap,
|
||||
getNormalizedDimensions,
|
||||
isInvisiblySmallElement,
|
||||
} from "../element";
|
||||
import { isLinearElementType } from "../element/typeChecks";
|
||||
import { randomId } from "../random";
|
||||
import {
|
||||
|
@ -16,6 +20,7 @@ import {
|
|||
} from "../constants";
|
||||
import { getDefaultAppState } from "../appState";
|
||||
import { LinearElementEditor } from "../element/linearElementEditor";
|
||||
import { bumpVersion } from "../element/mutateElement";
|
||||
|
||||
type RestoredAppState = Omit<
|
||||
AppState,
|
||||
|
@ -181,13 +186,20 @@ const restoreElement = (
|
|||
|
||||
export const restoreElements = (
|
||||
elements: ImportedDataState["elements"],
|
||||
/** NOTE doesn't serve for reconciliation */
|
||||
localElements: readonly ExcalidrawElement[] | null | undefined,
|
||||
): ExcalidrawElement[] => {
|
||||
const localElementsMap = localElements ? getElementMap(localElements) : null;
|
||||
return (elements || []).reduce((elements, element) => {
|
||||
// filtering out selection, which is legacy, no longer kept in elements,
|
||||
// and causing issues if retained
|
||||
if (element.type !== "selection" && !isInvisiblySmallElement(element)) {
|
||||
const migratedElement = restoreElement(element);
|
||||
let migratedElement: ExcalidrawElement = restoreElement(element);
|
||||
if (migratedElement) {
|
||||
const localElement = localElementsMap?.[element.id];
|
||||
if (localElement && localElement.version > migratedElement.version) {
|
||||
migratedElement = bumpVersion(migratedElement, localElement.version);
|
||||
}
|
||||
elements.push(migratedElement);
|
||||
}
|
||||
}
|
||||
|
@ -197,25 +209,25 @@ export const restoreElements = (
|
|||
|
||||
export const restoreAppState = (
|
||||
appState: ImportedDataState["appState"],
|
||||
localAppState: Partial<AppState> | null,
|
||||
localAppState: Partial<AppState> | null | undefined,
|
||||
): RestoredAppState => {
|
||||
appState = appState || {};
|
||||
|
||||
const defaultAppState = getDefaultAppState();
|
||||
const nextAppState = {} as typeof defaultAppState;
|
||||
|
||||
for (const [key, val] of Object.entries(defaultAppState) as [
|
||||
for (const [key, defaultValue] of Object.entries(defaultAppState) as [
|
||||
keyof typeof defaultAppState,
|
||||
any,
|
||||
][]) {
|
||||
const restoredValue = appState[key];
|
||||
const suppliedValue = appState[key];
|
||||
const localValue = localAppState ? localAppState[key] : undefined;
|
||||
(nextAppState as any)[key] =
|
||||
restoredValue !== undefined
|
||||
? restoredValue
|
||||
suppliedValue !== undefined
|
||||
? suppliedValue
|
||||
: localValue !== undefined
|
||||
? localValue
|
||||
: val;
|
||||
: defaultValue;
|
||||
}
|
||||
|
||||
return {
|
||||
|
@ -243,9 +255,10 @@ export const restore = (
|
|||
* Supply `null` if you can't get access to it.
|
||||
*/
|
||||
localAppState: Partial<AppState> | null | undefined,
|
||||
localElements: readonly ExcalidrawElement[] | null | undefined,
|
||||
): RestoredDataState => {
|
||||
return {
|
||||
elements: restoreElements(data?.elements),
|
||||
elements: restoreElements(data?.elements, localElements),
|
||||
appState: restoreAppState(data?.appState, localAppState || null),
|
||||
};
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue