feat: prefer hash when importing libraries & expose importLibrary (#3320)
This commit is contained in:
parent
5d26c15daf
commit
cf9e29834d
@ -58,6 +58,8 @@ import {
|
|||||||
TAP_TWICE_TIMEOUT,
|
TAP_TWICE_TIMEOUT,
|
||||||
TEXT_TO_CENTER_SNAP_THRESHOLD,
|
TEXT_TO_CENTER_SNAP_THRESHOLD,
|
||||||
TOUCH_CTX_MENU_TIMEOUT,
|
TOUCH_CTX_MENU_TIMEOUT,
|
||||||
|
URL_HASH_KEYS,
|
||||||
|
URL_QUERY_KEYS,
|
||||||
ZOOM_STEP,
|
ZOOM_STEP,
|
||||||
} from "../constants";
|
} from "../constants";
|
||||||
import { loadFromBlob } from "../data";
|
import { loadFromBlob } from "../data";
|
||||||
@ -278,6 +280,7 @@ export type ExcalidrawImperativeAPI = {
|
|||||||
getSceneElements: InstanceType<typeof App>["getSceneElements"];
|
getSceneElements: InstanceType<typeof App>["getSceneElements"];
|
||||||
getAppState: () => InstanceType<typeof App>["state"];
|
getAppState: () => InstanceType<typeof App>["state"];
|
||||||
setCanvasOffsets: InstanceType<typeof App>["setCanvasOffsets"];
|
setCanvasOffsets: InstanceType<typeof App>["setCanvasOffsets"];
|
||||||
|
importLibrary: InstanceType<typeof App>["importLibraryFromUrl"];
|
||||||
readyPromise: ResolvablePromise<ExcalidrawImperativeAPI>;
|
readyPromise: ResolvablePromise<ExcalidrawImperativeAPI>;
|
||||||
ready: true;
|
ready: true;
|
||||||
};
|
};
|
||||||
@ -338,6 +341,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
getSceneElements: this.getSceneElements,
|
getSceneElements: this.getSceneElements,
|
||||||
getAppState: () => this.state,
|
getAppState: () => this.state,
|
||||||
setCanvasOffsets: this.setCanvasOffsets,
|
setCanvasOffsets: this.setCanvasOffsets,
|
||||||
|
importLibrary: this.importLibraryFromUrl,
|
||||||
} as const;
|
} as const;
|
||||||
if (typeof excalidrawRef === "function") {
|
if (typeof excalidrawRef === "function") {
|
||||||
excalidrawRef(api);
|
excalidrawRef(api);
|
||||||
@ -606,7 +610,16 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private importLibraryFromUrl = async (url: string) => {
|
private importLibraryFromUrl = async (url: string) => {
|
||||||
window.history.replaceState({}, APP_NAME, window.location.origin);
|
if (window.location.hash.includes(URL_HASH_KEYS.addLibrary)) {
|
||||||
|
const hash = new URLSearchParams(window.location.hash.slice(1));
|
||||||
|
hash.delete(URL_HASH_KEYS.addLibrary);
|
||||||
|
window.history.replaceState({}, APP_NAME, `#${hash.toString()}`);
|
||||||
|
} else if (window.location.search.includes(URL_QUERY_KEYS.addLibrary)) {
|
||||||
|
const query = new URLSearchParams(window.location.search);
|
||||||
|
query.delete(URL_QUERY_KEYS.addLibrary);
|
||||||
|
window.history.replaceState({}, APP_NAME, `?${query.toString()}`);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const request = await fetch(decodeURIComponent(url));
|
const request = await fetch(decodeURIComponent(url));
|
||||||
const blob = await request.blob();
|
const blob = await request.blob();
|
||||||
@ -620,9 +633,11 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
await Library.importLibrary(blob);
|
await Library.importLibrary(blob);
|
||||||
this.setState({
|
// hack to rerender the library items after import
|
||||||
isLibraryOpen: true,
|
if (this.state.isLibraryOpen) {
|
||||||
});
|
this.setState({ isLibraryOpen: false });
|
||||||
|
}
|
||||||
|
this.setState({ isLibraryOpen: true });
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
window.alert(t("alerts.errorLoadingLibrary"));
|
window.alert(t("alerts.errorLoadingLibrary"));
|
||||||
@ -718,12 +733,18 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
commitToHistory: true,
|
commitToHistory: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const addToLibraryUrl = new URLSearchParams(window.location.search).get(
|
const libraryUrl =
|
||||||
"addLibrary",
|
// current
|
||||||
|
new URLSearchParams(window.location.hash.slice(1)).get(
|
||||||
|
URL_HASH_KEYS.addLibrary,
|
||||||
|
) ||
|
||||||
|
// legacy, kept for compat reasons
|
||||||
|
new URLSearchParams(window.location.search).get(
|
||||||
|
URL_QUERY_KEYS.addLibrary,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (addToLibraryUrl) {
|
if (libraryUrl) {
|
||||||
await this.importLibraryFromUrl(addToLibraryUrl);
|
await this.importLibraryFromUrl(libraryUrl);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -179,7 +179,7 @@ const LibraryMenuItems = ({
|
|||||||
<a
|
<a
|
||||||
href={`https://libraries.excalidraw.com?target=${
|
href={`https://libraries.excalidraw.com?target=${
|
||||||
window.name || "_blank"
|
window.name || "_blank"
|
||||||
}&referrer=${referrer}`}
|
}&referrer=${referrer}&useHash=true`}
|
||||||
target="_excalidraw_libraries"
|
target="_excalidraw_libraries"
|
||||||
>
|
>
|
||||||
{t("labels.libraries")}
|
{t("labels.libraries")}
|
||||||
|
@ -116,3 +116,11 @@ export const MODES = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const THEME_FILTER = cssVariables.themeFilter;
|
export const THEME_FILTER = cssVariables.themeFilter;
|
||||||
|
|
||||||
|
export const URL_QUERY_KEYS = {
|
||||||
|
addLibrary: "addLibrary",
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const URL_HASH_KEYS = {
|
||||||
|
addLibrary: "addLibrary",
|
||||||
|
} as const;
|
||||||
|
@ -12,7 +12,13 @@ import { getDefaultAppState } from "../appState";
|
|||||||
import { ExcalidrawImperativeAPI } from "../components/App";
|
import { ExcalidrawImperativeAPI } from "../components/App";
|
||||||
import { ErrorDialog } from "../components/ErrorDialog";
|
import { ErrorDialog } from "../components/ErrorDialog";
|
||||||
import { TopErrorBoundary } from "../components/TopErrorBoundary";
|
import { TopErrorBoundary } from "../components/TopErrorBoundary";
|
||||||
import { APP_NAME, EVENT, TITLE_TIMEOUT, VERSION_TIMEOUT } from "../constants";
|
import {
|
||||||
|
APP_NAME,
|
||||||
|
EVENT,
|
||||||
|
TITLE_TIMEOUT,
|
||||||
|
URL_HASH_KEYS,
|
||||||
|
VERSION_TIMEOUT,
|
||||||
|
} from "../constants";
|
||||||
import { loadFromBlob } from "../data/blob";
|
import { loadFromBlob } from "../data/blob";
|
||||||
import { DataState, ImportedDataState } from "../data/types";
|
import { DataState, ImportedDataState } from "../data/types";
|
||||||
import {
|
import {
|
||||||
@ -213,12 +219,25 @@ function ExcalidrawWrapper() {
|
|||||||
initialStatePromiseRef.current.promise.resolve(scene);
|
initialStatePromiseRef.current.promise.resolve(scene);
|
||||||
});
|
});
|
||||||
|
|
||||||
const onHashChange = (_: HashChangeEvent) => {
|
const onHashChange = (event: HashChangeEvent) => {
|
||||||
|
event.preventDefault();
|
||||||
|
const libraryUrl = new URLSearchParams(window.location.hash.slice(1)).get(
|
||||||
|
URL_HASH_KEYS.addLibrary,
|
||||||
|
);
|
||||||
|
if (libraryUrl) {
|
||||||
|
// If hash changed and it contains library url, import it and replace
|
||||||
|
// the url to its previous state (important in case of collaboration
|
||||||
|
// and similar).
|
||||||
|
// Using history API won't trigger another hashchange.
|
||||||
|
window.history.replaceState({}, "", event.oldURL);
|
||||||
|
excalidrawAPI.importLibrary(libraryUrl);
|
||||||
|
} else {
|
||||||
initializeScene({ collabAPI }).then((scene) => {
|
initializeScene({ collabAPI }).then((scene) => {
|
||||||
if (scene) {
|
if (scene) {
|
||||||
excalidrawAPI.updateScene(scene);
|
excalidrawAPI.updateScene(scene);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const titleTimeout = setTimeout(
|
const titleTimeout = setTimeout(
|
||||||
|
@ -18,6 +18,8 @@ Please add the latest change on the top under the correct section.
|
|||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
|
- #### BREAKING CHANGE
|
||||||
|
Use `location.hash` when importing libraries to fix installation issues. This will require host apps to add a `hashchange` listener and call the newly exposed `excalidrawAPI.importLibrary(url)` API when applicable [#3320](https://github.com/excalidraw/excalidraw/pull/3320).
|
||||||
- Append `location.pathname` to `libraryReturnUrl` default url [#3325](https://github.com/excalidraw/excalidraw/pull/3325).
|
- Append `location.pathname` to `libraryReturnUrl` default url [#3325](https://github.com/excalidraw/excalidraw/pull/3325).
|
||||||
|
|
||||||
## 0.5.0 (2021-03-21)
|
## 0.5.0 (2021-03-21)
|
||||||
|
@ -473,6 +473,7 @@ You can pass a `ref` when you want to access some excalidraw APIs. We expose the
|
|||||||
| history | `{ clear: () => void }` | This is the history API. `history.clear()` will clear the history |
|
| history | `{ clear: () => void }` | This is the history API. `history.clear()` will clear the history |
|
||||||
| setScrollToContent | <pre> (<a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L78">ExcalidrawElement[]</a>) => void </pre> | Scroll to the nearest element to center |
|
| setScrollToContent | <pre> (<a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L78">ExcalidrawElement[]</a>) => void </pre> | Scroll to the nearest element to center |
|
||||||
| setCanvasOffsets | `() => void` | Updates the offsets for the Excalidraw component so that the coordinates are computed correctly (for example the cursor position). You should call this API when your app changes the dimensions/position of the Excalidraw container, such as when toggling a sidebar. You don't have to call this when the position is changed on page scroll (we handled that ourselves). |
|
| setCanvasOffsets | `() => void` | Updates the offsets for the Excalidraw component so that the coordinates are computed correctly (for example the cursor position). You should call this API when your app changes the dimensions/position of the Excalidraw container, such as when toggling a sidebar. You don't have to call this when the position is changed on page scroll (we handled that ourselves). |
|
||||||
|
| importLibrary | `(url: string) => void` | Imports library from given URL. You should call this on `hashchange`, passing the `addLibrary` value if you detect it. |
|
||||||
|
|
||||||
#### `readyPromise`
|
#### `readyPromise`
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user