diff --git a/src/components/App.tsx b/src/components/App.tsx index 9a84579c..a4349a6c 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -1700,6 +1700,11 @@ class App extends React.Component { this.library.saveLibrary( restoreLibraryItems(sceneData.libraryItems, "unpublished"), ); + if (this.state.isLibraryOpen) { + this.setState({ isLibraryOpen: false }, () => { + this.setState({ isLibraryOpen: true }); + }); + } } }, ); diff --git a/src/data/library.ts b/src/data/library.ts index 248db516..2710b430 100644 --- a/src/data/library.ts +++ b/src/data/library.ts @@ -1,7 +1,6 @@ import { loadLibraryFromBlob } from "./blob"; import { LibraryItems, LibraryItem } from "../types"; -import { restoreElements, restoreLibraryItems } from "./restore"; -import { getNonDeletedElements } from "../element"; +import { restoreLibraryItems } from "./restore"; import type App from "../components/App"; class Library { @@ -17,15 +16,11 @@ class Library { this.libraryCache = []; }; - restoreLibraryItem = (libraryItem: LibraryItem): LibraryItem | null => { - const elements = getNonDeletedElements( - restoreElements(libraryItem.elements, null), - ); - return elements.length ? { ...libraryItem, elements } : null; - }; - /** imports library (currently merges, removing duplicates) */ - async importLibrary(blob: Blob, defaultStatus = "unpublished") { + async importLibrary( + blob: Blob, + defaultStatus: LibraryItem["status"] = "unpublished", + ) { const libraryFile = await loadLibraryFromBlob(blob); if (!libraryFile || !(libraryFile.libraryItems || libraryFile.library)) { return; @@ -58,15 +53,11 @@ class Library { const existingLibraryItems = await this.loadLibrary(); const library = libraryFile.libraryItems || libraryFile.library || []; - const restoredLibItems = restoreLibraryItems( - library, - defaultStatus as "published" | "unpublished", - ); + const restoredLibItems = restoreLibraryItems(library, defaultStatus); const filteredItems = []; for (const item of restoredLibItems) { - const restoredItem = this.restoreLibraryItem(item as LibraryItem); - if (restoredItem && isUniqueitem(existingLibraryItems, restoredItem)) { - filteredItems.push(restoredItem); + if (isUniqueitem(existingLibraryItems, item)) { + filteredItems.push(item); } } @@ -85,13 +76,7 @@ class Library { return resolve([]); } - const items = libraryItems.reduce((acc, item) => { - const restoredItem = this.restoreLibraryItem(item); - if (restoredItem) { - acc.push(item); - } - return acc; - }, [] as Mutable); + const items = restoreLibraryItems(libraryItems, "unpublished"); // clone to ensure we don't mutate the cached library elements in the app this.libraryCache = JSON.parse(JSON.stringify(items)); diff --git a/src/data/restore.ts b/src/data/restore.ts index f42fe93d..7c18da29 100644 --- a/src/data/restore.ts +++ b/src/data/restore.ts @@ -10,7 +10,11 @@ import { NormalizedZoomValue, } from "../types"; import { ImportedDataState } from "./types"; -import { getNormalizedDimensions, isInvisiblySmallElement } from "../element"; +import { + getNonDeletedElements, + getNormalizedDimensions, + isInvisiblySmallElement, +} from "../element"; import { isLinearElementType } from "../element/typeChecks"; import { randomId } from "../random"; import { @@ -293,6 +297,14 @@ export const restore = ( }; }; +const restoreLibraryItem = (libraryItem: LibraryItem) => { + const elements = restoreElements( + getNonDeletedElements(libraryItem.elements), + null, + ); + return elements.length ? { ...libraryItem, elements } : null; +}; + export const restoreLibraryItems = ( libraryItems: NonOptional, defaultStatus: LibraryItem["status"], @@ -301,20 +313,29 @@ export const restoreLibraryItems = ( for (const item of libraryItems) { // migrate older libraries if (Array.isArray(item)) { - restoredItems.push({ + const restoredItem = restoreLibraryItem({ status: defaultStatus, elements: item, id: randomId(), created: Date.now(), }); + if (restoredItem) { + restoredItems.push(restoredItem); + } } else { - const _item = item as MarkOptional; - restoredItems.push({ + const _item = item as MarkOptional< + LibraryItem, + "id" | "status" | "created" + >; + const restoredItem = restoreLibraryItem({ ..._item, id: _item.id || randomId(), status: _item.status || defaultStatus, created: _item.created || Date.now(), }); + if (restoredItem) { + restoredItems.push(restoredItem); + } } } return restoredItems; diff --git a/src/packages/excalidraw/CHANGELOG.md b/src/packages/excalidraw/CHANGELOG.md index 31da04e0..f7826461 100644 --- a/src/packages/excalidraw/CHANGELOG.md +++ b/src/packages/excalidraw/CHANGELOG.md @@ -15,6 +15,14 @@ Please add the latest change on the top under the correct section. ### Excalidraw API +#### Features + +- Exported [`restoreLibraryItems`](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#restoreLibraryItems) API useful when dealing with libraries [#4995](https://github.com/excalidraw/excalidraw/pull/4995). + +#### Fixes + +- Library menu now properly rerenders if open when library is updated using `updateScene({ libraryItems })` [#4995](https://github.com/excalidraw/excalidraw/pull/4995). + #### Refactor - Rename `appState.elementLocked` to `appState.activeTool.locked` [#4983](https://github.com/excalidraw/excalidraw/pull/4983). diff --git a/src/packages/excalidraw/README_NEXT.md b/src/packages/excalidraw/README_NEXT.md index aefa4c79..95a4c73e 100644 --- a/src/packages/excalidraw/README_NEXT.md +++ b/src/packages/excalidraw/README_NEXT.md @@ -802,6 +802,24 @@ import { restore } from "@excalidraw/excalidraw-next"; This function makes sure elements and state is set to appropriate values and set to default value if not present. It is a combination of [restoreElements](#restoreElements) and [restoreAppState](#restoreAppState). +#### `restoreLibraryItems` + +**_Signature_** + +
+restoreLibraryItems(libraryItems: ImportedDataState["libraryItems"], defaultStatus: "published" | "unpublished")
+
+ +**_How to use_** + +```js +import { restoreLibraryItems } from "@excalidraw/excalidraw-next"; + +restoreLibraryItems(libraryItems, "unpublished"); +``` + +This function normalizes library items elements, adding missing values when needed. + ### Export utilities #### `exportToCanvas` diff --git a/src/packages/excalidraw/index.tsx b/src/packages/excalidraw/index.tsx index 96f4741a..bee1e605 100644 --- a/src/packages/excalidraw/index.tsx +++ b/src/packages/excalidraw/index.tsx @@ -177,7 +177,12 @@ export { getNonDeletedElements, } from "../../element"; export { defaultLang, languages } from "../../i18n"; -export { restore, restoreAppState, restoreElements } from "../../data/restore"; +export { + restore, + restoreAppState, + restoreElements, + restoreLibraryItems, +} from "../../data/restore"; export { exportToCanvas, exportToBlob,