diff --git a/src/components/App.tsx b/src/components/App.tsx index 4961fa19..d894f65a 100644 --- a/src/components/App.tsx +++ b/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( + null, +); +export const useExcalidrawContainer = () => + useContext(ExcalidrawContainerContext); const { history } = createHistory(); @@ -305,6 +310,7 @@ class App extends React.Component { 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 { 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 { this.props.handleKeyboardGlobally ? undefined : this.onKeyDown } > - - - 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} - /> -
-
- {this.state.showStats && ( - + + + 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} /> - )} - {this.state.toastMessage !== null && ( - - )} -
{this.renderCanvas()}
-
+
+
+ {this.state.showStats && ( + + )} + {this.state.toastMessage !== null && ( + + )} +
{this.renderCanvas()}
+ +
); } @@ -988,9 +999,10 @@ class App extends React.Component { }); } - 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 && diff --git a/src/components/Dialog.tsx b/src/components/Dialog.tsx index c1d71fef..f2c917db 100644 --- a/src/components/Dialog.tsx +++ b/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(); useEffect(() => { @@ -70,6 +72,7 @@ export const Dialog = (props: { labelledBy="dialog-title" maxWidth={props.small ? 550 : 800} onCloseRequest={props.onCloseRequest} + theme={props.theme} >

diff --git a/src/components/Modal.tsx b/src/components/Modal.tsx index 2e354dc7..50b983c0 100644 --- a/src/components/Modal.tsx +++ b/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(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; }; diff --git a/src/excalidraw-app/collab/CollabWrapper.tsx b/src/excalidraw-app/collab/CollabWrapper.tsx index de87184c..a029b043 100644 --- a/src/excalidraw-app/collab/CollabWrapper.tsx +++ b/src/excalidraw-app/collab/CollabWrapper.tsx @@ -640,6 +640,7 @@ class CollabWrapper extends PureComponent { setErrorMessage={(errorMessage) => { this.setState({ errorMessage }); }} + theme={this.excalidrawAPI.getAppState().theme} /> )} {errorMessage && ( diff --git a/src/excalidraw-app/collab/RoomDialog.tsx b/src/excalidraw-app/collab/RoomDialog.tsx index 4203fb47..0cd2f733 100644 --- a/src/excalidraw-app/collab/RoomDialog.tsx +++ b/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(null); @@ -168,6 +171,7 @@ const RoomDialog = ({ small onCloseRequest={handleClose} title={t("labels.liveCollaboration")} + theme={theme} > {renderRoomDialog()} diff --git a/src/packages/excalidraw/CHANGELOG.md b/src/packages/excalidraw/CHANGELOG.md index 5fbc2b3e..5fbc2003 100644 --- a/src/packages/excalidraw/CHANGELOG.md +++ b/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)):