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
This commit is contained in:
parent
e0a449aa40
commit
793b69e592
@ -188,8 +188,13 @@ import { Stats } from "./Stats";
|
|||||||
import { Toast } from "./Toast";
|
import { Toast } from "./Toast";
|
||||||
import { actionToggleViewMode } from "../actions/actionToggleViewMode";
|
import { actionToggleViewMode } from "../actions/actionToggleViewMode";
|
||||||
|
|
||||||
export const IsMobileContext = React.createContext(false);
|
const IsMobileContext = React.createContext(false);
|
||||||
export const useIsMobile = () => useContext(IsMobileContext);
|
export const useIsMobile = () => useContext(IsMobileContext);
|
||||||
|
const ExcalidrawContainerContext = React.createContext<HTMLDivElement | null>(
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
export const useExcalidrawContainer = () =>
|
||||||
|
useContext(ExcalidrawContainerContext);
|
||||||
|
|
||||||
const { history } = createHistory();
|
const { history } = createHistory();
|
||||||
|
|
||||||
@ -305,6 +310,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
private scene: Scene;
|
private scene: Scene;
|
||||||
private resizeObserver: ResizeObserver | undefined;
|
private resizeObserver: ResizeObserver | undefined;
|
||||||
private nearestScrollableContainer: HTMLElement | Document | undefined;
|
private nearestScrollableContainer: HTMLElement | Document | undefined;
|
||||||
|
|
||||||
constructor(props: AppProps) {
|
constructor(props: AppProps) {
|
||||||
super(props);
|
super(props);
|
||||||
const defaultAppState = getDefaultAppState();
|
const defaultAppState = getDefaultAppState();
|
||||||
@ -328,6 +334,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
width: window.innerWidth,
|
width: window.innerWidth,
|
||||||
height: window.innerHeight,
|
height: window.innerHeight,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (excalidrawRef) {
|
if (excalidrawRef) {
|
||||||
const readyPromise =
|
const readyPromise =
|
||||||
("current" in excalidrawRef && excalidrawRef.current?.readyPromise) ||
|
("current" in excalidrawRef && excalidrawRef.current?.readyPromise) ||
|
||||||
@ -456,60 +463,64 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
this.props.handleKeyboardGlobally ? undefined : this.onKeyDown
|
this.props.handleKeyboardGlobally ? undefined : this.onKeyDown
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<IsMobileContext.Provider value={this.isMobile}>
|
<ExcalidrawContainerContext.Provider
|
||||||
<LayerUI
|
value={this.excalidrawContainerRef.current}
|
||||||
canvas={this.canvas}
|
>
|
||||||
appState={this.state}
|
<IsMobileContext.Provider value={this.isMobile}>
|
||||||
setAppState={this.setAppState}
|
<LayerUI
|
||||||
actionManager={this.actionManager}
|
canvas={this.canvas}
|
||||||
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
|
|
||||||
appState={this.state}
|
appState={this.state}
|
||||||
setAppState={this.setAppState}
|
setAppState={this.setAppState}
|
||||||
|
actionManager={this.actionManager}
|
||||||
elements={this.scene.getElements()}
|
elements={this.scene.getElements()}
|
||||||
onClose={this.toggleStats}
|
onCollabButtonClick={onCollabButtonClick}
|
||||||
renderCustomStats={renderCustomStats}
|
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" />
|
||||||
{this.state.toastMessage !== null && (
|
<div className="excalidraw-contextMenuContainer" />
|
||||||
<Toast
|
{this.state.showStats && (
|
||||||
message={this.state.toastMessage}
|
<Stats
|
||||||
clearToast={this.clearToast}
|
appState={this.state}
|
||||||
/>
|
setAppState={this.setAppState}
|
||||||
)}
|
elements={this.scene.getElements()}
|
||||||
<main>{this.renderCanvas()}</main>
|
onClose={this.toggleStats}
|
||||||
</IsMobileContext.Provider>
|
renderCustomStats={renderCustomStats}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{this.state.toastMessage !== null && (
|
||||||
|
<Toast
|
||||||
|
message={this.state.toastMessage}
|
||||||
|
clearToast={this.clearToast}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<main>{this.renderCanvas()}</main>
|
||||||
|
</IsMobileContext.Provider>
|
||||||
|
</ExcalidrawContainerContext.Provider>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -988,9 +999,10 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
document
|
this.excalidrawContainerRef.current?.classList.toggle(
|
||||||
.querySelector(".excalidraw")
|
"theme--dark",
|
||||||
?.classList.toggle("theme--dark", this.state.theme === "dark");
|
this.state.theme === "dark",
|
||||||
|
);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
this.state.editingLinearElement &&
|
this.state.editingLinearElement &&
|
||||||
|
@ -8,6 +8,7 @@ import "./Dialog.scss";
|
|||||||
import { back, close } from "./icons";
|
import { back, close } from "./icons";
|
||||||
import { Island } from "./Island";
|
import { Island } from "./Island";
|
||||||
import { Modal } from "./Modal";
|
import { Modal } from "./Modal";
|
||||||
|
import { AppState } from "../types";
|
||||||
|
|
||||||
export const Dialog = (props: {
|
export const Dialog = (props: {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
@ -16,6 +17,7 @@ export const Dialog = (props: {
|
|||||||
onCloseRequest(): void;
|
onCloseRequest(): void;
|
||||||
title: React.ReactNode;
|
title: React.ReactNode;
|
||||||
autofocus?: boolean;
|
autofocus?: boolean;
|
||||||
|
theme?: AppState["theme"];
|
||||||
}) => {
|
}) => {
|
||||||
const [islandNode, setIslandNode] = useCallbackRefState<HTMLDivElement>();
|
const [islandNode, setIslandNode] = useCallbackRefState<HTMLDivElement>();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -70,6 +72,7 @@ export const Dialog = (props: {
|
|||||||
labelledBy="dialog-title"
|
labelledBy="dialog-title"
|
||||||
maxWidth={props.small ? 550 : 800}
|
maxWidth={props.small ? 550 : 800}
|
||||||
onCloseRequest={props.onCloseRequest}
|
onCloseRequest={props.onCloseRequest}
|
||||||
|
theme={props.theme}
|
||||||
>
|
>
|
||||||
<Island ref={setIslandNode}>
|
<Island ref={setIslandNode}>
|
||||||
<h2 id="dialog-title" className="Dialog__title">
|
<h2 id="dialog-title" className="Dialog__title">
|
||||||
|
@ -4,7 +4,8 @@ import React, { useState, useLayoutEffect, useRef } from "react";
|
|||||||
import { createPortal } from "react-dom";
|
import { createPortal } from "react-dom";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import { KEYS } from "../keys";
|
import { KEYS } from "../keys";
|
||||||
import { useIsMobile } from "../components/App";
|
import { useExcalidrawContainer, useIsMobile } from "./App";
|
||||||
|
import { AppState } from "../types";
|
||||||
|
|
||||||
export const Modal = (props: {
|
export const Modal = (props: {
|
||||||
className?: string;
|
className?: string;
|
||||||
@ -12,8 +13,10 @@ export const Modal = (props: {
|
|||||||
maxWidth?: number;
|
maxWidth?: number;
|
||||||
onCloseRequest(): void;
|
onCloseRequest(): void;
|
||||||
labelledBy: string;
|
labelledBy: string;
|
||||||
|
theme?: AppState["theme"];
|
||||||
}) => {
|
}) => {
|
||||||
const modalRoot = useBodyRoot();
|
const { theme = "light" } = props;
|
||||||
|
const modalRoot = useBodyRoot(theme);
|
||||||
|
|
||||||
if (!modalRoot) {
|
if (!modalRoot) {
|
||||||
return null;
|
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 [div, setDiv] = useState<HTMLDivElement | null>(null);
|
||||||
|
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
const isMobileRef = useRef(isMobile);
|
const isMobileRef = useRef(isMobile);
|
||||||
isMobileRef.current = isMobile;
|
isMobileRef.current = isMobile;
|
||||||
|
|
||||||
|
const excalidrawContainer = useExcalidrawContainer();
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
if (div) {
|
if (div) {
|
||||||
div.classList.toggle("excalidraw--mobile", isMobile);
|
div.classList.toggle("excalidraw--mobile", isMobile);
|
||||||
@ -62,9 +67,9 @@ const useBodyRoot = () => {
|
|||||||
}, [div, isMobile]);
|
}, [div, isMobile]);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
const isDarkTheme = !!document
|
const isDarkTheme =
|
||||||
.querySelector(".excalidraw")
|
!!excalidrawContainer?.classList.contains("theme--dark") ||
|
||||||
?.classList.contains("theme--dark");
|
theme === "dark";
|
||||||
const div = document.createElement("div");
|
const div = document.createElement("div");
|
||||||
|
|
||||||
div.classList.add("excalidraw", "excalidraw-modal-container");
|
div.classList.add("excalidraw", "excalidraw-modal-container");
|
||||||
@ -81,7 +86,7 @@ const useBodyRoot = () => {
|
|||||||
return () => {
|
return () => {
|
||||||
document.body.removeChild(div);
|
document.body.removeChild(div);
|
||||||
};
|
};
|
||||||
}, []);
|
}, [excalidrawContainer, theme]);
|
||||||
|
|
||||||
return div;
|
return div;
|
||||||
};
|
};
|
||||||
|
@ -640,6 +640,7 @@ class CollabWrapper extends PureComponent<Props, CollabState> {
|
|||||||
setErrorMessage={(errorMessage) => {
|
setErrorMessage={(errorMessage) => {
|
||||||
this.setState({ errorMessage });
|
this.setState({ errorMessage });
|
||||||
}}
|
}}
|
||||||
|
theme={this.excalidrawAPI.getAppState().theme}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{errorMessage && (
|
{errorMessage && (
|
||||||
|
@ -13,6 +13,7 @@ import { ToolButton } from "../../components/ToolButton";
|
|||||||
import { t } from "../../i18n";
|
import { t } from "../../i18n";
|
||||||
import "./RoomDialog.scss";
|
import "./RoomDialog.scss";
|
||||||
import Stack from "../../components/Stack";
|
import Stack from "../../components/Stack";
|
||||||
|
import { AppState } from "../../types";
|
||||||
|
|
||||||
const getShareIcon = () => {
|
const getShareIcon = () => {
|
||||||
const navigator = window.navigator as any;
|
const navigator = window.navigator as any;
|
||||||
@ -36,6 +37,7 @@ const RoomDialog = ({
|
|||||||
onRoomCreate,
|
onRoomCreate,
|
||||||
onRoomDestroy,
|
onRoomDestroy,
|
||||||
setErrorMessage,
|
setErrorMessage,
|
||||||
|
theme,
|
||||||
}: {
|
}: {
|
||||||
handleClose: () => void;
|
handleClose: () => void;
|
||||||
activeRoomLink: string;
|
activeRoomLink: string;
|
||||||
@ -44,6 +46,7 @@ const RoomDialog = ({
|
|||||||
onRoomCreate: () => void;
|
onRoomCreate: () => void;
|
||||||
onRoomDestroy: () => void;
|
onRoomDestroy: () => void;
|
||||||
setErrorMessage: (message: string) => void;
|
setErrorMessage: (message: string) => void;
|
||||||
|
theme: AppState["theme"];
|
||||||
}) => {
|
}) => {
|
||||||
const roomLinkInput = useRef<HTMLInputElement>(null);
|
const roomLinkInput = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
@ -168,6 +171,7 @@ const RoomDialog = ({
|
|||||||
small
|
small
|
||||||
onCloseRequest={handleClose}
|
onCloseRequest={handleClose}
|
||||||
title={t("labels.liveCollaboration")}
|
title={t("labels.liveCollaboration")}
|
||||||
|
theme={theme}
|
||||||
>
|
>
|
||||||
{renderRoomDialog()}
|
{renderRoomDialog()}
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
@ -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`.
|
- 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).
|
- 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
|
## Types
|
||||||
|
|
||||||
- Renamed the following types in case you depend on them (via [#3427](https://github.com/excalidraw/excalidraw/pull/3427)):
|
- Renamed the following types in case you depend on them (via [#3427](https://github.com/excalidraw/excalidraw/pull/3427)):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user