This commit is contained in:
Aakansha Doshi 2023-05-03 13:41:41 +05:30
parent 7087db42c0
commit 59e8bf498d
9 changed files with 109 additions and 65 deletions

View file

@ -129,6 +129,7 @@ import {
import { LinearElementEditor } from "../element/linearElementEditor";
import { mutateElement, newElementWith } from "../element/mutateElement";
import {
convertToExcalidrawElements,
deepCopyElement,
duplicateElements,
newFreeDrawElement,
@ -2001,7 +2002,9 @@ class App extends React.Component<AppProps, AppState> {
}
if (sceneData.elements) {
this.scene.replaceAllElements(sceneData.elements);
this.scene.replaceAllElements(
convertToExcalidrawElements(sceneData.elements),
);
}
if (sceneData.collaborators) {

View file

@ -13,6 +13,7 @@ import { FileSystemHandle, nativeFileSystemSupported } from "./filesystem";
import { isValidExcalidrawData, isValidLibrary } from "./json";
import { restore, restoreLibraryItems } from "./restore";
import { ImportedLibraryData } from "./types";
import { convertToExcalidrawElements } from "../element/newElement";
const parseFileContents = async (blob: Blob | File) => {
let contents: string;
@ -138,14 +139,16 @@ export const loadSceneOrLibraryFromBlob = async (
type: MIME_TYPES.excalidraw,
data: restore(
{
elements: clearElementsForExport(data.elements || []),
elements: clearElementsForExport(
convertToExcalidrawElements(data.elements || []),
),
appState: {
theme: localAppState?.theme,
fileHandle: fileHandle || blob.handle || null,
...cleanAppStateForExport(data.appState || {}),
...(localAppState
? calculateScrollCenter(
data.elements || [],
convertToExcalidrawElements(data.elements || []),
localAppState,
null,
)

View file

@ -40,7 +40,7 @@ import {
getDefaultLineHeight,
measureBaseline,
} from "../element/textElement";
import { updateElementChildren } from "../element/newElement";
import { convertToExcalidrawElements } from "../element/newElement";
type RestoredAppState = Omit<
AppState,
@ -373,9 +373,10 @@ export const restoreElements = (
): ExcalidrawElement[] => {
// used to detect duplicate top-level element ids
const existingIds = new Set<string>();
const excalidrawElements = convertToExcalidrawElements(elements);
const localElementsMap = localElements ? arrayToMap(localElements) : null;
const restoredElements = (elements || []).reduce((elements, element) => {
const restoredElements = (excalidrawElements || []).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)) {
@ -386,28 +387,23 @@ export const restoreElements = (
if (migratedElement) {
const localElement = localElementsMap?.get(element.id);
if (localElement && localElement.version > migratedElement.version) {
migratedElement = bumpVersion(migratedElement, localElement.version);
migratedElement = bumpVersion(
migratedElement,
localElement.version,
);
}
if (existingIds.has(migratedElement.id)) {
migratedElement = { ...migratedElement, id: randomId() };
}
existingIds.add(migratedElement.id);
//@ts-ignore
if (element.children?.length) {
//@ts-ignore
const newElements = updateElementChildren(element);
if (newElements) {
elements.push(...newElements);
} else {
elements.push(migratedElement);
}
} else {
elements.push(migratedElement);
}
}
}
return elements;
}, [] as ExcalidrawElement[]);
},
[] as ExcalidrawElement[],
);
if (!opts?.repairBindings) {
return restoredElements;

View file

@ -1,4 +1,4 @@
import { ExcalidrawElement } from "../element/types";
import { ExcalidrawElement, ExcalidrawGenericElement } from "../element/types";
import {
AppState,
BinaryFiles,
@ -7,6 +7,8 @@ import {
} from "../types";
import type { cleanAppStateForExport } from "../appState";
import { VERSIONS } from "../constants";
import { MarkOptional } from "../utility-types";
import { ElementConstructorOpts } from "../element/newElement";
export interface ExportedDataState {
type: string;
@ -35,7 +37,28 @@ export interface ImportedDataState {
type?: string;
version?: number;
source?: string;
elements?: readonly ExcalidrawElement[] | null;
elements?:
| readonly (
| (ExcalidrawElement & {
children?: [
{ text: string } & MarkOptional<
ElementConstructorOpts,
"x" | "y"
>,
];
})
| {
type: Exclude<ExcalidrawGenericElement["type"], "selection">;
children?: [
{ text: string } & MarkOptional<
ElementConstructorOpts,
"x" | "y"
>,
] &
MarkOptional<ElementConstructorOpts, "x" | "y">;
}
)[]
| null;
appState?: Readonly<
Partial<
AppState & {

View file

@ -12,7 +12,6 @@ import {
ExcalidrawFreeDrawElement,
FontFamilyValues,
ExcalidrawTextContainer,
NonDeletedExcalidrawElement,
} from "../element/types";
import {
arrayToMap,
@ -47,9 +46,9 @@ import {
DEFAULT_VERTICAL_ALIGN,
VERTICAL_ALIGN,
} from "../constants";
import { isArrowElement, isTextElement } from "./typeChecks";
import { isArrowElement } from "./typeChecks";
import { MarkOptional, Merge, Mutable } from "../utility-types";
import { Children } from "react";
import { ImportedDataState } from "../data/types";
export type ElementConstructorOpts = MarkOptional<
Omit<ExcalidrawGenericElement, "id" | "type" | "isDeleted" | "updated">,
@ -649,21 +648,33 @@ export const duplicateElements = (
return clonedElements;
};
export const updateElementChildren = (element: {
type: ExcalidrawGenericElement["type"];
children?: [
{ text: string } & MarkOptional<ElementConstructorOpts, "x" | "y">,
] &
MarkOptional<ElementConstructorOpts, "x" | "y">;
}) => {
const textElement = element.children?.find(
(
child: { text: string } & MarkOptional<ElementConstructorOpts, "x" | "y">,
) => child.text !== null,
);
if (isValidTextContainer(element) && textElement) {
const elements = bindTextToContainer(element, textElement);
return elements;
export const convertToExcalidrawElements = (
elements: ImportedDataState["elements"],
) => {
const res: ExcalidrawElement[] = [];
if (!elements) {
return [];
}
return null;
elements.forEach((element) => {
if (!element) {
return;
}
const textElement = element.children?.find((child) => child.text !== null);
if (
isValidTextContainer(element) &&
textElement &&
(element.type === "rectangle" ||
element.type === "ellipse" ||
element.type === "diamond")
) {
const elements = bindTextToContainer(element, textElement);
res.push(...elements);
} else {
delete element.children;
//@ts-ignore
res.push(element);
}
});
return res;
};

View file

@ -1,7 +1,10 @@
import { getFontString, arrayToMap, isTestEnv } from "../utils";
import {
ExcalidrawDiamondElement,
ExcalidrawElement,
ExcalidrawEllipseElement,
ExcalidrawGenericElement,
ExcalidrawRectangleElement,
ExcalidrawTextContainer,
ExcalidrawTextElement,
ExcalidrawTextElementWithContainer,
@ -982,8 +985,9 @@ export const getDefaultLineHeight = (fontFamily: FontFamilyValues) => {
};
export const bindTextToContainer = (
containerProps: {
type: ExcalidrawGenericElement["type"];
containerProps:
| {
type: Exclude<ExcalidrawGenericElement["type"], "selection">;
} & MarkOptional<ElementConstructorOpts, "x" | "y">,
textProps: { text: string } & MarkOptional<ElementConstructorOpts, "x" | "y">,
) => {
@ -1000,6 +1004,7 @@ export const bindTextToContainer = (
textAlign: TEXT_ALIGN.CENTER,
verticalAlign: VERTICAL_ALIGN.MIDDLE,
});
mutateElement(container, {
boundElements: (container.boundElements || []).concat({
type: "text",

View file

@ -7,6 +7,7 @@ import {
import { serializeAsJSON } from "../../data/json";
import { restore } from "../../data/restore";
import { ImportedDataState } from "../../data/types";
import { convertToExcalidrawElements } from "../../element/newElement";
import { isInvisiblySmallElement } from "../../element/sizeHelpers";
import { isInitializedImageElement } from "../../element/typeChecks";
import { ExcalidrawElement, FileId } from "../../element/types";
@ -262,7 +263,7 @@ export const loadScene = async (
data = restore(
await importFromBackend(id, privateKey),
localDataState?.appState,
localDataState?.elements,
convertToExcalidrawElements(localDataState?.elements),
{ repairBindings: true, refreshDimensions: false },
);
} else {

View file

@ -86,6 +86,7 @@ import { appJotaiStore } from "./app-jotai";
import "./index.scss";
import { ResolutionType } from "../utility-types";
import { convertToExcalidrawElements } from "../element/newElement";
polyfill();
@ -206,7 +207,7 @@ const initializeScene = async (opts: {
isLoading: false,
},
elements: reconcileElements(
scene?.elements || [],
convertToExcalidrawElements(scene?.elements || []),
excalidrawAPI.getSceneElementsIncludingDeleted(),
excalidrawAPI.getAppState(),
),
@ -286,7 +287,7 @@ const ExcalidrawWrapper = () => {
if (data.scene.elements) {
collabAPI
.fetchImageFilesFromFirebase({
elements: data.scene.elements,
elements: convertToExcalidrawElements(data.scene.elements),
forceFetchFiles: true,
})
.then(({ loadedFiles, erroredFiles }) => {
@ -299,8 +300,9 @@ const ExcalidrawWrapper = () => {
});
}
} else {
const sceneElements = convertToExcalidrawElements(data.scene.elements);
const fileIds =
data.scene.elements?.reduce((acc, element) => {
sceneElements?.reduce((acc, element) => {
if (isInitializedImageElement(element)) {
return acc.concat(element.fileId);
}
@ -351,7 +353,6 @@ const ExcalidrawWrapper = () => {
elements: [
{
type: "rectangle",
//@ts-ignore
children: [{ text: "HELLO DAMMMMY" }],
},
],

View file

@ -1,5 +1,6 @@
import { decodePngMetadata, decodeSvgMetadata } from "../../data/image";
import { ImportedDataState } from "../../data/types";
import { convertToExcalidrawElements } from "../../element/newElement";
import * as utils from "../../packages/utils";
import { API } from "../helpers/api";
@ -31,7 +32,7 @@ describe("embedding scene data", () => {
const importedData: ImportedDataState = JSON.parse(parsedString);
expect(sourceElements.map((x) => x.id)).toEqual(
importedData.elements?.map((el) => el.id),
convertToExcalidrawElements(importedData.elements)?.map((el) => el.id),
);
});
});
@ -60,7 +61,7 @@ describe("embedding scene data", () => {
const importedData: ImportedDataState = JSON.parse(parsedString);
expect(sourceElements.map((x) => x.id)).toEqual(
importedData.elements?.map((el) => el.id),
convertToExcalidrawElements(importedData.elements)?.map((el) => el.id),
);
});
});