LibraryUnit.tsx 2.7 KB

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