)}
diff --git a/src/components/LibraryMenuSection.tsx b/src/components/LibraryMenuSection.tsx
index 496f045b..0e10470f 100644
--- a/src/components/LibraryMenuSection.tsx
+++ b/src/components/LibraryMenuSection.tsx
@@ -1,16 +1,10 @@
-import React, { useEffect, useMemo, useState } from "react";
-import { LibraryUnit } from "./LibraryUnit";
+import React, { memo, ReactNode, useEffect, useState } from "react";
+import { EmptyLibraryUnit, LibraryUnit } from "./LibraryUnit";
import { LibraryItem } from "../types";
-import Stack from "./Stack";
-import clsx from "clsx";
import { ExcalidrawElement, NonDeleted } from "../element/types";
import { SvgCache } from "../hooks/useLibraryItemSvg";
import { useTransition } from "../hooks/useTransition";
-const ITEMS_PER_ROW = 4;
-const ROWS_RENDERED_PER_BATCH = 6;
-const CACHED_ROWS_RENDERED_PER_BATCH = 16;
-
type LibraryOrPendingItem = (
| LibraryItem
| /* pending library item */ {
@@ -26,91 +20,58 @@ interface Props {
onItemDrag: (id: LibraryItem["id"], event: React.DragEvent) => void;
isItemSelected: (id: LibraryItem["id"] | null) => boolean;
svgCache: SvgCache;
+ itemsRenderedPerBatch: number;
}
-function LibraryRow({
- items,
- onItemSelectToggle,
- onItemDrag,
- isItemSelected,
- onClick,
- svgCache,
-}: Props) {
- return (
-
- {items.map((item) => (
-
-
-
- ))}
-
- );
-}
+export const LibraryMenuSectionGrid = ({
+ children,
+}: {
+ children: ReactNode;
+}) => {
+ return
{children}
;
+};
-const EmptyLibraryRow = () => (
-
- {Array.from({ length: ITEMS_PER_ROW }).map((_, index) => (
-
-
-
- ))}
-
+export const LibraryMenuSection = memo(
+ ({
+ items,
+ onItemSelectToggle,
+ onItemDrag,
+ isItemSelected,
+ onClick,
+ svgCache,
+ itemsRenderedPerBatch,
+ }: Props) => {
+ const [, startTransition] = useTransition();
+ const [index, setIndex] = useState(0);
+
+ useEffect(() => {
+ if (index < items.length) {
+ startTransition(() => {
+ setIndex(index + itemsRenderedPerBatch);
+ });
+ }
+ }, [index, items.length, startTransition, itemsRenderedPerBatch]);
+
+ return (
+ <>
+ {items.map((item, i) => {
+ return i < index ? (
+
+ ) : (
+
+ );
+ })}
+ >
+ );
+ },
);
-
-function LibraryMenuSection({
- items,
- onItemSelectToggle,
- onItemDrag,
- isItemSelected,
- onClick,
- svgCache,
-}: Props) {
- const rows = Math.ceil(items.length / ITEMS_PER_ROW);
- const [, startTransition] = useTransition();
- const [index, setIndex] = useState(0);
-
- const rowsRenderedPerBatch = useMemo(() => {
- return svgCache.size === 0
- ? ROWS_RENDERED_PER_BATCH
- : CACHED_ROWS_RENDERED_PER_BATCH;
- }, [svgCache]);
-
- useEffect(() => {
- if (index < rows) {
- startTransition(() => {
- setIndex(index + rowsRenderedPerBatch);
- });
- }
- }, [index, rows, startTransition, rowsRenderedPerBatch]);
-
- return (
- <>
- {Array.from({ length: rows }).map((_, i) =>
- i < index ? (
-
- ) : (
-
- ),
- )}
- >
- );
-}
-
-export default LibraryMenuSection;
diff --git a/src/components/LibraryUnit.scss b/src/components/LibraryUnit.scss
index 1bfc1e6d..a5f26bff 100644
--- a/src/components/LibraryUnit.scss
+++ b/src/components/LibraryUnit.scss
@@ -30,7 +30,7 @@
var(--color-gray-10)
);
background-size: 200% 200%;
- animation: library-unit__skeleton-opacity-animation 0.3s linear;
+ animation: library-unit__skeleton-opacity-animation 0.2s linear;
}
}
diff --git a/src/components/LibraryUnit.tsx b/src/components/LibraryUnit.tsx
index f256b49f..ce5d4ebb 100644
--- a/src/components/LibraryUnit.tsx
+++ b/src/components/LibraryUnit.tsx
@@ -1,5 +1,5 @@
import clsx from "clsx";
-import { useEffect, useRef, useState } from "react";
+import { memo, useEffect, useRef, useState } from "react";
import { useDevice } from "../components/App";
import { LibraryItem } from "../types";
import "./LibraryUnit.scss";
@@ -7,96 +7,101 @@ import { CheckboxItem } from "./CheckboxItem";
import { PlusIcon } from "./icons";
import { SvgCache, useLibraryItemSvg } from "../hooks/useLibraryItemSvg";
-export const LibraryUnit = ({
- 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
(null);
- const svg = useLibraryItemSvg(id, elements, svgCache);
+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(null);
+ const svg = useLibraryItemSvg(id, elements, svgCache);
- useEffect(() => {
- const node = ref.current;
+ useEffect(() => {
+ const node = ref.current;
- if (!node) {
- return;
- }
+ if (!node) {
+ return;
+ }
- if (svg) {
- svg.querySelector(".style-fonts")?.remove();
- node.innerHTML = svg.outerHTML;
- }
+ if (svg) {
+ node.innerHTML = svg.outerHTML;
+ }
- return () => {
- node.innerHTML = "";
- };
- }, [elements, svg]);
+ return () => {
+ node.innerHTML = "";
+ };
+ }, [svg]);
- const [isHovered, setIsHovered] = useState(false);
- const isMobile = useDevice().isMobile;
- const adder = isPending && (
- {PlusIcon}
- );
+ const [isHovered, setIsHovered] = useState(false);
+ const isMobile = useDevice().isMobile;
+ const adder = isPending && (
+ {PlusIcon}
+ );
- return (
- setIsHovered(true)}
- onMouseLeave={() => setIsHovered(false)}
- >
+ return (
{
- if (id && event.shiftKey) {
- onToggle(id, event);
- } else {
- onClick(id);
+ onMouseEnter={() => setIsHovered(true)}
+ onMouseLeave={() => setIsHovered(false)}
+ >
+
{
+ if (id && event.shiftKey) {
+ onToggle(id, event);
+ } else {
+ onClick(id);
+ }
}
- }
- : undefined
- }
- onDragStart={(event) => {
- if (!id) {
- event.preventDefault();
- return;
+ : undefined
}
- setIsHovered(false);
- onDrag(id, event);
- }}
- />
- {adder}
- {id && elements && (isHovered || isMobile || selected) && (
- onToggle(id, event)}
- className="library-unit__checkbox"
+ onDragStart={(event) => {
+ if (!id) {
+ event.preventDefault();
+ return;
+ }
+ setIsHovered(false);
+ onDrag(id, event);
+ }}
/>
- )}
-
- );
-};
+ {adder}
+ {id && elements && (isHovered || isMobile || selected) && (
+
onToggle(id, event)}
+ className="library-unit__checkbox"
+ />
+ )}
+
+ );
+ },
+);
+
+export const EmptyLibraryUnit = () => (
+
+);
diff --git a/src/hooks/useLibraryItemSvg.ts b/src/hooks/useLibraryItemSvg.ts
index ba980219..1c27f0ce 100644
--- a/src/hooks/useLibraryItemSvg.ts
+++ b/src/hooks/useLibraryItemSvg.ts
@@ -39,6 +39,7 @@ export const useLibraryItemSvg = (
// When there is no svg in cache export it and save to cache
(async () => {
const exportedSvg = await exportLibraryItemToSvg(elements);
+ exportedSvg.querySelector(".style-fonts")?.remove();
if (exportedSvg) {
svgCache.set(id, exportedSvg);