diff options
| author | kj_sh604 | 2026-03-15 16:19:35 -0400 |
|---|---|---|
| committer | kj_sh604 | 2026-03-15 16:19:35 -0400 |
| commit | 6ec259a0e71174651bae95d4628138bf6fd68742 (patch) | |
| tree | 5e33c6a5ec091ecabfcb257fdc7b6a88ed8754ac /packages/excalidraw/components/LibraryUnit.tsx | |
| parent | 16c8578b15c727f22921f8a80a56ee4d4e7f2272 (diff) | |
refactor: packages/
Diffstat (limited to 'packages/excalidraw/components/LibraryUnit.tsx')
| -rw-r--r-- | packages/excalidraw/components/LibraryUnit.tsx | 108 |
1 files changed, 108 insertions, 0 deletions
diff --git a/packages/excalidraw/components/LibraryUnit.tsx b/packages/excalidraw/components/LibraryUnit.tsx new file mode 100644 index 0000000..71e1a00 --- /dev/null +++ b/packages/excalidraw/components/LibraryUnit.tsx @@ -0,0 +1,108 @@ +import clsx from "clsx"; +import { memo, useEffect, useRef, useState } from "react"; +import { useDevice } from "./App"; +import type { LibraryItem } from "../types"; +import "./LibraryUnit.scss"; +import { CheckboxItem } from "./CheckboxItem"; +import { PlusIcon } from "./icons"; +import type { SvgCache } from "../hooks/useLibraryItemSvg"; +import { useLibraryItemSvg } from "../hooks/useLibraryItemSvg"; + +export const LibraryUnit = memo( + ({ + id, + elements, + isPending, + onClick, + selected, + onToggle, + onDrag, + svgCache, + }: { + id: LibraryItem["id"] | /** for pending item */ null; + elements?: LibraryItem["elements"]; + isPending?: boolean; + onClick: (id: LibraryItem["id"] | null) => void; + selected: boolean; + onToggle: (id: string, event: React.MouseEvent) => void; + onDrag: (id: string, event: React.DragEvent) => void; + svgCache: SvgCache; + }) => { + const ref = useRef<HTMLDivElement | null>(null); + const svg = useLibraryItemSvg(id, elements, svgCache); + + useEffect(() => { + const node = ref.current; + + if (!node) { + return; + } + + if (svg) { + node.innerHTML = svg.outerHTML; + } + + return () => { + node.innerHTML = ""; + }; + }, [svg]); + + const [isHovered, setIsHovered] = useState(false); + const isMobile = useDevice().editor.isMobile; + const adder = isPending && ( + <div className="library-unit__adder">{PlusIcon}</div> + ); + + return ( + <div + className={clsx("library-unit", { + "library-unit__active": elements, + "library-unit--hover": elements && isHovered, + "library-unit--selected": selected, + "library-unit--skeleton": !svg, + })} + onMouseEnter={() => setIsHovered(true)} + onMouseLeave={() => setIsHovered(false)} + > + <div + className={clsx("library-unit__dragger", { + "library-unit__pulse": !!isPending, + })} + ref={ref} + draggable={!!elements} + onClick={ + !!elements || !!isPending + ? (event) => { + if (id && event.shiftKey) { + onToggle(id, event); + } else { + onClick(id); + } + } + : undefined + } + onDragStart={(event) => { + if (!id) { + event.preventDefault(); + return; + } + setIsHovered(false); + onDrag(id, event); + }} + /> + {adder} + {id && elements && (isHovered || isMobile || selected) && ( + <CheckboxItem + checked={selected} + onChange={(checked, event) => onToggle(id, event)} + className="library-unit__checkbox" + /> + )} + </div> + ); + }, +); + +export const EmptyLibraryUnit = () => ( + <div className="library-unit library-unit--skeleton" /> +); |
