Преглед изворни кода

fix: Apply theme to only to active excalidraw component (#3446)

* feat: Apply theme to only current instance of excalidraw

* fix

* fix

* fix

* fix

* fix

* update changelog

* fix
Aakansha Doshi пре 4 година
родитељ
комит
793b69e592

+ 66 - 54
src/components/App.tsx

@@ -188,8 +188,13 @@ import { Stats } from "./Stats";
 import { Toast } from "./Toast";
 import { actionToggleViewMode } from "../actions/actionToggleViewMode";
 
-export const IsMobileContext = React.createContext(false);
+const IsMobileContext = React.createContext(false);
 export const useIsMobile = () => useContext(IsMobileContext);
+const ExcalidrawContainerContext = React.createContext<HTMLDivElement | null>(
+  null,
+);
+export const useExcalidrawContainer = () =>
+  useContext(ExcalidrawContainerContext);
 
 const { history } = createHistory();
 
@@ -305,6 +310,7 @@ class App extends React.Component<AppProps, AppState> {
   private scene: Scene;
   private resizeObserver: ResizeObserver | undefined;
   private nearestScrollableContainer: HTMLElement | Document | undefined;
+
   constructor(props: AppProps) {
     super(props);
     const defaultAppState = getDefaultAppState();
@@ -328,6 +334,7 @@ class App extends React.Component<AppProps, AppState> {
       width: window.innerWidth,
       height: window.innerHeight,
     };
+
     if (excalidrawRef) {
       const readyPromise =
         ("current" in excalidrawRef && excalidrawRef.current?.readyPromise) ||
@@ -456,60 +463,64 @@ class App extends React.Component<AppProps, AppState> {
           this.props.handleKeyboardGlobally ? undefined : this.onKeyDown
         }
       >
-        <IsMobileContext.Provider value={this.isMobile}>
-          <LayerUI
-            canvas={this.canvas}
-            appState={this.state}
-            setAppState={this.setAppState}
-            actionManager={this.actionManager}
-            elements={this.scene.getElements()}
-            onCollabButtonClick={onCollabButtonClick}
-            onLockToggle={this.toggleLock}
-            onInsertElements={(elements) =>
-              this.addElementsFromPasteOrLibrary(
-                elements,
-                DEFAULT_PASTE_X,
-                DEFAULT_PASTE_Y,
-              )
-            }
-            zenModeEnabled={zenModeEnabled}
-            toggleZenMode={this.toggleZenMode}
-            langCode={getLanguage().code}
-            isCollaborating={this.props.isCollaborating || false}
-            onExportToBackend={onExportToBackend}
-            renderCustomFooter={renderFooter}
-            viewModeEnabled={viewModeEnabled}
-            showExitZenModeBtn={
-              typeof this.props?.zenModeEnabled === "undefined" &&
-              zenModeEnabled
-            }
-            showThemeBtn={
-              typeof this.props?.theme === "undefined" &&
-              this.props.UIOptions.canvasActions.theme
-            }
-            libraryReturnUrl={this.props.libraryReturnUrl}
-            UIOptions={this.props.UIOptions}
-            focusContainer={this.focusContainer}
-          />
-          <div className="excalidraw-textEditorContainer" />
-          <div className="excalidraw-contextMenuContainer" />
-          {this.state.showStats && (
-            <Stats
+        <ExcalidrawContainerContext.Provider
+          value={this.excalidrawContainerRef.current}
+        >
+          <IsMobileContext.Provider value={this.isMobile}>
+            <LayerUI
+              canvas={this.canvas}
               appState={this.state}
               setAppState={this.setAppState}
+              actionManager={this.actionManager}
               elements={this.scene.getElements()}
-              onClose={this.toggleStats}
-              renderCustomStats={renderCustomStats}
-            />
-          )}
-          {this.state.toastMessage !== null && (
-            <Toast
-              message={this.state.toastMessage}
-              clearToast={this.clearToast}
+              onCollabButtonClick={onCollabButtonClick}
+              onLockToggle={this.toggleLock}
+              onInsertElements={(elements) =>
+                this.addElementsFromPasteOrLibrary(
+                  elements,
+                  DEFAULT_PASTE_X,
+                  DEFAULT_PASTE_Y,
+                )
+              }
+              zenModeEnabled={zenModeEnabled}
+              toggleZenMode={this.toggleZenMode}
+              langCode={getLanguage().code}
+              isCollaborating={this.props.isCollaborating || false}
+              onExportToBackend={onExportToBackend}
+              renderCustomFooter={renderFooter}
+              viewModeEnabled={viewModeEnabled}
+              showExitZenModeBtn={
+                typeof this.props?.zenModeEnabled === "undefined" &&
+                zenModeEnabled
+              }
+              showThemeBtn={
+                typeof this.props?.theme === "undefined" &&
+                this.props.UIOptions.canvasActions.theme
+              }
+              libraryReturnUrl={this.props.libraryReturnUrl}
+              UIOptions={this.props.UIOptions}
+              focusContainer={this.focusContainer}
             />
-          )}
-          <main>{this.renderCanvas()}</main>
-        </IsMobileContext.Provider>
+            <div className="excalidraw-textEditorContainer" />
+            <div className="excalidraw-contextMenuContainer" />
+            {this.state.showStats && (
+              <Stats
+                appState={this.state}
+                setAppState={this.setAppState}
+                elements={this.scene.getElements()}
+                onClose={this.toggleStats}
+                renderCustomStats={renderCustomStats}
+              />
+            )}
+            {this.state.toastMessage !== null && (
+              <Toast
+                message={this.state.toastMessage}
+                clearToast={this.clearToast}
+              />
+            )}
+            <main>{this.renderCanvas()}</main>
+          </IsMobileContext.Provider>
+        </ExcalidrawContainerContext.Provider>
       </div>
     );
   }
@@ -988,9 +999,10 @@ class App extends React.Component<AppProps, AppState> {
       });
     }
 
