{
- onInsertElements(distributeLibraryItemsOnSquareGrid(libraryItems));
- }}
+ pendingElements={pendingElements}
+ onInsertLibraryItems={onInsertLibraryItems}
onAddToLibrary={deselectItems}
setAppState={setAppState}
libraryReturnUrl={appProps.libraryReturnUrl}
- library={library}
+ library={memoizedLibrary}
id={id}
- appState={appState}
+ theme={appState.theme}
selectedItems={selectedItems}
onSelectItems={setSelectedItems}
/>
diff --git a/src/components/LibraryMenuHeaderContent.tsx b/src/components/LibraryMenuHeaderContent.tsx
index 3f16bdb62a..56c8fe6d0c 100644
--- a/src/components/LibraryMenuHeaderContent.tsx
+++ b/src/components/LibraryMenuHeaderContent.tsx
@@ -24,6 +24,7 @@ import DropdownMenu from "./dropdownMenu/DropdownMenu";
import { isLibraryMenuOpenAtom } from "./LibraryMenu";
import { useUIAppState } from "../context/ui-appState";
import clsx from "clsx";
+import { useLibraryCache } from "../hooks/useLibraryItemSvg";
const getSelectedItems = (
libraryItems: LibraryItems,
@@ -55,7 +56,7 @@ export const LibraryDropdownMenuButton: React.FC<{
jotaiScope,
);
- const renderRemoveLibAlert = useCallback(() => {
+ const renderRemoveLibAlert = () => {
const content = selectedItems.length
? t("alerts.removeItemsFromsLibrary", { count: selectedItems.length })
: t("alerts.resetLibrary");
@@ -80,7 +81,7 @@ export const LibraryDropdownMenuButton: React.FC<{
{content}
);
- }, [selectedItems, onRemoveFromLibrary, resetLibrary]);
+ };
const [showRemoveLibAlert, setShowRemoveLibAlert] = useState(false);
@@ -106,7 +107,7 @@ export const LibraryDropdownMenuButton: React.FC<{
onCloseRequest={() => setPublishLibSuccess(null)}
title={t("publishSuccessDialog.title")}
className="publish-library-success"
- small={true}
+ size="small"
>
{
- setShowPublishLibraryDialog(false);
- setPublishLibSuccess({ url: data.url, authorName: data.authorName });
- const nextLibItems = libraryItems.slice();
- nextLibItems.forEach((libItem) => {
- if (selectedItems.includes(libItem.id)) {
- libItem.status = "published";
- }
- });
- library.setLibrary(nextLibItems);
- },
- [setShowPublishLibraryDialog, setPublishLibSuccess, selectedItems, library],
- );
+ const onPublishLibSuccess = (
+ data: { url: string; authorName: string },
+ libraryItems: LibraryItems,
+ ) => {
+ setShowPublishLibraryDialog(false);
+ setPublishLibSuccess({ url: data.url, authorName: data.authorName });
+ const nextLibItems = libraryItems.slice();
+ nextLibItems.forEach((libItem) => {
+ if (selectedItems.includes(libItem.id)) {
+ libItem.status = "published";
+ }
+ });
+ library.setLibrary(nextLibItems);
+ };
const onLibraryImport = async () => {
try {
@@ -280,27 +281,29 @@ export const LibraryDropdownMenu = ({
className?: string;
}) => {
const { library } = useApp();
+ const { clearLibraryCache, deleteItemsFromLibraryCache } = useLibraryCache();
const appState = useUIAppState();
const setAppState = useExcalidrawSetAppState();
const [libraryItemsData] = useAtom(libraryItemsAtom, jotaiScope);
- const removeFromLibrary = useCallback(
- async (libraryItems: LibraryItems) => {
- const nextItems = libraryItems.filter(
- (item) => !selectedItems.includes(item.id),
- );
- library.setLibrary(nextItems).catch(() => {
- setAppState({ errorMessage: t("alerts.errorRemovingFromLibrary") });
- });
- onSelectItems([]);
- },
- [library, setAppState, selectedItems, onSelectItems],
- );
+ const removeFromLibrary = async (libraryItems: LibraryItems) => {
+ const nextItems = libraryItems.filter(
+ (item) => !selectedItems.includes(item.id),
+ );
+ library.setLibrary(nextItems).catch(() => {
+ setAppState({ errorMessage: t("alerts.errorRemovingFromLibrary") });
+ });
- const resetLibrary = useCallback(() => {
+ deleteItemsFromLibraryCache(selectedItems);
+
+ onSelectItems([]);
+ };
+
+ const resetLibrary = () => {
library.resetLibrary();
- }, [library]);
+ clearLibraryCache();
+ };
return (
void;
onAddToLibrary: (elements: LibraryItem["elements"]) => void;
- selectedItems: LibraryItem["id"][];
- onSelectItems: (id: LibraryItem["id"][]) => void;
libraryReturnUrl: ExcalidrawProps["libraryReturnUrl"];
theme: UIAppState["theme"];
id: string;
-}) => {
- const [lastSelectedItem, setLastSelectedItem] = useState<
- LibraryItem["id"] | null
- >(null);
+ selectedItems: LibraryItem["id"][];
+ onSelectItems: (id: LibraryItem["id"][]) => void;
+}) {
+ const libraryContainerRef = useRef(null);
+ const scrollPosition = useScrollPosition(libraryContainerRef);
- const onItemSelectToggle = (
- id: LibraryItem["id"],
- event: React.MouseEvent,
- ) => {
- const shouldSelect = !selectedItems.includes(id);
-
- const orderedItems = [...unpublishedItems, ...publishedItems];
-
- if (shouldSelect) {
- if (event.shiftKey && lastSelectedItem) {
- const rangeStart = orderedItems.findIndex(
- (item) => item.id === lastSelectedItem,
- );
- const rangeEnd = orderedItems.findIndex((item) => item.id === id);
-
- if (rangeStart === -1 || rangeEnd === -1) {
- onSelectItems([...selectedItems, id]);
- return;
- }
-
- const selectedItemsMap = arrayToMap(selectedItems);
- const nextSelectedIds = orderedItems.reduce(
- (acc: LibraryItem["id"][], item, idx) => {
- if (
- (idx >= rangeStart && idx <= rangeEnd) ||
- selectedItemsMap.has(item.id)
- ) {
- acc.push(item.id);
- }
- return acc;
- },
- [],
- );
-
- onSelectItems(nextSelectedIds);
- } else {
- onSelectItems([...selectedItems, id]);
- }
- setLastSelectedItem(id);
- } else {
- setLastSelectedItem(null);
- onSelectItems(selectedItems.filter((_id) => _id !== id));
+ // This effect has to be called only on first render, therefore `scrollPosition` isn't in the dependency array
+ useEffect(() => {
+ if (scrollPosition > 0) {
+ libraryContainerRef.current?.scrollTo(0, scrollPosition);
}
- };
+ }, []); // eslint-disable-line react-hooks/exhaustive-deps
- const getInsertedElements = (id: string) => {
- let targetElements;
- if (selectedItems.includes(id)) {
- targetElements = libraryItems.filter((item) =>
- selectedItems.includes(item.id),
- );
- } else {
- targetElements = libraryItems.filter((item) => item.id === id);
- }
- return targetElements.map((item) => {
- return {
- ...item,
- // duplicate each library item before inserting on canvas to confine
- // ids and bindings to each library item. See #6465
- elements: duplicateElements(item.elements, { randomizeSeed: true }),
- };
- });
- };
-
- const createLibraryItemCompo = (params: {
- item:
- | LibraryItem
- | /* pending library item */ {
- id: null;
- elements: readonly NonDeleted[];
- }
- | null;
- onClick?: () => void;
- key: string;
- }) => {
- return (
-
- {})}
- id={params.item?.id || null}
- selected={!!params.item?.id && selectedItems.includes(params.item.id)}
- onToggle={onItemSelectToggle}
- onDrag={(id, event) => {
- event.dataTransfer.setData(
- MIME_TYPES.excalidrawlib,
- serializeLibraryAsJSON(getInsertedElements(id)),
- );
- }}
- />
-
- );
- };
-
- const renderLibrarySection = (
- items: (
- | LibraryItem
- | /* pending library item */ {
- id: null;
- elements: readonly NonDeleted[];
- }
- )[],
- ) => {
- const _items = items.map((item) => {
- if (item.id) {
- return createLibraryItemCompo({
- item,
- onClick: () => onInsertLibraryItems(getInsertedElements(item.id)),
- key: item.id,
- });
- }
- return createLibraryItemCompo({
- key: "__pending__item__",
- item,
- onClick: () => onAddToLibrary(pendingElements),
- });
- });
-
- // ensure we render all empty cells if no items are present
- let rows = chunk(_items, CELLS_PER_ROW);
- if (!rows.length) {
- rows = [[]];
- }
-
- return rows.map((rowItems, index, rows) => {
- if (index === rows.length - 1) {
- // pad row with empty cells
- rowItems = rowItems.concat(
- new Array(CELLS_PER_ROW - rowItems.length)
- .fill(null)
- .map((_, index) => {
- return createLibraryItemCompo({
- key: `empty_${index}`,
- item: null,
- });
- }),
- );
- }
- return (
-
- {rowItems}
-
- );
- });
- };
-
- const unpublishedItems = libraryItems.filter(
- (item) => item.status !== "published",
+ const { svgCache } = useLibraryCache();
+ const unpublishedItems = useMemo(
+ () => libraryItems.filter((item) => item.status !== "published"),
+ [libraryItems],
);
- const publishedItems = libraryItems.filter(
- (item) => item.status === "published",
+
+ const publishedItems = useMemo(
+ () => libraryItems.filter((item) => item.status === "published"),
+ [libraryItems],
);
const showBtn = !libraryItems.length && !pendingElements.length;
@@ -213,6 +87,122 @@ const LibraryMenuItems = ({
!unpublishedItems.length &&
!publishedItems.length;
+ const [lastSelectedItem, setLastSelectedItem] = useState<
+ LibraryItem["id"] | null
+ >(null);
+
+ const onItemSelectToggle = useCallback(
+ (id: LibraryItem["id"], event: React.MouseEvent) => {
+ const shouldSelect = !selectedItems.includes(id);
+
+ const orderedItems = [...unpublishedItems, ...publishedItems];
+
+ if (shouldSelect) {
+ if (event.shiftKey && lastSelectedItem) {
+ const rangeStart = orderedItems.findIndex(
+ (item) => item.id === lastSelectedItem,
+ );
+ const rangeEnd = orderedItems.findIndex((item) => item.id === id);
+
+ if (rangeStart === -1 || rangeEnd === -1) {
+ onSelectItems([...selectedItems, id]);
+ return;
+ }
+
+ const selectedItemsMap = arrayToMap(selectedItems);
+ const nextSelectedIds = orderedItems.reduce(
+ (acc: LibraryItem["id"][], item, idx) => {
+ if (
+ (idx >= rangeStart && idx <= rangeEnd) ||
+ selectedItemsMap.has(item.id)
+ ) {
+ acc.push(item.id);
+ }
+ return acc;
+ },
+ [],
+ );
+
+ onSelectItems(nextSelectedIds);
+ } else {
+ onSelectItems([...selectedItems, id]);
+ }
+ setLastSelectedItem(id);
+ } else {
+ setLastSelectedItem(null);
+ onSelectItems(selectedItems.filter((_id) => _id !== id));
+ }
+ },
+ [
+ lastSelectedItem,
+ onSelectItems,
+ publishedItems,
+ selectedItems,
+ unpublishedItems,
+ ],
+ );
+
+ const getInsertedElements = useCallback(
+ (id: string) => {
+ let targetElements;
+ if (selectedItems.includes(id)) {
+ targetElements = libraryItems.filter((item) =>
+ selectedItems.includes(item.id),
+ );
+ } else {
+ targetElements = libraryItems.filter((item) => item.id === id);
+ }
+ return targetElements.map((item) => {
+ return {
+ ...item,
+ // duplicate each library item before inserting on canvas to confine
+ // ids and bindings to each library item. See #6465
+ elements: duplicateElements(item.elements, { randomizeSeed: true }),
+ };
+ });
+ },
+ [libraryItems, selectedItems],
+ );
+
+ const onItemDrag = useCallback(
+ (id: LibraryItem["id"], event: React.DragEvent) => {
+ event.dataTransfer.setData(
+ MIME_TYPES.excalidrawlib,
+ serializeLibraryAsJSON(getInsertedElements(id)),
+ );
+ },
+ [getInsertedElements],
+ );
+
+ const isItemSelected = useCallback(
+ (id: LibraryItem["id"] | null) => {
+ if (!id) {
+ return false;
+ }
+
+ return selectedItems.includes(id);
+ },
+ [selectedItems],
+ );
+
+ const onAddToLibraryClick = useCallback(() => {
+ onAddToLibrary(pendingElements);
+ }, [pendingElements, onAddToLibrary]);
+
+ const onItemClick = useCallback(
+ (id: LibraryItem["id"] | null) => {
+ if (id) {
+ onInsertLibraryItems(getInsertedElements(id));
+ }
+ },
+ [getInsertedElements, onInsertLibraryItems],
+ );
+
+ const itemsRenderedPerBatch =
+ svgCache.size >= libraryItems.length
+ ? CACHED_ITEMS_RENDERED_PER_BATCH
+ : ITEMS_RENDERED_PER_BATCH;
+
return (
0 ? 1 : "0 1 auto",
marginBottom: 0,
}}
+ ref={libraryContainerRef}
>
<>
{!isLibraryEmpty && (
@@ -258,28 +249,41 @@ const LibraryMenuItems = ({
)}
-
- {!pendingElements.length && !unpublishedItems.length ? (
-
-
- {t("library.noItems")}
-
-
- {publishedItems.length > 0
- ? t("library.hint_emptyPrivateLibrary")
- : t("library.hint_emptyLibrary")}
-
+ {!pendingElements.length && !unpublishedItems.length ? (
+
+
+ {t("library.noItems")}
- ) : (
- renderLibrarySection([
- // append pending library item
- ...(pendingElements.length
- ? [{ id: null, elements: pendingElements }]
- : []),
- ...unpublishedItems,
- ])
- )}
-
+
+ {publishedItems.length > 0
+ ? t("library.hint_emptyPrivateLibrary")
+ : t("library.hint_emptyLibrary")}
+
+
+ ) : (
+
+ {pendingElements.length > 0 && (
+
+ )}
+
+
+ )}
>
<>
@@ -291,7 +295,17 @@ const LibraryMenuItems = ({
)}
{publishedItems.length > 0 ? (
- renderLibrarySection(publishedItems)
+
+
+
) : unpublishedItems.length > 0 ? (
);
-};
-
-export default LibraryMenuItems;
+}
diff --git a/src/components/LibraryMenuSection.tsx b/src/components/LibraryMenuSection.tsx
new file mode 100644
index 0000000000..0e10470fc9
--- /dev/null
+++ b/src/components/LibraryMenuSection.tsx
@@ -0,0 +1,77 @@
+import React, { memo, ReactNode, useEffect, useState } from "react";
+import { EmptyLibraryUnit, LibraryUnit } from "./LibraryUnit";
+import { LibraryItem } from "../types";
+import { ExcalidrawElement, NonDeleted } from "../element/types";
+import { SvgCache } from "../hooks/useLibraryItemSvg";
+import { useTransition } from "../hooks/useTransition";
+
+type LibraryOrPendingItem = (
+ | LibraryItem
+ | /* pending library item */ {
+ id: null;
+ elements: readonly NonDeleted[];
+ }
+)[];
+
+interface Props {
+ items: LibraryOrPendingItem;
+ onClick: (id: LibraryItem["id"] | null) => void;
+ onItemSelectToggle: (id: LibraryItem["id"], event: React.MouseEvent) => void;
+ onItemDrag: (id: LibraryItem["id"], event: React.DragEvent) => void;
+ isItemSelected: (id: LibraryItem["id"] | null) => boolean;
+ svgCache: SvgCache;
+ itemsRenderedPerBatch: number;
+}
+
+export const LibraryMenuSectionGrid = ({
+ children,
+}: {
+ children: ReactNode;
+}) => {
+ return {children}
;
+};
+
+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 ? (
+
+ ) : (
+
+ );
+ })}
+ >
+ );
+ },
+);
diff --git a/src/components/LibraryUnit.scss b/src/components/LibraryUnit.scss
index 85d7505c38..a5f26bff4e 100644
--- a/src/components/LibraryUnit.scss
+++ b/src/components/LibraryUnit.scss
@@ -20,6 +20,27 @@
border-color: var(--color-primary);
border-width: 1px;
}
+
+ &--skeleton {
+ opacity: 0.5;
+ background: linear-gradient(
+ -45deg,
+ var(--color-gray-10),
+ var(--color-gray-20),
+ var(--color-gray-10)
+ );
+ background-size: 200% 200%;
+ animation: library-unit__skeleton-opacity-animation 0.2s linear;
+ }
+ }
+
+ &.theme--dark .library-unit--skeleton {
+ background-image: linear-gradient(
+ -45deg,
+ var(--color-gray-100),
+ var(--color-gray-80),
+ var(--color-gray-100)
+ );
}
.library-unit__dragger {
@@ -142,4 +163,18 @@
transform: scale(0.85);
}
}
+
+ @keyframes library-unit__skeleton-opacity-animation {
+ 0% {
+ opacity: 0;
+ }
+
+ 75% {
+ opacity: 0;
+ }
+
+ 100% {
+ opacity: 0.5;
+ }
+ }
}
diff --git a/src/components/LibraryUnit.tsx b/src/components/LibraryUnit.tsx
index 7e8181d7b4..ce5d4ebbb0 100644
--- a/src/components/LibraryUnit.tsx
+++ b/src/components/LibraryUnit.tsx
@@ -1,108 +1,107 @@
import clsx from "clsx";
-import { useEffect, useRef, useState } from "react";
+import { memo, useEffect, useRef, useState } from "react";
import { useDevice } from "../components/App";
-import { exportToSvg } from "../packages/utils";
import { LibraryItem } from "../types";
import "./LibraryUnit.scss";
import { CheckboxItem } from "./CheckboxItem";
import { PlusIcon } from "./icons";
-import { COLOR_PALETTE } from "../colors";
+import { SvgCache, useLibraryItemSvg } from "../hooks/useLibraryItemSvg";
-export const LibraryUnit = ({
- id,
- elements,
- isPending,
- onClick,
- selected,
- onToggle,
- onDrag,
-}: {
- id: LibraryItem["id"] | /** for pending item */ null;
- elements?: LibraryItem["elements"];
- isPending?: boolean;
- onClick: () => void;
- selected: boolean;
- onToggle: (id: string, event: React.MouseEvent) => void;
- onDrag: (id: string, event: React.DragEvent) => void;
-}) => {
- const ref = useRef(null);
- useEffect(() => {
- const node = ref.current;
- if (!node) {
- return;
- }
+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);
- (async () => {
- if (!elements) {
+ useEffect(() => {
+ const node = ref.current;
+
+ if (!node) {
return;
}
- const svg = await exportToSvg({
- elements,
- appState: {
- exportBackground: false,
- viewBackgroundColor: COLOR_PALETTE.white,
- },
- files: null,
- });
- svg.querySelector(".style-fonts")?.remove();
- node.innerHTML = svg.outerHTML;
- })();
- return () => {
- node.innerHTML = "";
- };
- }, [elements]);
+ if (svg) {
+ node.innerHTML = svg.outerHTML;
+ }
- const [isHovered, setIsHovered] = useState(false);
- const isMobile = useDevice().isMobile;
- const adder = isPending && (
- {PlusIcon}
- );
+ return () => {
+ node.innerHTML = "";
+ };
+ }, [svg]);
- return (
- setIsHovered(true)}
- onMouseLeave={() => setIsHovered(false)}
- >
+ const [isHovered, setIsHovered] = useState(false);
+ const isMobile = useDevice().isMobile;
+ const adder = isPending && (
+
{PlusIcon}
+ );
+
+ return (
{
- if (id && event.shiftKey) {
- onToggle(id, event);
- } else {
- onClick();
+ 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/components/Modal.scss b/src/components/Modal.scss
index 71a1a4c0f9..8bf50caafb 100644
--- a/src/components/Modal.scss
+++ b/src/components/Modal.scss
@@ -24,13 +24,15 @@
}
.Modal__background {
- position: absolute;
+ position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 1;
background-color: rgba(#121212, 0.2);
+
+ animation: Modal__background__fade-in 0.125s linear forwards;
}
.Modal__content {
@@ -65,14 +67,23 @@
}
}
- @keyframes Modal__content_fade-in {
+ @keyframes Modal__background__fade-in {
from {
opacity: 0;
- transform: translateY(10px);
}
to {
opacity: 1;
- transform: translateY(0);
+ }
+ }
+
+ @keyframes Modal__content_fade-in {
+ from {
+ opacity: 0;
+ transform: scale(0.9);
+ }
+ to {
+ opacity: 1;
+ transform: scale(1);
}
}
diff --git a/src/components/Modal.tsx b/src/components/Modal.tsx
index 3d774b3df0..3dd737e457 100644
--- a/src/components/Modal.tsx
+++ b/src/components/Modal.tsx
@@ -1,12 +1,11 @@
import "./Modal.scss";
-import React, { useState, useLayoutEffect, useRef } from "react";
+import React from "react";
import { createPortal } from "react-dom";
import clsx from "clsx";
import { KEYS } from "../keys";
-import { useExcalidrawContainer, useDevice } from "./App";
import { AppState } from "../types";
-import { THEME } from "../constants";
+import { useCreatePortalContainer } from "../hooks/useCreatePortalContainer";
export const Modal: React.FC<{
className?: string;
@@ -17,8 +16,10 @@ export const Modal: React.FC<{
theme?: AppState["theme"];
closeOnClickOutside?: boolean;
}> = (props) => {
- const { theme = THEME.LIGHT, closeOnClickOutside = true } = props;
- const modalRoot = useBodyRoot(theme);
+ const { closeOnClickOutside = true } = props;
+ const modalRoot = useCreatePortalContainer({
+ className: "excalidraw-modal-container",
+ });
if (!modalRoot) {
return null;
@@ -44,7 +45,7 @@ export const Modal: React.FC<{
+ />
{
- const [div, setDiv] = useState
(null);
-
- const device = useDevice();
- const isMobileRef = useRef(device.isMobile);
- isMobileRef.current = device.isMobile;
-
- const { container: excalidrawContainer } = useExcalidrawContainer();
-
- useLayoutEffect(() => {
- if (div) {
- div.classList.toggle("excalidraw--mobile", device.isMobile);
- }
- }, [div, device.isMobile]);
-
- useLayoutEffect(() => {
- const isDarkTheme =
- !!excalidrawContainer?.classList.contains("theme--dark") ||
- theme === "dark";
- const div = document.createElement("div");
-
- div.classList.add("excalidraw", "excalidraw-modal-container");
- div.classList.toggle("excalidraw--mobile", isMobileRef.current);
-
- if (isDarkTheme) {
- div.classList.add("theme--dark");
- div.classList.add("theme--dark-background-none");
- }
- document.body.appendChild(div);
-
- setDiv(div);
-
- return () => {
- document.body.removeChild(div);
- };
- }, [excalidrawContainer, theme]);
-
- return div;
-};
diff --git a/src/components/PasteChartDialog.tsx b/src/components/PasteChartDialog.tsx
index a44db40259..9878f6fcc2 100644
--- a/src/components/PasteChartDialog.tsx
+++ b/src/components/PasteChartDialog.tsx
@@ -106,7 +106,7 @@ export const PasteChartDialog = ({
return (