feat: library restoring changes (#4995)
* restore library items in all cases & refactor * export `restoreLibraryItems` from package * feat: rerender library menu when updating via API * update readme & changelog * fix changelog
This commit is contained in:
parent
bf6d0eeef7
commit
70b3a9de49
@ -1700,6 +1700,11 @@ class App extends React.Component<AppProps, AppState> {
|
||||
this.library.saveLibrary(
|
||||
restoreLibraryItems(sceneData.libraryItems, "unpublished"),
|
||||
);
|
||||
if (this.state.isLibraryOpen) {
|
||||
this.setState({ isLibraryOpen: false }, () => {
|
||||
this.setState({ isLibraryOpen: true });
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
@ -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<LibraryItems>);
|
||||
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));
|
||||
|
@ -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<ImportedDataState["libraryItems"]>,
|
||||
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<LibraryItem, "id" | "status">;
|
||||
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;
|
||||
|
@ -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).
|
||||
|
@ -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_**
|
||||
|
||||
<pre>
|
||||
restoreLibraryItems(libraryItems: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/data/types.ts#L22">ImportedDataState["libraryItems"]</a>, defaultStatus: "published" | "unpublished")
|
||||
</pre>
|
||||
|
||||
**_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`
|
||||
|
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user