LibraryUnit.tsx 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. import clsx from "clsx";
  2. import oc from "open-color";
  3. import { useEffect, useRef, useState } from "react";
  4. import { useDevice } from "../components/App";
  5. import { exportToSvg } from "../scene/export";
  6. import { LibraryItem } from "../types";
  7. import "./LibraryUnit.scss";
  8. import { CheckboxItem } from "./CheckboxItem";
  9. import { PlusIcon } from "./icons";
  10. export const LibraryUnit = ({
  11. id,
  12. elements,
  13. isPending,
  14. onClick,
  15. selected,
  16. onToggle,
  17. onDrag,
  18. }: {
  19. id: LibraryItem["id"] | /** for pending item */ null;
  20. elements?: LibraryItem["elements"];
  21. isPending?: boolean;
  22. onClick: () => void;
  23. selected: boolean;
  24. onToggle: (id: string, event: React.MouseEvent) => void;
  25. onDrag: (id: string, event: React.DragEvent) => void;
  26. }) => {
  27. const ref = useRef<HTMLDivElement | null>(null);
  28. useEffect(() => {
  29. const node = ref.current;
  30. if (!node) {
  31. return;
  32. }
  33. (async () => {
  34. if (!elements) {
  35. return;
  36. }
  37. const svg = await exportToSvg(
  38. elements,
  39. {
  40. exportBackground: false,
  41. viewBackgroundColor: oc.white,
  42. },
  43. null,
  44. );
  45. svg.querySelector(".style-fonts")?.remove();
  46. node.innerHTML = svg.outerHTML;
  47. })();
  48. return () => {
  49. node.innerHTML = "";
  50. };
  51. }, [elements]);
  52. const [isHovered, setIsHovered] = useState(false);
  53. const isMobile = useDevice().isMobile;
  54. const adder = isPending && (
  55. <div className="library-unit__adder">{PlusIcon}</div>
  56. );
  57. return (
  58. <div
  59. className={clsx("library-unit", {
  60. "library-unit__active": elements,
  61. "library-unit--hover": elements && isHovered,
  62. "library-unit--selected": selected,
  63. })}
  64. onMouseEnter={() => setIsHovered(true)}
  65. onMouseLeave={() => setIsHovered(false)}
  66. >
  67. <div
  68. className={clsx("library-unit__dragger", {
  69. "library-unit__pulse": !!isPending,
  70. })}
  71. ref={ref}
  72. draggable={!!elements}
  73. onClick={
  74. !!elements || !!isPending
  75. ? (event) => {
  76. if (id && event.shiftKey) {
  77. onToggle(id, event);
  78. } else {
  79. onClick();
  80. }
  81. }
  82. : undefined
  83. }
  84. onDragStart={(event) => {
  85. if (!id) {
  86. event.preventDefault();
  87. return;
  88. }
  89. setIsHovered(false);
  90. onDrag(id, event);
  91. }}
  92. />
  93. {adder}
  94. {id && elements && (isHovered || isMobile || selected) && (
  95. <CheckboxItem
  96. checked={selected}
  97. onChange={(checked, event) => onToggle(id, event)}
  98. className="library-unit__checkbox"
  99. />
  100. )}
  101. </div>
  102. );
  103. };