-    document
-      .querySelector(".excalidraw")
-      ?.classList.toggle("theme--dark", this.state.theme === "dark");
+    this.excalidrawContainerRef.current?.classList.toggle(
+      "theme--dark",
+      this.state.theme === "dark",
+    );
 
     if (
       this.state.editingLinearElement &&

+ 3 - 0
src/components/Dialog.tsx

@@ -8,6 +8,7 @@ import "./Dialog.scss";
 import { back, close } from "./icons";
 import { Island } from "./Island";
 import { Modal } from "./Modal";
+import { AppState } from "../types";
 
 export const Dialog = (props: {
   children: React.ReactNode;
@@ -16,6 +17,7 @@ export const Dialog = (props: {
   onCloseRequest(): void;
   title: React.ReactNode;
   autofocus?: boolean;
+  theme?: AppState["theme"];
 }) => {
   const [islandNode, setIslandNode] = useCallbackRefState<HTMLDivElement>();
   useEffect(() => {
@@ -70,6 +72,7 @@ export const Dialog = (props: {
       labelledBy="dialog-title"
       maxWidth={props.small ? 550 : 800}
       onCloseRequest={props.onCloseRequest}
+      theme={props.theme}
     >
       <Island ref={setIslandNode}>
         <h2 id="dialog-title" className="Dialog__title">

+ 12 - 7
src/components/Modal.tsx

@@ -4,7 +4,8 @@ import React, { useState, useLayoutEffect, useRef } from "react";
 import { createPortal } from "react-dom";
 import clsx from "clsx";
 import { KEYS } from "../keys";
-import { useIsMobile } from "../components/App";
+import { useExcalidrawContainer, useIsMobile } from "./App";
+import { AppState } from "../types";
 
 export const Modal = (props: {
   className?: string;
@@ -12,8 +13,10 @@ export const Modal = (props: {
   maxWidth?: number;
   onCloseRequest(): void;
   labelledBy: string;
+  theme?: AppState["theme"];
 }) => {
-  const modalRoot = useBodyRoot();
+  const { theme = "light" } = props;
+  const modalRoot = useBodyRoot(theme);
 
   if (!modalRoot) {
     return null;
@@ -48,13 +51,15 @@ export const Modal = (props: {
   );
 };
 
-const useBodyRoot = () => {
+const useBodyRoot = (theme: AppState["theme"]) => {
   const [div, setDiv] = useState<HTMLDivElement | null>(null);
 
   const isMobile = useIsMobile();
   const isMobileRef = useRef(isMobile);
   isMobileRef.current = isMobile;
 
+  const excalidrawContainer = useExcalidrawContainer();
+
   useLayoutEffect(() => {
     if (div) {
       div.classList.toggle("excalidraw--mobile", isMobile);
@@ -62,9 +67,9 @@ const useBodyRoot = () => {
   }, [div, isMobile]);
 
   useLayoutEffect(() => {
-    const isDarkTheme = !!document
-      .querySelector(".excalidraw")
-      ?.classList.contains("theme--dark");
+    const isDarkTheme =
+      !!excalidrawContainer?.classList.contains("theme--dark") ||
+      theme === "dark";
     const div = document.createElement("div");
 
     div.classList.add("excalidraw", "excalidraw-modal-container");
@@ -81,7 +86,7 @@ const useBodyRoot = () => {
     return () => {
       document.body.removeChild(div);
     };
-  }, []);
+  }, [excalidrawContainer, theme]);
 
   return div;
 };

+ 1 - 0
src/excalidraw-app/collab/CollabWrapper.tsx

@@ -640,6 +640,7 @@ class CollabWrapper extends PureComponent<Props, CollabState> {
             setErrorMessage={(errorMessage) => {
               this.setState({ errorMessage });
             }}
+            theme={this.excalidrawAPI.getAppState().theme}
           />
         )}
         {errorMessage && (

+ 4 - 0
src/excalidraw-app/collab/RoomDialog.tsx

@@ -13,6 +13,7 @@ import { ToolButton } from "../../components/ToolButton";
 import { t } from "../../i18n";
 import "./RoomDialog.scss";
 import Stack from "../../components/Stack";
+import { AppState } from "../../types";
 
 const getShareIcon = () => {
   const navigator = window.navigator as any;
@@ -36,6 +37,7 @@ const RoomDialog = ({
   onRoomCreate,
   onRoomDestroy,
   setErrorMessage,
+  theme,
 }: {
   handleClose: () => void;
   activeRoomLink: string;
@@ -44,6 +46,7 @@ const RoomDialog = ({
   onRoomCreate: () => void;
   onRoomDestroy: () => void;
   setErrorMessage: (message: string) => void;
+  theme: AppState["theme"];
 }) => {
   const roomLinkInput = useRef<HTMLInputElement>(null);
 
@@ -168,6 +171,7 @@ const RoomDialog = ({
       small
       onCloseRequest={handleClose}
       title={t("labels.liveCollaboration")}
+      theme={theme}
     >
       {renderRoomDialog()}
     </Dialog>

+ 4 - 0
src/packages/excalidraw/CHANGELOG.md

@@ -26,6 +26,10 @@ Please add the latest change on the top under the correct section.
 - Recompute offsets on `scroll` of the nearest scrollable container [#3408](https://github.com/excalidraw/excalidraw/pull/3408). This can be disabled by setting [`detectScroll`](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#detectScroll) to `false`.
 - Add `onPaste` prop to handle custom clipboard behaviours [#3420](https://github.com/excalidraw/excalidraw/pull/3420).
 
+### Fixes
+
+- When switching theme, apply it only to the active Excalidraw component. This fixes a case where the theme was getting applied to the first Excalidraw component if you had multiple Excalidraw components on the same page [#3446](https://github.com/excalidraw/excalidraw/pull/3446)
+
 ## Types
 
 - Renamed the following types in case you depend on them (via [#3427](https://github.com/excalidraw/excalidraw/pull/3427)):