diff options
Diffstat (limited to 'packages/excalidraw/data/json.ts')
| -rw-r--r-- | packages/excalidraw/data/json.ts | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/packages/excalidraw/data/json.ts b/packages/excalidraw/data/json.ts new file mode 100644 index 0000000..1270fd1 --- /dev/null +++ b/packages/excalidraw/data/json.ts @@ -0,0 +1,156 @@ +import { fileOpen, fileSave } from "./filesystem"; +import { cleanAppStateForExport, clearAppStateForDatabase } from "../appState"; +import { + DEFAULT_FILENAME, + EXPORT_DATA_TYPES, + EXPORT_SOURCE, + MIME_TYPES, + VERSIONS, +} from "../constants"; +import { clearElementsForDatabase, clearElementsForExport } from "../element"; +import type { ExcalidrawElement } from "../element/types"; +import type { AppState, BinaryFiles, LibraryItems } from "../types"; +import { isImageFileHandle, loadFromBlob, normalizeFile } from "./blob"; + +import type { + ExportedDataState, + ImportedDataState, + ExportedLibraryData, + ImportedLibraryData, +} from "./types"; + +/** + * Strips out files which are only referenced by deleted elements + */ +const filterOutDeletedFiles = ( + elements: readonly ExcalidrawElement[], + files: BinaryFiles, +) => { + const nextFiles: BinaryFiles = {}; + for (const element of elements) { + if ( + !element.isDeleted && + "fileId" in element && + element.fileId && + files[element.fileId] + ) { + nextFiles[element.fileId] = files[element.fileId]; + } + } + return nextFiles; +}; + +export const serializeAsJSON = ( + elements: readonly ExcalidrawElement[], + appState: Partial<AppState>, + files: BinaryFiles, + type: "local" | "database", +): string => { + const data: ExportedDataState = { + type: EXPORT_DATA_TYPES.excalidraw, + version: VERSIONS.excalidraw, + source: EXPORT_SOURCE, + elements: + type === "local" + ? clearElementsForExport(elements) + : clearElementsForDatabase(elements), + appState: + type === "local" + ? cleanAppStateForExport(appState) + : clearAppStateForDatabase(appState), + files: + type === "local" + ? filterOutDeletedFiles(elements, files) + : // will be stripped from JSON + undefined, + }; + + return JSON.stringify(data, null, 2); +}; + +export const saveAsJSON = async ( + elements: readonly ExcalidrawElement[], + appState: AppState, + files: BinaryFiles, + /** filename */ + name: string = appState.name || DEFAULT_FILENAME, +) => { + const serialized = serializeAsJSON(elements, appState, files, "local"); + const blob = new Blob([serialized], { + type: MIME_TYPES.excalidraw, + }); + + const fileHandle = await fileSave(blob, { + name, + extension: "excalidraw", + description: "Excalidraw file", + fileHandle: isImageFileHandle(appState.fileHandle) + ? null + : appState.fileHandle, + }); + return { fileHandle }; +}; + +export const loadFromJSON = async ( + localAppState: AppState, + localElements: readonly ExcalidrawElement[] | null, +) => { + const file = await fileOpen({ + description: "Excalidraw files", + // ToDo: Be over-permissive until https://bugs.webkit.org/show_bug.cgi?id=34442 + // gets resolved. Else, iOS users cannot open `.excalidraw` files. + // extensions: ["json", "excalidraw", "png", "svg"], + }); + return loadFromBlob( + await normalizeFile(file), + localAppState, + localElements, + file.handle, + ); +}; + +export const isValidExcalidrawData = (data?: { + type?: any; + elements?: any; + appState?: any; +}): data is ImportedDataState => { + return ( + data?.type === EXPORT_DATA_TYPES.excalidraw && + (!data.elements || + (Array.isArray(data.elements) && + (!data.appState || typeof data.appState === "object"))) + ); +}; + +export const isValidLibrary = (json: any): json is ImportedLibraryData => { + return ( + typeof json === "object" && + json && + json.type === EXPORT_DATA_TYPES.excalidrawLibrary && + (json.version === 1 || json.version === 2) + ); +}; + +export const serializeLibraryAsJSON = (libraryItems: LibraryItems) => { + const data: ExportedLibraryData = { + type: EXPORT_DATA_TYPES.excalidrawLibrary, + version: VERSIONS.excalidrawLibrary, + source: EXPORT_SOURCE, + libraryItems, + }; + return JSON.stringify(data, null, 2); +}; + +export const saveLibraryAsJSON = async (libraryItems: LibraryItems) => { + const serialized = serializeLibraryAsJSON(libraryItems); + await fileSave( + new Blob([serialized], { + type: MIME_TYPES.excalidrawlib, + }), + { + name: "library", + extension: "excalidrawlib", + description: "Excalidraw library file", + }, + ); +}; |
