fix single text element, unique id and tests

This commit is contained in:
Aakansha Doshi 2023-05-25 14:12:01 +05:30
parent 6c41e507db
commit 1c291f8b3d
4 changed files with 60 additions and 23 deletions

View file

@ -135,23 +135,20 @@ export const loadSceneOrLibraryFromBlob = async (
try { try {
const data = JSON.parse(contents); const data = JSON.parse(contents);
if (isValidExcalidrawData(data)) { if (isValidExcalidrawData(data)) {
const excaldrawElements = convertToExcalidrawElements(
data.elements || [],
);
return { return {
type: MIME_TYPES.excalidraw, type: MIME_TYPES.excalidraw,
data: restore( data: restore(
{ {
elements: clearElementsForExport( elements: clearElementsForExport(excaldrawElements),
convertToExcalidrawElements(data.elements || []),
),
appState: { appState: {
theme: localAppState?.theme, theme: localAppState?.theme,
fileHandle: fileHandle || blob.handle || null, fileHandle: fileHandle || blob.handle || null,
...cleanAppStateForExport(data.appState || {}), ...cleanAppStateForExport(data.appState || {}),
...(localAppState ...(localAppState
? calculateScrollCenter( ? calculateScrollCenter(excaldrawElements, localAppState, null)
convertToExcalidrawElements(data.elements || []),
localAppState,
null,
)
: {}), : {}),
}, },
files: data.files, files: data.files,

View file

@ -1,4 +1,9 @@
import { TEXT_ALIGN, VERTICAL_ALIGN } from "../constants"; import {
DEFAULT_FONT_FAMILY,
DEFAULT_FONT_SIZE,
TEXT_ALIGN,
VERTICAL_ALIGN,
} from "../constants";
import { import {
newElement, newElement,
newLinearElement, newLinearElement,
@ -6,8 +11,17 @@ import {
} from "../element"; } from "../element";
import { bindLinearElement } from "../element/binding"; import { bindLinearElement } from "../element/binding";
import { mutateElement } from "../element/mutateElement"; import { mutateElement } from "../element/mutateElement";
import { ElementConstructorOpts, newTextElement } from "../element/newElement"; import {
import { VALID_CONTAINER_TYPES } from "../element/textElement"; ElementConstructorOpts,
newTextElement,
regenerateId,
} from "../element/newElement";
import {
VALID_CONTAINER_TYPES,
getDefaultLineHeight,
measureText,
normalizeText,
} from "../element/textElement";
import { import {
ExcalidrawBindableElement, ExcalidrawBindableElement,
ExcalidrawElement, ExcalidrawElement,
@ -18,8 +32,8 @@ import {
TextAlign, TextAlign,
VerticalAlign, VerticalAlign,
} from "../element/types"; } from "../element/types";
import { randomId } from "../random";
import { MarkOptional } from "../utility-types"; import { MarkOptional } from "../utility-types";
import { getFontString } from "../utils";
import { ImportedDataState } from "./types"; import { ImportedDataState } from "./types";
export const ELEMENTS_SUPPORTING_PROGRAMMATIC_API = [ export const ELEMENTS_SUPPORTING_PROGRAMMATIC_API = [
@ -212,14 +226,20 @@ const excalidrawElements = (() => {
}; };
const clear = () => { const clear = () => {
res.length = 0; res.length = 0;
elementMap.clear();
}; };
const get = () => { const get = () => {
return res; return res;
}; };
const hasElementWithId = (id: string) => {
const index = elementMap.get(id);
return index !== undefined && index >= 0;
};
return { return {
push, push,
clear, clear,
get, get,
hasElementWithId,
}; };
})(); })();
@ -234,6 +254,15 @@ export const convertToExcalidrawElements = (
if (!element) { if (!element) {
return; return;
} }
let elementId = element.id || regenerateId(null);
// To make sure every element has a unique id
while (excalidrawElements.hasElementWithId(elementId)) {
elementId = regenerateId(elementId);
}
const elementWithid = { ...element, id: elementId };
if (!ELEMENTS_SUPPORTING_PROGRAMMATIC_API.includes(element.type)) { if (!ELEMENTS_SUPPORTING_PROGRAMMATIC_API.includes(element.type)) {
excalidrawElements.push(element as ExcalidrawElement); excalidrawElements.push(element as ExcalidrawElement);
@ -242,7 +271,7 @@ export const convertToExcalidrawElements = (
//@ts-ignore //@ts-ignore
if (VALID_CONTAINER_TYPES.has(element.type) && element?.label?.text) { if (VALID_CONTAINER_TYPES.has(element.type) && element?.label?.text) {
//@ts-ignore //@ts-ignore
let [container, text] = bindTextToContainer(element, element.label); let [container, text] = bindTextToContainer(elementWithid, element.label);
excalidrawElements.push(container); excalidrawElements.push(container);
excalidrawElements.push(text); excalidrawElements.push(text);
@ -263,15 +292,28 @@ export const convertToExcalidrawElements = (
} else { } else {
let excalidrawElement; let excalidrawElement;
if (element.type === "text") { if (element.type === "text") {
excalidrawElement = newTextElement({ const fontFamily = element?.fontFamily || DEFAULT_FONT_FAMILY;
...element, const fontSize = element?.fontSize || DEFAULT_FONT_SIZE;
}); const lineHeight =
element?.lineHeight || getDefaultLineHeight(fontFamily);
const text = element.text ?? "";
const normalizedText = normalizeText(text);
const metrics = measureText(
normalizedText,
getFontString({ fontFamily, fontSize }),
lineHeight,
);
excalidrawElement = {
width: metrics.width,
height: metrics.height,
...elementWithid,
};
excalidrawElements.push(excalidrawElement); excalidrawElements.push(excalidrawElement as ExcalidrawTextElement);
} else if (element.type === "arrow" || element.type === "line") { } else if (element.type === "arrow" || element.type === "line") {
const { linearElement, startBoundElement, endBoundElement } = const { linearElement, startBoundElement, endBoundElement } =
//@ts-ignore //@ts-ignore
bindLinearElementToElement(element); bindLinearElementToElement(elementWithid);
excalidrawElements.push(linearElement); excalidrawElements.push(linearElement);
excalidrawElements.push(startBoundElement); excalidrawElements.push(startBoundElement);
excalidrawElements.push(endBoundElement); excalidrawElements.push(endBoundElement);
@ -287,8 +329,7 @@ export const convertToExcalidrawElements = (
} }
} else { } else {
excalidrawElement = { excalidrawElement = {
...element, ...elementWithid,
id: element.id || randomId(),
width: width:
element?.width || element?.width ||
(ELEMENTS_SUPPORTING_PROGRAMMATIC_API.includes(element.type) (ELEMENTS_SUPPORTING_PROGRAMMATIC_API.includes(element.type)

View file

@ -18,7 +18,6 @@ import type { cleanAppStateForExport } from "../appState";
import { VERSIONS } from "../constants"; import { VERSIONS } from "../constants";
import { MarkOptional } from "../utility-types"; import { MarkOptional } from "../utility-types";
import { ElementConstructorOpts } from "../element/newElement"; import { ElementConstructorOpts } from "../element/newElement";
import { ELEMENTS_SUPPORTING_PROGRAMMATIC_API } from "./transform";
export interface ExportedDataState { export interface ExportedDataState {
type: string; type: string;
@ -62,7 +61,7 @@ export interface ImportedDataState {
type: "text"; type: "text";
text: string; text: string;
id?: ExcalidrawTextElement["id"]; id?: ExcalidrawTextElement["id"];
} & ElementConstructorOpts) } & Partial<ExcalidrawTextElement>)
| ({ | ({
type: ExcalidrawLinearElement["type"]; type: ExcalidrawLinearElement["type"];
x: number; x: number;

View file

@ -471,7 +471,7 @@ export const deepCopyElement = <T extends ExcalidrawElement>(
* utility wrapper to generate new id. In test env it reuses the old + postfix * utility wrapper to generate new id. In test env it reuses the old + postfix
* for test assertions. * for test assertions.
*/ */
const regenerateId = ( export const regenerateId = (
/** supply null if no previous id exists */ /** supply null if no previous id exists */
previousId: string | null, previousId: string | null,
) => { ) => {