LibraryUnit.tsx 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. import React, { useRef, useEffect, useState } from "react";
  2. import { exportToSvg } from "../scene/export";
  3. import { close } from "../components/icons";
  4. import "./LibraryUnit.scss";
  5. import { t } from "../i18n";
  6. import useIsMobile from "../is-mobile";
  7. import { LibraryItem } from "../types";
  8. // fa-plus
  9. const PLUS_ICON = (
  10. <svg viewBox="0 0 1792 1792">
  11. <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" />
  12. </svg>
  13. );
  14. export const LibraryUnit = ({
  15. elements,
  16. pendingElements,
  17. onRemoveFromLibrary,
  18. onClick,
  19. }: {
  20. elements?: LibraryItem;
  21. pendingElements?: LibraryItem;
  22. onRemoveFromLibrary: () => void;
  23. onClick: () => void;
  24. }) => {
  25. const ref = useRef<HTMLDivElement | null>(null);
  26. useEffect(() => {
  27. const elementsToRender = elements || pendingElements;
  28. if (!elementsToRender) {
  29. return;
  30. }
  31. const svg = exportToSvg(elementsToRender, {
  32. exportBackground: false,
  33. viewBackgroundColor: "#fff",
  34. shouldAddWatermark: false,
  35. });
  36. for (const child of ref.current!.children) {
  37. if (child.tagName !== "svg") {
  38. continue;
  39. }
  40. ref.current!.removeChild(child);
  41. }
  42. ref.current!.appendChild(svg);
  43. const current = ref.current!;
  44. return () => {
  45. current.removeChild(svg);
  46. };
  47. }, [elements, pendingElements]);
  48. const [isHovered, setIsHovered] = useState(false);
  49. const isMobile = useIsMobile();
  50. const adder = (isHovered || isMobile) && pendingElements && (
  51. <div className="library-unit__adder">{PLUS_ICON}</div>
  52. );
  53. return (
  54. <div
  55. className={`library-unit ${
  56. elements || pendingElements ? "library-unit__active" : ""
  57. }`}
  58. onMouseEnter={() => setIsHovered(true)}
  59. onMouseLeave={() => setIsHovered(false)}
  60. >
  61. <div
  62. className={`library-unit__dragger ${
  63. !!pendingElements ? "library-unit__pulse" : ""
  64. }`}
  65. ref={ref}
  66. draggable={!!elements}
  67. onClick={!!elements || !!pendingElements ? onClick : undefined}
  68. onDragStart={(event) => {
  69. setIsHovered(false);
  70. event.dataTransfer.setData(
  71. "application/vnd.excalidrawlib+json",
  72. JSON.stringify(elements),
  73. );
  74. }}
  75. />
  76. {adder}
  77. {elements && (isHovered || isMobile) && (
  78. <button
  79. className="library-unit__removeFromLibrary"
  80. aria-label={t("labels.removeFromLibrary")}
  81. onClick={onRemoveFromLibrary}
  82. >
  83. {close}
  84. </button>
  85. )}
  86. </div>
  87. );
  88. };