parent
7ab0c1aba8
commit
6428b59ccb
23
src/actions/actionAddToLibrary.ts
Normal file
23
src/actions/actionAddToLibrary.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { register } from "./register";
|
||||||
|
import { getSelectedElements } from "../scene";
|
||||||
|
import { getNonDeletedElements } from "../element";
|
||||||
|
import { deepCopyElement } from "../element/newElement";
|
||||||
|
import { loadLibrary, saveLibrary } from "../data/localStorage";
|
||||||
|
|
||||||
|
export const actionAddToLibrary = register({
|
||||||
|
name: "addToLibrary",
|
||||||
|
perform: (elements, appState) => {
|
||||||
|
const selectedElements = getSelectedElements(
|
||||||
|
getNonDeletedElements(elements),
|
||||||
|
appState,
|
||||||
|
);
|
||||||
|
|
||||||
|
loadLibrary().then((items) => {
|
||||||
|
saveLibrary([...items, selectedElements.map(deepCopyElement)]);
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
contextMenuOrder: 6,
|
||||||
|
contextItemLabel: "labels.addToLibrary",
|
||||||
|
});
|
@ -49,3 +49,5 @@ export {
|
|||||||
export { actionGroup, actionUngroup } from "./actionGroup";
|
export { actionGroup, actionUngroup } from "./actionGroup";
|
||||||
|
|
||||||
export { actionGoToCollaborator } from "./actionNavigate";
|
export { actionGoToCollaborator } from "./actionNavigate";
|
||||||
|
|
||||||
|
export { actionAddToLibrary } from "./actionAddToLibrary";
|
||||||
|
@ -62,7 +62,8 @@ export type ActionName =
|
|||||||
| "toggleShortcuts"
|
| "toggleShortcuts"
|
||||||
| "group"
|
| "group"
|
||||||
| "ungroup"
|
| "ungroup"
|
||||||
| "goToCollaborator";
|
| "goToCollaborator"
|
||||||
|
| "addToLibrary";
|
||||||
|
|
||||||
export interface Action {
|
export interface Action {
|
||||||
name: ActionName;
|
name: ActionName;
|
||||||
|
@ -58,6 +58,7 @@ export const getDefaultAppState = (): AppState => {
|
|||||||
selectedGroupIds: {},
|
selectedGroupIds: {},
|
||||||
width: window.innerWidth,
|
width: window.innerWidth,
|
||||||
height: window.innerHeight,
|
height: window.innerHeight,
|
||||||
|
isLibraryOpen: false,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -76,6 +77,7 @@ export const clearAppStateForLocalStorage = (appState: AppState) => {
|
|||||||
errorMessage,
|
errorMessage,
|
||||||
showShortcutsDialog,
|
showShortcutsDialog,
|
||||||
editingLinearElement,
|
editingLinearElement,
|
||||||
|
isLibraryOpen,
|
||||||
...exportedState
|
...exportedState
|
||||||
} = appState;
|
} = appState;
|
||||||
return exportedState;
|
return exportedState;
|
||||||
|
@ -85,12 +85,21 @@ export const SelectedShapeActions = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const LIBRARY_ICON = (
|
||||||
|
// fa-th-large
|
||||||
|
<svg viewBox="0 0 512 512">
|
||||||
|
<path d="M296 32h192c13.255 0 24 10.745 24 24v160c0 13.255-10.745 24-24 24H296c-13.255 0-24-10.745-24-24V56c0-13.255 10.745-24 24-24zm-80 0H24C10.745 32 0 42.745 0 56v160c0 13.255 10.745 24 24 24h192c13.255 0 24-10.745 24-24V56c0-13.255-10.745-24-24-24zM0 296v160c0 13.255 10.745 24 24 24h192c13.255 0 24-10.745 24-24V296c0-13.255-10.745-24-24-24H24c-13.255 0-24 10.745-24 24zm296 184h192c13.255 0 24-10.745 24-24V296c0-13.255-10.745-24-24-24H296c-13.255 0-24 10.745-24 24v160c0 13.255 10.745 24 24 24z" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
export const ShapesSwitcher = ({
|
export const ShapesSwitcher = ({
|
||||||
elementType,
|
elementType,
|
||||||
setAppState,
|
setAppState,
|
||||||
|
isLibraryOpen,
|
||||||
}: {
|
}: {
|
||||||
elementType: ExcalidrawElement["type"];
|
elementType: ExcalidrawElement["type"];
|
||||||
setAppState: any;
|
setAppState: (appState: Partial<AppState>) => void;
|
||||||
|
isLibraryOpen: boolean;
|
||||||
}) => (
|
}) => (
|
||||||
<>
|
<>
|
||||||
{SHAPES.map(({ value, icon, key }, index) => {
|
{SHAPES.map(({ value, icon, key }, index) => {
|
||||||
@ -119,9 +128,21 @@ export const ShapesSwitcher = ({
|
|||||||
setCursorForShape(value);
|
setCursorForShape(value);
|
||||||
setAppState({});
|
setAppState({});
|
||||||
}}
|
}}
|
||||||
></ToolButton>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
<ToolButton
|
||||||
|
type="button"
|
||||||
|
icon={LIBRARY_ICON}
|
||||||
|
name="editor-library"
|
||||||
|
keyBindingLabel="9"
|
||||||
|
aria-keyshortcuts="9"
|
||||||
|
title={`${capitalizeString(t("toolBar.library"))} — 9`}
|
||||||
|
aria-label={capitalizeString(t("toolBar.library"))}
|
||||||
|
onClick={() => {
|
||||||
|
setAppState({ isLibraryOpen: !isLibraryOpen });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -299,6 +299,9 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
onLockToggle={this.toggleLock}
|
onLockToggle={this.toggleLock}
|
||||||
|
onInsertShape={(elements) =>
|
||||||
|
this.addElementsFromPasteOrLibrary(elements)
|
||||||
|
}
|
||||||
zenModeEnabled={zenModeEnabled}
|
zenModeEnabled={zenModeEnabled}
|
||||||
toggleZenMode={this.toggleZenMode}
|
toggleZenMode={this.toggleZenMode}
|
||||||
lng={getLanguage().lng}
|
lng={getLanguage().lng}
|
||||||
@ -870,7 +873,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
if (data.error) {
|
if (data.error) {
|
||||||
alert(data.error);
|
alert(data.error);
|
||||||
} else if (data.elements) {
|
} else if (data.elements) {
|
||||||
this.addElementsFromPaste(data.elements);
|
this.addElementsFromPasteOrLibrary(data.elements);
|
||||||
} else if (data.text) {
|
} else if (data.text) {
|
||||||
this.addTextFromPaste(data.text);
|
this.addTextFromPaste(data.text);
|
||||||
}
|
}
|
||||||
@ -879,8 +882,10 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
private addElementsFromPaste = (
|
private addElementsFromPasteOrLibrary = (
|
||||||
clipboardElements: readonly ExcalidrawElement[],
|
clipboardElements: readonly ExcalidrawElement[],
|
||||||
|
clientX = cursorX,
|
||||||
|
clientY = cursorY,
|
||||||
) => {
|
) => {
|
||||||
const [minX, minY, maxX, maxY] = getCommonBounds(clipboardElements);
|
const [minX, minY, maxX, maxY] = getCommonBounds(clipboardElements);
|
||||||
|
|
||||||
@ -888,7 +893,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
const elementsCenterY = distance(minY, maxY) / 2;
|
const elementsCenterY = distance(minY, maxY) / 2;
|
||||||
|
|
||||||
const { x, y } = viewportCoordsToSceneCoords(
|
const { x, y } = viewportCoordsToSceneCoords(
|
||||||
{ clientX: cursorX, clientY: cursorY },
|
{ clientX, clientY },
|
||||||
this.state,
|
this.state,
|
||||||
this.canvas,
|
this.canvas,
|
||||||
window.devicePixelRatio,
|
window.devicePixelRatio,
|
||||||
@ -911,6 +916,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
]);
|
]);
|
||||||
history.resumeRecording();
|
history.resumeRecording();
|
||||||
this.setState({
|
this.setState({
|
||||||
|
isLibraryOpen: false,
|
||||||
selectedElementIds: newElements.reduce((map, element) => {
|
selectedElementIds: newElements.reduce((map, element) => {
|
||||||
map[element.id] = true;
|
map[element.id] = true;
|
||||||
return map;
|
return map;
|
||||||
@ -1355,6 +1361,10 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (event.code === "Digit9") {
|
||||||
|
this.setState({ isLibraryOpen: !this.state.isLibraryOpen });
|
||||||
|
}
|
||||||
|
|
||||||
const shape = findShapeByKey(event.key);
|
const shape = findShapeByKey(event.key);
|
||||||
|
|
||||||
if (isArrowKey(event.key)) {
|
if (isArrowKey(event.key)) {
|
||||||
@ -3135,6 +3145,18 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private handleCanvasOnDrop = (event: React.DragEvent<HTMLCanvasElement>) => {
|
private handleCanvasOnDrop = (event: React.DragEvent<HTMLCanvasElement>) => {
|
||||||
|
const libraryShapes = event.dataTransfer.getData(
|
||||||
|
"application/vnd.excalidraw.json",
|
||||||
|
);
|
||||||
|
if (libraryShapes !== "") {
|
||||||
|
this.addElementsFromPasteOrLibrary(
|
||||||
|
JSON.parse(libraryShapes),
|
||||||
|
event.clientX,
|
||||||
|
event.clientY,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const file = event.dataTransfer?.files[0];
|
const file = event.dataTransfer?.files[0];
|
||||||
if (
|
if (
|
||||||
file?.type === "application/json" ||
|
file?.type === "application/json" ||
|
||||||
|
@ -1,5 +1,22 @@
|
|||||||
@import "open-color/open-color";
|
@import "open-color/open-color";
|
||||||
|
|
||||||
|
.layer-ui__library {
|
||||||
|
margin: auto;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layer-ui__library-message {
|
||||||
|
padding: 10px 20px;
|
||||||
|
max-width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layer-ui__library-items {
|
||||||
|
max-height: 50vh;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.layer-ui__wrapper {
|
.layer-ui__wrapper {
|
||||||
.encrypted-icon {
|
.encrypted-icon {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -1,10 +1,20 @@
|
|||||||
import React from "react";
|
import React, {
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
RefObject,
|
||||||
|
useEffect,
|
||||||
|
useCallback,
|
||||||
|
} from "react";
|
||||||
import { showSelectedShapeActions } from "../element";
|
import { showSelectedShapeActions } from "../element";
|
||||||
import { calculateScrollCenter } from "../scene";
|
import { calculateScrollCenter, getSelectedElements } from "../scene";
|
||||||
import { exportCanvas } from "../data";
|
import { exportCanvas } from "../data";
|
||||||
|
|
||||||
import { AppState } from "../types";
|
import { AppState, LibraryItems } from "../types";
|
||||||
import { NonDeletedExcalidrawElement } from "../element/types";
|
import {
|
||||||
|
NonDeletedExcalidrawElement,
|
||||||
|
ExcalidrawElement,
|
||||||
|
NonDeleted,
|
||||||
|
} from "../element/types";
|
||||||
|
|
||||||
import { ActionManager } from "../actions/manager";
|
import { ActionManager } from "../actions/manager";
|
||||||
import { Island } from "./Island";
|
import { Island } from "./Island";
|
||||||
@ -32,6 +42,8 @@ import { GitHubCorner } from "./GitHubCorner";
|
|||||||
import { Tooltip } from "./Tooltip";
|
import { Tooltip } from "./Tooltip";
|
||||||
|
|
||||||
import "./LayerUI.scss";
|
import "./LayerUI.scss";
|
||||||
|
import { LibraryUnit } from "./LibraryUnit";
|
||||||
|
import { loadLibrary, saveLibrary } from "../data/localStorage";
|
||||||
|
|
||||||
interface LayerUIProps {
|
interface LayerUIProps {
|
||||||
actionManager: ActionManager;
|
actionManager: ActionManager;
|
||||||
@ -43,11 +55,182 @@ interface LayerUIProps {
|
|||||||
onUsernameChange: (username: string) => void;
|
onUsernameChange: (username: string) => void;
|
||||||
onRoomDestroy: () => void;
|
onRoomDestroy: () => void;
|
||||||
onLockToggle: () => void;
|
onLockToggle: () => void;
|
||||||
|
onInsertShape: (elements: readonly NonDeleted<ExcalidrawElement>[]) => void;
|
||||||
zenModeEnabled: boolean;
|
zenModeEnabled: boolean;
|
||||||
toggleZenMode: () => void;
|
toggleZenMode: () => void;
|
||||||
lng: string;
|
lng: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function useOnClickOutside(
|
||||||
|
ref: RefObject<HTMLElement>,
|
||||||
|
cb: (event: MouseEvent) => void,
|
||||||
|
) {
|
||||||
|
useEffect(() => {
|
||||||
|
const listener = (event: MouseEvent) => {
|
||||||
|
if (!ref.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
event.target instanceof Element &&
|
||||||
|
(ref.current.contains(event.target) ||
|
||||||
|
!document.body.contains(event.target))
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cb(event);
|
||||||
|
};
|
||||||
|
document.addEventListener("pointerdown", listener, false);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener("pointerdown", listener);
|
||||||
|
};
|
||||||
|
}, [ref, cb]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const LibraryMenuItems = ({
|
||||||
|
library,
|
||||||
|
onRemoveFromLibrary,
|
||||||
|
onAddToLibrary,
|
||||||
|
onInsertShape,
|
||||||
|
pendingElements,
|
||||||
|
}: {
|
||||||
|
library: LibraryItems;
|
||||||
|
pendingElements: NonDeleted<ExcalidrawElement>[];
|
||||||
|
onClickOutside: (event: MouseEvent) => void;
|
||||||
|
onRemoveFromLibrary: (index: number) => void;
|
||||||
|
onInsertShape: (elements: readonly NonDeleted<ExcalidrawElement>[]) => void;
|
||||||
|
onAddToLibrary: (elements: NonDeleted<ExcalidrawElement>[]) => void;
|
||||||
|
}) => {
|
||||||
|
const numCells = library.length + (pendingElements.length > 0 ? 1 : 0);
|
||||||
|
const CELLS_PER_ROW = 3;
|
||||||
|
const numRows = Math.max(1, Math.ceil(numCells / CELLS_PER_ROW));
|
||||||
|
const rows = [];
|
||||||
|
let addedPendingElements = false;
|
||||||
|
|
||||||
|
for (let row = 0; row < numRows; row++) {
|
||||||
|
const i = CELLS_PER_ROW * row;
|
||||||
|
const children = [];
|
||||||
|
for (let j = 0; j < 3; j++) {
|
||||||
|
const shouldAddPendingElements: boolean =
|
||||||
|
pendingElements.length > 0 &&
|
||||||
|
!addedPendingElements &&
|
||||||
|
i + j >= library.length;
|
||||||
|
addedPendingElements = addedPendingElements || shouldAddPendingElements;
|
||||||
|
|
||||||
|
children.push(
|
||||||
|
<Stack.Col key={j}>
|
||||||
|
<LibraryUnit
|
||||||
|
elements={library[i + j]}
|
||||||
|
pendingElements={
|
||||||
|
shouldAddPendingElements ? pendingElements : undefined
|
||||||
|
}
|
||||||
|
onRemoveFromLibrary={onRemoveFromLibrary.bind(null, i + j)}
|
||||||
|
onClick={
|
||||||
|
shouldAddPendingElements
|
||||||
|
? onAddToLibrary.bind(null, pendingElements)
|
||||||
|
: onInsertShape.bind(null, library[i + j])
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Stack.Col>,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
rows.push(
|
||||||
|
<Stack.Row align="center" gap={1} key={row}>
|
||||||
|
{children}
|
||||||
|
</Stack.Row>,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack.Col align="center" gap={1} className="layer-ui__library-items">
|
||||||
|
{rows}
|
||||||
|
</Stack.Col>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const LibraryMenu = ({
|
||||||
|
onClickOutside,
|
||||||
|
onInsertShape,
|
||||||
|
pendingElements,
|
||||||
|
onAddToLibrary,
|
||||||
|
}: {
|
||||||
|
pendingElements: NonDeleted<ExcalidrawElement>[];
|
||||||
|
onClickOutside: (event: MouseEvent) => void;
|
||||||
|
onInsertShape: (elements: readonly NonDeleted<ExcalidrawElement>[]) => void;
|
||||||
|
onAddToLibrary: () => void;
|
||||||
|
}) => {
|
||||||
|
const ref = useRef<HTMLDivElement | null>(null);
|
||||||
|
useOnClickOutside(ref, onClickOutside);
|
||||||
|
|
||||||
|
const [libraryItems, setLibraryItems] = useState<LibraryItems>([]);
|
||||||
|
|
||||||
|
const [loadingState, setIsLoading] = useState<
|
||||||
|
"preloading" | "loading" | "ready"
|
||||||
|
>("preloading");
|
||||||
|
|
||||||
|
const loadingTimerRef = useRef<NodeJS.Timeout | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
Promise.race([
|
||||||
|
new Promise((resolve) => {
|
||||||
|
loadingTimerRef.current = setTimeout(() => {
|
||||||
|
resolve("loading");
|
||||||
|
}, 100);
|
||||||
|
}),
|
||||||
|
loadLibrary().then((items) => {
|
||||||
|
setLibraryItems(items);
|
||||||
|
setIsLoading("ready");
|
||||||
|
}),
|
||||||
|
]).then((data) => {
|
||||||
|
if (data === "loading") {
|
||||||
|
setIsLoading("loading");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return () => {
|
||||||
|
clearTimeout(loadingTimerRef.current!);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const removeFromLibrary = useCallback(async (indexToRemove) => {
|
||||||
|
const items = await loadLibrary();
|
||||||
|
const nextItems = items.filter((_, index) => index !== indexToRemove);
|
||||||
|
saveLibrary(nextItems);
|
||||||
|
setLibraryItems(nextItems);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const addToLibrary = useCallback(
|
||||||
|
async (elements: NonDeleted<ExcalidrawElement>[]) => {
|
||||||
|
const items = await loadLibrary();
|
||||||
|
const nextItems = [...items, elements];
|
||||||
|
onAddToLibrary();
|
||||||
|
saveLibrary(nextItems);
|
||||||
|
setLibraryItems(nextItems);
|
||||||
|
},
|
||||||
|
[onAddToLibrary],
|
||||||
|
);
|
||||||
|
|
||||||
|
return loadingState === "preloading" ? null : (
|
||||||
|
<Island padding={1} ref={ref} className="layer-ui__library">
|
||||||
|
{loadingState === "loading" ? (
|
||||||
|
<div className="layer-ui__library-message">
|
||||||
|
{t("labels.libraryLoadingMessage")}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<LibraryMenuItems
|
||||||
|
library={libraryItems}
|
||||||
|
onClickOutside={onClickOutside}
|
||||||
|
onRemoveFromLibrary={removeFromLibrary}
|
||||||
|
onAddToLibrary={addToLibrary}
|
||||||
|
onInsertShape={onInsertShape}
|
||||||
|
pendingElements={pendingElements}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Island>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const LayerUI = ({
|
const LayerUI = ({
|
||||||
actionManager,
|
actionManager,
|
||||||
appState,
|
appState,
|
||||||
@ -58,6 +241,7 @@ const LayerUI = ({
|
|||||||
onUsernameChange,
|
onUsernameChange,
|
||||||
onRoomDestroy,
|
onRoomDestroy,
|
||||||
onLockToggle,
|
onLockToggle,
|
||||||
|
onInsertShape,
|
||||||
zenModeEnabled,
|
zenModeEnabled,
|
||||||
toggleZenMode,
|
toggleZenMode,
|
||||||
}: LayerUIProps) => {
|
}: LayerUIProps) => {
|
||||||
@ -167,11 +351,33 @@ const LayerUI = ({
|
|||||||
</Section>
|
</Section>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const closeLibrary = useCallback(
|
||||||
|
(event) => {
|
||||||
|
setAppState({ isLibraryOpen: false });
|
||||||
|
},
|
||||||
|
[setAppState],
|
||||||
|
);
|
||||||
|
|
||||||
|
const deselectItems = useCallback(() => {
|
||||||
|
setAppState({
|
||||||
|
selectedElementIds: {},
|
||||||
|
selectedGroupIds: {},
|
||||||
|
});
|
||||||
|
}, [setAppState]);
|
||||||
|
|
||||||
const renderFixedSideContainer = () => {
|
const renderFixedSideContainer = () => {
|
||||||
const shouldRenderSelectedShapeActions = showSelectedShapeActions(
|
const shouldRenderSelectedShapeActions = showSelectedShapeActions(
|
||||||
appState,
|
appState,
|
||||||
elements,
|
elements,
|
||||||
);
|
);
|
||||||
|
const libraryMenu = appState.isLibraryOpen ? (
|
||||||
|
<LibraryMenu
|
||||||
|
pendingElements={getSelectedElements(elements, appState)}
|
||||||
|
onClickOutside={closeLibrary}
|
||||||
|
onInsertShape={onInsertShape}
|
||||||
|
onAddToLibrary={deselectItems}
|
||||||
|
/>
|
||||||
|
) : null;
|
||||||
return (
|
return (
|
||||||
<FixedSideContainer side="top">
|
<FixedSideContainer side="top">
|
||||||
<HintViewer appState={appState} elements={elements} />
|
<HintViewer appState={appState} elements={elements} />
|
||||||
@ -193,6 +399,7 @@ const LayerUI = ({
|
|||||||
<ShapesSwitcher
|
<ShapesSwitcher
|
||||||
elementType={appState.elementType}
|
elementType={appState.elementType}
|
||||||
setAppState={setAppState}
|
setAppState={setAppState}
|
||||||
|
isLibraryOpen={appState.isLibraryOpen}
|
||||||
/>
|
/>
|
||||||
</Stack.Row>
|
</Stack.Row>
|
||||||
</Island>
|
</Island>
|
||||||
@ -203,6 +410,7 @@ const LayerUI = ({
|
|||||||
title={t("toolBar.lock")}
|
title={t("toolBar.lock")}
|
||||||
/>
|
/>
|
||||||
</Stack.Row>
|
</Stack.Row>
|
||||||
|
{libraryMenu}
|
||||||
</Stack.Col>
|
</Stack.Col>
|
||||||
)}
|
)}
|
||||||
</Section>
|
</Section>
|
||||||
|
75
src/components/LibraryUnit.scss
Normal file
75
src/components/LibraryUnit.scss
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
.library-unit {
|
||||||
|
align-items: center;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
display: flex;
|
||||||
|
height: 126px; // match width
|
||||||
|
justify-content: center;
|
||||||
|
position: relative;
|
||||||
|
width: 126px; // exactly match the toolbar width when 3 are lined up + padding
|
||||||
|
}
|
||||||
|
|
||||||
|
.library-unit__dragger {
|
||||||
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.library-unit__dragger > svg {
|
||||||
|
flex-grow: 1;
|
||||||
|
max-height: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.library-unit__removeFromLibrary,
|
||||||
|
.library-unit__removeFromLibrary:hover,
|
||||||
|
.library-unit__removeFromLibrary:active {
|
||||||
|
align-items: center;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
position: absolute;
|
||||||
|
right: 5px;
|
||||||
|
top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.library-unit__removeFromLibrary > svg {
|
||||||
|
height: 16px;
|
||||||
|
width: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.library-unit__pulse {
|
||||||
|
transform: scale(1);
|
||||||
|
animation: library-unit__pulse-animation 1s ease-in infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.library-unit__adder {
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
margin-left: -10px;
|
||||||
|
margin-top: -10px;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.library-unit__active {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes library-unit__pulse-animation {
|
||||||
|
0% {
|
||||||
|
transform: scale(0.95);
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: scale(0.95);
|
||||||
|
}
|
||||||
|
}
|
93
src/components/LibraryUnit.tsx
Normal file
93
src/components/LibraryUnit.tsx
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
import React, { useRef, useEffect, useState } from "react";
|
||||||
|
import { exportToSvg } from "../scene/export";
|
||||||
|
import { ExcalidrawElement, NonDeleted } from "../element/types";
|
||||||
|
import { close } from "../components/icons";
|
||||||
|
|
||||||
|
import "./LibraryUnit.scss";
|
||||||
|
import { t } from "../i18n";
|
||||||
|
|
||||||
|
// fa-plus
|
||||||
|
const PLUS_ICON = (
|
||||||
|
<svg viewBox="0 0 1792 1792">
|
||||||
|
<path d="M1600 736v192q0 40-28 68t-68 28h-416v416q0 40-28 68t-68 28h-192q-40 0-68-28t-28-68v-416h-416q-40 0-68-28t-28-68v-192q0-40 28-68t68-28h416v-416q0-40 28-68t68-28h192q40 0 68 28t28 68v416h416q40 0 68 28t28 68z" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const LibraryUnit = ({
|
||||||
|
elements,
|
||||||
|
pendingElements,
|
||||||
|
onRemoveFromLibrary,
|
||||||
|
onClick,
|
||||||
|
}: {
|
||||||
|
elements?: NonDeleted<ExcalidrawElement>[];
|
||||||
|
pendingElements?: NonDeleted<ExcalidrawElement>[];
|
||||||
|
onRemoveFromLibrary: () => void;
|
||||||
|
onClick: () => void;
|
||||||
|
}) => {
|
||||||
|
const ref = useRef<HTMLDivElement | null>(null);
|
||||||
|
useEffect(() => {
|
||||||
|
const elementsToRender = elements || pendingElements;
|
||||||
|
if (!elementsToRender) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const svg = exportToSvg(elementsToRender, {
|
||||||
|
exportBackground: false,
|
||||||
|
viewBackgroundColor: "#fff",
|
||||||
|
shouldAddWatermark: false,
|
||||||
|
});
|
||||||
|
for (const child of ref.current!.children) {
|
||||||
|
if (child.tagName !== "svg") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ref.current!.removeChild(child);
|
||||||
|
}
|
||||||
|
ref.current!.appendChild(svg);
|
||||||
|
|
||||||
|
const current = ref.current!;
|
||||||
|
return () => {
|
||||||
|
current.removeChild(svg);
|
||||||
|
};
|
||||||
|
}, [elements, pendingElements]);
|
||||||
|
|
||||||
|
const [isHovered, setIsHovered] = useState(false);
|
||||||
|
|
||||||
|
const adder = isHovered && pendingElements && (
|
||||||
|
<div className="library-unit__adder">{PLUS_ICON}</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`library-unit ${
|
||||||
|
elements || pendingElements ? "library-unit__active" : ""
|
||||||
|
}`}
|
||||||
|
onMouseEnter={() => setIsHovered(true)}
|
||||||
|
onMouseLeave={() => setIsHovered(false)}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={`library-unit__dragger ${
|
||||||
|
!!pendingElements ? "library-unit__pulse" : ""
|
||||||
|
}`}
|
||||||
|
ref={ref}
|
||||||
|
draggable={!!elements}
|
||||||
|
onClick={!!elements || !!pendingElements ? onClick : undefined}
|
||||||
|
onDragStart={(event) => {
|
||||||
|
setIsHovered(false);
|
||||||
|
event.dataTransfer.setData(
|
||||||
|
"application/vnd.excalidraw.json",
|
||||||
|
JSON.stringify(elements),
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{adder}
|
||||||
|
{elements && isHovered && (
|
||||||
|
<button
|
||||||
|
className="library-unit__removeFromLibrary"
|
||||||
|
aria-label={t("labels.removeFromLibrary")}
|
||||||
|
onClick={onRemoveFromLibrary}
|
||||||
|
>
|
||||||
|
{close}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -56,6 +56,7 @@ export const MobileMenu = ({
|
|||||||
<ShapesSwitcher
|
<ShapesSwitcher
|
||||||
elementType={appState.elementType}
|
elementType={appState.elementType}
|
||||||
setAppState={setAppState}
|
setAppState={setAppState}
|
||||||
|
isLibraryOpen={appState.isLibraryOpen}
|
||||||
/>
|
/>
|
||||||
</Stack.Row>
|
</Stack.Row>
|
||||||
</Island>
|
</Island>
|
||||||
|
@ -63,6 +63,11 @@ export const ToolButton = React.forwardRef((props: ToolButtonProps, ref) => {
|
|||||||
>
|
>
|
||||||
<div className="ToolIcon__icon" aria-hidden="true">
|
<div className="ToolIcon__icon" aria-hidden="true">
|
||||||
{props.icon || props.label}
|
{props.icon || props.label}
|
||||||
|
{props.keyBindingLabel && (
|
||||||
|
<span className="ToolIcon__keybinding">
|
||||||
|
{props.keyBindingLabel}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
{props.showAriaLabel && (
|
{props.showAriaLabel && (
|
||||||
<div className="ToolIcon__label">{props["aria-label"]}</div>
|
<div className="ToolIcon__label">{props["aria-label"]}</div>
|
||||||
|
@ -348,11 +348,12 @@ export const exportCanvas = async (
|
|||||||
window.alert(t("alerts.couldNotCopyToClipboard"));
|
window.alert(t("alerts.couldNotCopyToClipboard"));
|
||||||
}
|
}
|
||||||
} else if (type === "backend") {
|
} else if (type === "backend") {
|
||||||
const appState = getDefaultAppState();
|
exportToBackend(elements, {
|
||||||
if (exportBackground) {
|
...appState,
|
||||||
appState.viewBackgroundColor = viewBackgroundColor;
|
viewBackgroundColor: exportBackground
|
||||||
}
|
? appState.viewBackgroundColor
|
||||||
exportToBackend(elements, appState);
|
: getDefaultAppState().viewBackgroundColor,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// clean up the DOM
|
// clean up the DOM
|
||||||
|
@ -1,11 +1,54 @@
|
|||||||
import { ExcalidrawElement } from "../element/types";
|
import { ExcalidrawElement } from "../element/types";
|
||||||
import { AppState } from "../types";
|
import { AppState, LibraryItems } from "../types";
|
||||||
import { clearAppStateForLocalStorage } from "../appState";
|
import { clearAppStateForLocalStorage } from "../appState";
|
||||||
import { restore } from "./restore";
|
import { restore } from "./restore";
|
||||||
|
|
||||||
const LOCAL_STORAGE_KEY = "excalidraw";
|
const LOCAL_STORAGE_KEY = "excalidraw";
|
||||||
const LOCAL_STORAGE_KEY_STATE = "excalidraw-state";
|
const LOCAL_STORAGE_KEY_STATE = "excalidraw-state";
|
||||||
const LOCAL_STORAGE_KEY_COLLAB = "excalidraw-collab";
|
const LOCAL_STORAGE_KEY_COLLAB = "excalidraw-collab";
|
||||||
|
const LOCAL_STORAGE_KEY_LIBRARY = "excalidraw-library";
|
||||||
|
|
||||||
|
let _LATEST_LIBRARY_ITEMS: LibraryItems | null = null;
|
||||||
|
export const loadLibrary = (): Promise<LibraryItems> => {
|
||||||
|
return new Promise(async (resolve) => {
|
||||||
|
if (_LATEST_LIBRARY_ITEMS) {
|
||||||
|
return resolve(JSON.parse(JSON.stringify(_LATEST_LIBRARY_ITEMS)));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = localStorage.getItem(LOCAL_STORAGE_KEY_LIBRARY);
|
||||||
|
if (!data) {
|
||||||
|
return resolve([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const items = (JSON.parse(data) as ExcalidrawElement[][]).map(
|
||||||
|
(elements) => restore(elements, null).elements,
|
||||||
|
) as Mutable<LibraryItems>;
|
||||||
|
|
||||||
|
// clone to ensure we don't mutate the cached library elements in the app
|
||||||
|
_LATEST_LIBRARY_ITEMS = JSON.parse(JSON.stringify(items));
|
||||||
|
|
||||||
|
resolve(items);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
resolve([]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const saveLibrary = (items: LibraryItems) => {
|
||||||
|
const prevLibraryItems = _LATEST_LIBRARY_ITEMS;
|
||||||
|
try {
|
||||||
|
const serializedItems = JSON.stringify(items);
|
||||||
|
// cache optimistically so that consumers have access to the latest
|
||||||
|
// immediately
|
||||||
|
_LATEST_LIBRARY_ITEMS = JSON.parse(serializedItems);
|
||||||
|
localStorage.setItem(LOCAL_STORAGE_KEY_LIBRARY, serializedItems);
|
||||||
|
} catch (e) {
|
||||||
|
_LATEST_LIBRARY_ITEMS = prevLibraryItems;
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const saveUsernameToLocalStorage = (username: string) => {
|
export const saveUsernameToLocalStorage = (username: string) => {
|
||||||
try {
|
try {
|
||||||
|
@ -65,7 +65,10 @@
|
|||||||
"group": "Group selection",
|
"group": "Group selection",
|
||||||
"ungroup": "Ungroup selection",
|
"ungroup": "Ungroup selection",
|
||||||
"collaborators": "Collaborators",
|
"collaborators": "Collaborators",
|
||||||
"toggleGridMode": "Toggle grid mode"
|
"toggleGridMode": "Toggle grid mode",
|
||||||
|
"addToLibrary": "Add to library",
|
||||||
|
"removeFromLibrary": "Remove from library",
|
||||||
|
"libraryLoadingMessage": "Loading library..."
|
||||||
},
|
},
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"clearReset": "Reset the canvas",
|
"clearReset": "Reset the canvas",
|
||||||
@ -115,6 +118,7 @@
|
|||||||
"arrow": "Arrow",
|
"arrow": "Arrow",
|
||||||
"line": "Line",
|
"line": "Line",
|
||||||
"text": "Text",
|
"text": "Text",
|
||||||
|
"library": "Library",
|
||||||
"lock": "Keep selected tool active after drawing"
|
"lock": "Keep selected tool active after drawing"
|
||||||
},
|
},
|
||||||
"headings": {
|
"headings": {
|
||||||
|
@ -27,6 +27,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -427,6 +428,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -636,6 +638,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -762,6 +765,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -1024,6 +1028,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -1188,6 +1193,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -1390,6 +1396,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -1598,6 +1605,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -1907,6 +1915,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -2302,6 +2311,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -4090,6 +4100,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -4216,6 +4227,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -4342,6 +4354,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -4468,6 +4481,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -4616,6 +4630,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -4764,6 +4779,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -4912,6 +4928,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -5060,6 +5077,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -5186,6 +5204,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -5312,6 +5331,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -5460,6 +5480,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -5586,6 +5607,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -5734,6 +5756,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -6374,6 +6397,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -6583,6 +6607,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -6650,6 +6675,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -6715,6 +6741,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -7537,6 +7564,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -7936,6 +7964,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -8252,6 +8281,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -8489,6 +8519,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -8651,6 +8682,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -9422,6 +9454,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -10094,6 +10127,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -10671,6 +10705,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -11157,6 +11192,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -11599,6 +11635,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -11956,6 +11993,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -12232,6 +12270,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -12431,6 +12470,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -13253,6 +13293,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -13974,6 +14015,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -14598,6 +14640,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -15129,6 +15172,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -15402,6 +15446,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -15613,6 +15658,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -15892,6 +15938,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -15957,6 +16004,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -16083,6 +16131,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -16148,6 +16197,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -16802,6 +16852,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -16869,6 +16920,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -17297,6 +17349,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
@ -17373,6 +17426,7 @@ Object {
|
|||||||
"gridSize": null,
|
"gridSize": null,
|
||||||
"height": 768,
|
"height": 768,
|
||||||
"isCollaborating": false,
|
"isCollaborating": false,
|
||||||
|
"isLibraryOpen": false,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"isResizing": false,
|
"isResizing": false,
|
||||||
"isRotating": false,
|
"isRotating": false,
|
||||||
|
@ -884,6 +884,7 @@ describe("regression tests", () => {
|
|||||||
"Copy styles",
|
"Copy styles",
|
||||||
"Paste styles",
|
"Paste styles",
|
||||||
"Delete",
|
"Delete",
|
||||||
|
"Add to library",
|
||||||
"Send backward",
|
"Send backward",
|
||||||
"Bring forward",
|
"Bring forward",
|
||||||
"Send to back",
|
"Send to back",
|
||||||
@ -892,7 +893,7 @@ describe("regression tests", () => {
|
|||||||
];
|
];
|
||||||
|
|
||||||
expect(contextMenu).not.toBeNull();
|
expect(contextMenu).not.toBeNull();
|
||||||
expect(contextMenu?.children.length).toBe(8);
|
expect(contextMenu?.children.length).toBe(9);
|
||||||
options?.forEach((opt, i) => {
|
options?.forEach((opt, i) => {
|
||||||
expect(opt.textContent).toBe(expectedOptions[i]);
|
expect(opt.textContent).toBe(expectedOptions[i]);
|
||||||
});
|
});
|
||||||
@ -926,6 +927,7 @@ describe("regression tests", () => {
|
|||||||
"Paste styles",
|
"Paste styles",
|
||||||
"Delete",
|
"Delete",
|
||||||
"Group selection",
|
"Group selection",
|
||||||
|
"Add to library",
|
||||||
"Send backward",
|
"Send backward",
|
||||||
"Bring forward",
|
"Bring forward",
|
||||||
"Send to back",
|
"Send to back",
|
||||||
@ -934,7 +936,7 @@ describe("regression tests", () => {
|
|||||||
];
|
];
|
||||||
|
|
||||||
expect(contextMenu).not.toBeNull();
|
expect(contextMenu).not.toBeNull();
|
||||||
expect(contextMenu?.children.length).toBe(9);
|
expect(contextMenu?.children.length).toBe(10);
|
||||||
options?.forEach((opt, i) => {
|
options?.forEach((opt, i) => {
|
||||||
expect(opt.textContent).toBe(expectedOptions[i]);
|
expect(opt.textContent).toBe(expectedOptions[i]);
|
||||||
});
|
});
|
||||||
@ -973,6 +975,7 @@ describe("regression tests", () => {
|
|||||||
"Delete",
|
"Delete",
|
||||||
"Group selection",
|
"Group selection",
|
||||||
"Ungroup selection",
|
"Ungroup selection",
|
||||||
|
"Add to library",
|
||||||
"Send backward",
|
"Send backward",
|
||||||
"Bring forward",
|
"Bring forward",
|
||||||
"Send to back",
|
"Send to back",
|
||||||
@ -981,7 +984,7 @@ describe("regression tests", () => {
|
|||||||
];
|
];
|
||||||
|
|
||||||
expect(contextMenu).not.toBeNull();
|
expect(contextMenu).not.toBeNull();
|
||||||
expect(contextMenu?.children.length).toBe(10);
|
expect(contextMenu?.children.length).toBe(11);
|
||||||
options?.forEach((opt, i) => {
|
options?.forEach((opt, i) => {
|
||||||
expect(opt.textContent).toBe(expectedOptions[i]);
|
expect(opt.textContent).toBe(expectedOptions[i]);
|
||||||
});
|
});
|
||||||
|
@ -81,6 +81,8 @@ export type AppState = {
|
|||||||
editingGroupId: GroupId | null;
|
editingGroupId: GroupId | null;
|
||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
|
|
||||||
|
isLibraryOpen: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type PointerCoords = Readonly<{
|
export type PointerCoords = Readonly<{
|
||||||
@ -103,3 +105,5 @@ export declare class GestureEvent extends UIEvent {
|
|||||||
export type SocketUpdateData = SocketUpdateDataSource[keyof SocketUpdateDataSource] & {
|
export type SocketUpdateData = SocketUpdateDataSource[keyof SocketUpdateDataSource] & {
|
||||||
_brand: "socketUpdateData";
|
_brand: "socketUpdateData";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type LibraryItems = readonly NonDeleted<ExcalidrawElement>[][];
|
||||||
|
Loading…
x
Reference in New Issue
Block a user