Import and export library from/to a file (#1940)

Co-authored-by: dwelle <luzar.david@gmail.com>
This commit is contained in:
Mohammed Salman 2020-07-27 15:29:19 +03:00 committed by GitHub
parent 7eff6893c5
commit ee8fa6aaad
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 199 additions and 39 deletions

View file

@ -2,17 +2,11 @@ import { getDefaultAppState, cleanAppStateForExport } from "../appState";
import { restore } from "./restore";
import { t } from "../i18n";
import { AppState } from "../types";
import { LibraryData } from "./types";
import { calculateScrollCenter } from "../scene";
/**
* @param blob
* @param appState if provided, used for centering scroll to restored scene
*/
export const loadFromBlob = async (blob: any, appState?: AppState) => {
if (blob.handle) {
(window as any).handle = blob.handle;
}
let contents;
const loadFileContents = async (blob: any) => {
let contents: string;
if ("text" in Blob) {
contents = await blob.text();
} else {
@ -26,7 +20,19 @@ export const loadFromBlob = async (blob: any, appState?: AppState) => {
};
});
}
return contents;
};
/**
* @param blob
* @param appState if provided, used for centering scroll to restored scene
*/
export const loadFromBlob = async (blob: any, appState?: AppState) => {
if (blob.handle) {
(window as any).handle = blob.handle;
}
const contents = await loadFileContents(blob);
const defaultAppState = getDefaultAppState();
let elements = [];
let _appState = appState || defaultAppState;
@ -47,3 +53,12 @@ export const loadFromBlob = async (blob: any, appState?: AppState) => {
return restore(elements, _appState);
};
export const loadLibraryFromBlob = async (blob: any) => {
const contents = await loadFileContents(blob);
const data: LibraryData = JSON.parse(contents);
if (data.type !== "excalidrawlib") {
throw new Error(t("alerts.couldNotLoadInvalidFile"));
}
return data;
};

View file

@ -4,6 +4,8 @@ import { cleanAppStateForExport } from "../appState";
import { fileOpen, fileSave } from "browser-nativefs";
import { loadFromBlob } from "./blob";
import { loadLibrary } from "./localStorage";
import { Library } from "./library";
export const serializeAsJSON = (
elements: readonly ExcalidrawElement[],
@ -50,3 +52,34 @@ export const loadFromJSON = async (appState: AppState) => {
});
return loadFromBlob(blob, appState);
};
export const saveLibraryAsJSON = async () => {
const library = await loadLibrary();
const serialized = JSON.stringify(
{
type: "excalidrawlib",
version: 1,
library,
},
null,
2,
);
const fileName = `library.excalidrawlib`;
const blob = new Blob([serialized], {
type: "application/vnd.excalidrawlib+json",
});
await fileSave(blob, {
fileName,
description: "Excalidraw library file",
extensions: ["excalidrawlib"],
});
};
export const importLibraryFromJSON = async () => {
const blob = await fileOpen({
description: "Excalidraw library files",
extensions: ["json", "excalidrawlib"],
mimeTypes: ["application/json"],
});
Library.importLibrary(blob);
};

43
src/data/library.ts Normal file
View file

@ -0,0 +1,43 @@
import { loadLibraryFromBlob } from "./blob";
import { LibraryItems, LibraryItem } from "../types";
import { loadLibrary, saveLibrary } from "./localStorage";
export class Library {
/** imports library (currently merges, removing duplicates) */
static async importLibrary(blob: any) {
const libraryFile = await loadLibraryFromBlob(blob);
if (!libraryFile || !libraryFile.library) {
return;
}
/**
* checks if library item does not exist already in current library
*/
const isUniqueitem = (
existingLibraryItems: LibraryItems,
targetLibraryItem: LibraryItem,
) => {
return !existingLibraryItems.find((libraryItem) => {
if (libraryItem.length !== targetLibraryItem.length) {
return false;
}
// detect z-index difference by checking the excalidraw elements
// are in order
return libraryItem.every((libItemExcalidrawItem, idx) => {
return (
libItemExcalidrawItem.id === targetLibraryItem[idx].id &&
libItemExcalidrawItem.versionNonce ===
targetLibraryItem[idx].versionNonce
);
});
});
};
const existingLibraryItems = await loadLibrary();
const filtered = libraryFile.library!.filter((libraryItem) =>
isUniqueitem(existingLibraryItems, libraryItem),
);
saveLibrary([...existingLibraryItems, ...filtered]);
}
}

View file

@ -21,7 +21,7 @@ export const loadLibrary = (): Promise<LibraryItems> => {
return resolve([]);
}
const items = (JSON.parse(data) as ExcalidrawElement[][]).map(
const items = (JSON.parse(data) as LibraryItems).map(
(elements) => restore(elements, null).elements,
) as Mutable<LibraryItems>;

View file

@ -1,5 +1,5 @@
import { ExcalidrawElement } from "../element/types";
import { AppState } from "../types";
import { AppState, LibraryItems } from "../types";
export interface DataState {
type?: string;
@ -8,3 +8,10 @@ export interface DataState {
elements: readonly ExcalidrawElement[];
appState: MarkOptional<AppState, "offsetTop" | "offsetLeft"> | null;
}
export interface LibraryData {
type?: string;
version?: number;
source?: string;
library?: LibraryItems;
}