parent
7ab0c1aba8
commit
6428b59ccb
@ -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",
|
||||||
|
});
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -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>
|
||||||
|
);
|
||||||
|
};
|
Loading…
Reference in New Issue