fix: default light theme splash 🔧 (#5660)
Co-authored-by: dwelle <luzar.david@gmail.com> Co-authored-by: Aakansha Doshi <aakansha1216@gmail.com>
This commit is contained in:
parent
ec4b3d913e
commit
7eaf47c9d4
@ -137,7 +137,6 @@ export class ActionManager {
|
|||||||
*/
|
*/
|
||||||
renderAction = (name: ActionName, data?: PanelComponentProps["data"]) => {
|
renderAction = (name: ActionName, data?: PanelComponentProps["data"]) => {
|
||||||
const canvasActions = this.app.props.UIOptions.canvasActions;
|
const canvasActions = this.app.props.UIOptions.canvasActions;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
this.actions[name] &&
|
this.actions[name] &&
|
||||||
"PanelComponent" in this.actions[name] &&
|
"PanelComponent" in this.actions[name] &&
|
||||||
|
@ -552,10 +552,6 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
typeof this.props?.zenModeEnabled === "undefined" &&
|
typeof this.props?.zenModeEnabled === "undefined" &&
|
||||||
this.state.zenModeEnabled
|
this.state.zenModeEnabled
|
||||||
}
|
}
|
||||||
showThemeBtn={
|
|
||||||
typeof this.props?.theme === "undefined" &&
|
|
||||||
this.props.UIOptions.canvasActions.theme
|
|
||||||
}
|
|
||||||
libraryReturnUrl={this.props.libraryReturnUrl}
|
libraryReturnUrl={this.props.libraryReturnUrl}
|
||||||
UIOptions={this.props.UIOptions}
|
UIOptions={this.props.UIOptions}
|
||||||
focusContainer={this.focusContainer}
|
focusContainer={this.focusContainer}
|
||||||
@ -645,7 +641,8 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
let viewModeEnabled = actionResult?.appState?.viewModeEnabled || false;
|
let viewModeEnabled = actionResult?.appState?.viewModeEnabled || false;
|
||||||
let zenModeEnabled = actionResult?.appState?.zenModeEnabled || false;
|
let zenModeEnabled = actionResult?.appState?.zenModeEnabled || false;
|
||||||
let gridSize = actionResult?.appState?.gridSize || null;
|
let gridSize = actionResult?.appState?.gridSize || null;
|
||||||
let theme = actionResult?.appState?.theme || THEME.LIGHT;
|
const theme =
|
||||||
|
actionResult?.appState?.theme || this.props.theme || THEME.LIGHT;
|
||||||
let name = actionResult?.appState?.name ?? this.state.name;
|
let name = actionResult?.appState?.name ?? this.state.name;
|
||||||
if (typeof this.props.viewModeEnabled !== "undefined") {
|
if (typeof this.props.viewModeEnabled !== "undefined") {
|
||||||
viewModeEnabled = this.props.viewModeEnabled;
|
viewModeEnabled = this.props.viewModeEnabled;
|
||||||
@ -659,10 +656,6 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
gridSize = this.props.gridModeEnabled ? GRID_SIZE : null;
|
gridSize = this.props.gridModeEnabled ? GRID_SIZE : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof this.props.theme !== "undefined") {
|
|
||||||
theme = this.props.theme;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof this.props.name !== "undefined") {
|
if (typeof this.props.name !== "undefined") {
|
||||||
name = this.props.name;
|
name = this.props.name;
|
||||||
}
|
}
|
||||||
@ -755,6 +748,9 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.props.theme) {
|
||||||
|
this.setState({ theme: this.props.theme });
|
||||||
|
}
|
||||||
if (!this.state.isLoading) {
|
if (!this.state.isLoading) {
|
||||||
this.setState({ isLoading: true });
|
this.setState({ isLoading: true });
|
||||||
}
|
}
|
||||||
@ -784,6 +780,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
const scene = restore(initialData, null, null);
|
const scene = restore(initialData, null, null);
|
||||||
scene.appState = {
|
scene.appState = {
|
||||||
...scene.appState,
|
...scene.appState,
|
||||||
|
theme: this.props.theme || scene.appState.theme,
|
||||||
// we're falling back to current (pre-init) state when deciding
|
// we're falling back to current (pre-init) state when deciding
|
||||||
// whether to open the library, to handle a case where we
|
// whether to open the library, to handle a case where we
|
||||||
// update the state outside of initialData (e.g. when loading the app
|
// update the state outside of initialData (e.g. when loading the app
|
||||||
|
@ -1,20 +1,12 @@
|
|||||||
import React from "react";
|
|
||||||
import { ActionManager } from "../actions/manager";
|
import { ActionManager } from "../actions/manager";
|
||||||
import { AppState } from "../types";
|
|
||||||
|
|
||||||
export const BackgroundPickerAndDarkModeToggle = ({
|
export const BackgroundPickerAndDarkModeToggle = ({
|
||||||
appState,
|
|
||||||
setAppState,
|
|
||||||
actionManager,
|
actionManager,
|
||||||
showThemeBtn,
|
|
||||||
}: {
|
}: {
|
||||||
actionManager: ActionManager;
|
actionManager: ActionManager;
|
||||||
appState: AppState;
|
|
||||||
setAppState: React.Component<any, AppState>["setState"];
|
|
||||||
showThemeBtn: boolean;
|
|
||||||
}) => (
|
}) => (
|
||||||
<div style={{ display: "flex" }}>
|
<div style={{ display: "flex" }}>
|
||||||
{actionManager.renderAction("changeViewBackgroundColor")}
|
{actionManager.renderAction("changeViewBackgroundColor")}
|
||||||
{showThemeBtn && actionManager.renderAction("toggleTheme")}
|
{actionManager.renderAction("toggleTheme")}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -2,10 +2,12 @@ import React, { useEffect, useState } from "react";
|
|||||||
|
|
||||||
import { LoadingMessage } from "./LoadingMessage";
|
import { LoadingMessage } from "./LoadingMessage";
|
||||||
import { defaultLang, Language, languages, setLanguage } from "../i18n";
|
import { defaultLang, Language, languages, setLanguage } from "../i18n";
|
||||||
|
import { Theme } from "../element/types";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
langCode: Language["code"];
|
langCode: Language["code"];
|
||||||
children: React.ReactElement;
|
children: React.ReactElement;
|
||||||
|
theme?: Theme;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const InitializeApp = (props: Props) => {
|
export const InitializeApp = (props: Props) => {
|
||||||
@ -21,5 +23,5 @@ export const InitializeApp = (props: Props) => {
|
|||||||
updateLang();
|
updateLang();
|
||||||
}, [props.langCode]);
|
}, [props.langCode]);
|
||||||
|
|
||||||
return loading ? <LoadingMessage /> : props.children;
|
return loading ? <LoadingMessage theme={props.theme} /> : props.children;
|
||||||
};
|
};
|
||||||
|
@ -53,7 +53,6 @@ interface LayerUIProps {
|
|||||||
onPenModeToggle: () => void;
|
onPenModeToggle: () => void;
|
||||||
onInsertElements: (elements: readonly NonDeletedExcalidrawElement[]) => void;
|
onInsertElements: (elements: readonly NonDeletedExcalidrawElement[]) => void;
|
||||||
showExitZenModeBtn: boolean;
|
showExitZenModeBtn: boolean;
|
||||||
showThemeBtn: boolean;
|
|
||||||
langCode: Language["code"];
|
langCode: Language["code"];
|
||||||
isCollaborating: boolean;
|
isCollaborating: boolean;
|
||||||
renderTopRightUI?: ExcalidrawProps["renderTopRightUI"];
|
renderTopRightUI?: ExcalidrawProps["renderTopRightUI"];
|
||||||
@ -78,7 +77,6 @@ const LayerUI = ({
|
|||||||
onPenModeToggle,
|
onPenModeToggle,
|
||||||
onInsertElements,
|
onInsertElements,
|
||||||
showExitZenModeBtn,
|
showExitZenModeBtn,
|
||||||
showThemeBtn,
|
|
||||||
isCollaborating,
|
isCollaborating,
|
||||||
renderTopRightUI,
|
renderTopRightUI,
|
||||||
renderCustomFooter,
|
renderCustomFooter,
|
||||||
@ -209,12 +207,7 @@ const LayerUI = ({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Stack.Row>
|
</Stack.Row>
|
||||||
<BackgroundPickerAndDarkModeToggle
|
<BackgroundPickerAndDarkModeToggle actionManager={actionManager} />
|
||||||
appState={appState}
|
|
||||||
actionManager={actionManager}
|
|
||||||
setAppState={setAppState}
|
|
||||||
showThemeBtn={showThemeBtn}
|
|
||||||
/>
|
|
||||||
{appState.fileHandle && (
|
{appState.fileHandle && (
|
||||||
<>{actionManager.renderAction("saveToActiveFile")}</>
|
<>{actionManager.renderAction("saveToActiveFile")}</>
|
||||||
)}
|
)}
|
||||||
@ -424,7 +417,6 @@ const LayerUI = ({
|
|||||||
canvas={canvas}
|
canvas={canvas}
|
||||||
isCollaborating={isCollaborating}
|
isCollaborating={isCollaborating}
|
||||||
renderCustomFooter={renderCustomFooter}
|
renderCustomFooter={renderCustomFooter}
|
||||||
showThemeBtn={showThemeBtn}
|
|
||||||
onImageAction={onImageAction}
|
onImageAction={onImageAction}
|
||||||
renderTopRightUI={renderTopRightUI}
|
renderTopRightUI={renderTopRightUI}
|
||||||
renderCustomStats={renderCustomStats}
|
renderCustomStats={renderCustomStats}
|
||||||
|
@ -1,8 +1,14 @@
|
|||||||
import { t } from "../i18n";
|
import { t } from "../i18n";
|
||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import Spinner from "./Spinner";
|
import Spinner from "./Spinner";
|
||||||
|
import clsx from "clsx";
|
||||||
|
import { THEME } from "../constants";
|
||||||
|
import { Theme } from "../element/types";
|
||||||
|
|
||||||
export const LoadingMessage: React.FC<{ delay?: number }> = ({ delay }) => {
|
export const LoadingMessage: React.FC<{ delay?: number; theme?: Theme }> = ({
|
||||||
|
delay,
|
||||||
|
theme,
|
||||||
|
}) => {
|
||||||
const [isWaiting, setIsWaiting] = useState(!!delay);
|
const [isWaiting, setIsWaiting] = useState(!!delay);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -20,7 +26,11 @@ export const LoadingMessage: React.FC<{ delay?: number }> = ({ delay }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="LoadingMessage">
|
<div
|
||||||
|
className={clsx("LoadingMessage", {
|
||||||
|
"LoadingMessage--dark": theme === THEME.DARK,
|
||||||
|
})}
|
||||||
|
>
|
||||||
<div>
|
<div>
|
||||||
<Spinner />
|
<Spinner />
|
||||||
</div>
|
</div>
|
||||||
|
@ -38,7 +38,6 @@ type MobileMenuProps = {
|
|||||||
isMobile: boolean,
|
isMobile: boolean,
|
||||||
appState: AppState,
|
appState: AppState,
|
||||||
) => JSX.Element | null;
|
) => JSX.Element | null;
|
||||||
showThemeBtn: boolean;
|
|
||||||
onImageAction: (data: { insertOnCanvasDirectly: boolean }) => void;
|
onImageAction: (data: { insertOnCanvasDirectly: boolean }) => void;
|
||||||
renderTopRightUI?: (
|
renderTopRightUI?: (
|
||||||
isMobile: boolean,
|
isMobile: boolean,
|
||||||
@ -61,7 +60,6 @@ export const MobileMenu = ({
|
|||||||
canvas,
|
canvas,
|
||||||
isCollaborating,
|
isCollaborating,
|
||||||
renderCustomFooter,
|
renderCustomFooter,
|
||||||
showThemeBtn,
|
|
||||||
onImageAction,
|
onImageAction,
|
||||||
renderTopRightUI,
|
renderTopRightUI,
|
||||||
renderCustomStats,
|
renderCustomStats,
|
||||||
@ -171,14 +169,7 @@ export const MobileMenu = ({
|
|||||||
onClick={onCollabButtonClick}
|
onClick={onCollabButtonClick}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{
|
{<BackgroundPickerAndDarkModeToggle actionManager={actionManager} />}
|
||||||
<BackgroundPickerAndDarkModeToggle
|
|
||||||
actionManager={actionManager}
|
|
||||||
appState={appState}
|
|
||||||
setAppState={setAppState}
|
|
||||||
showThemeBtn={showThemeBtn}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -149,7 +149,7 @@ export const DEFAULT_UI_OPTIONS: AppProps["UIOptions"] = {
|
|||||||
export: { saveFileToDisk: true },
|
export: { saveFileToDisk: true },
|
||||||
loadScene: true,
|
loadScene: true,
|
||||||
saveToActiveFile: true,
|
saveToActiveFile: true,
|
||||||
theme: true,
|
toggleTheme: null,
|
||||||
saveAsImage: true,
|
saveAsImage: true,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
@import "open-color/open-color.scss";
|
||||||
|
|
||||||
.visually-hidden {
|
.visually-hidden {
|
||||||
position: absolute !important;
|
position: absolute !important;
|
||||||
height: 1px;
|
height: 1px;
|
||||||
@ -30,3 +32,8 @@
|
|||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.LoadingMessage--dark {
|
||||||
|
background-color: $oc-black;
|
||||||
|
color: $oc-white;
|
||||||
|
}
|
||||||
|
@ -34,6 +34,7 @@ export const STORAGE_KEYS = {
|
|||||||
LOCAL_STORAGE_APP_STATE: "excalidraw-state",
|
LOCAL_STORAGE_APP_STATE: "excalidraw-state",
|
||||||
LOCAL_STORAGE_COLLAB: "excalidraw-collab",
|
LOCAL_STORAGE_COLLAB: "excalidraw-collab",
|
||||||
LOCAL_STORAGE_LIBRARY: "excalidraw-library",
|
LOCAL_STORAGE_LIBRARY: "excalidraw-library",
|
||||||
|
LOCAL_STORAGE_THEME: "excalidraw-theme",
|
||||||
VERSION_DATA_STATE: "version-dataState",
|
VERSION_DATA_STATE: "version-dataState",
|
||||||
VERSION_FILES: "version-files",
|
VERSION_FILES: "version-files",
|
||||||
} as const;
|
} as const;
|
||||||
|
@ -9,6 +9,7 @@ import {
|
|||||||
APP_NAME,
|
APP_NAME,
|
||||||
COOKIES,
|
COOKIES,
|
||||||
EVENT,
|
EVENT,
|
||||||
|
THEME,
|
||||||
TITLE_TIMEOUT,
|
TITLE_TIMEOUT,
|
||||||
VERSION_TIMEOUT,
|
VERSION_TIMEOUT,
|
||||||
} from "../constants";
|
} from "../constants";
|
||||||
@ -17,6 +18,7 @@ import {
|
|||||||
ExcalidrawElement,
|
ExcalidrawElement,
|
||||||
FileId,
|
FileId,
|
||||||
NonDeletedExcalidrawElement,
|
NonDeletedExcalidrawElement,
|
||||||
|
Theme,
|
||||||
} from "../element/types";
|
} from "../element/types";
|
||||||
import { useCallbackRefState } from "../hooks/useCallbackRefState";
|
import { useCallbackRefState } from "../hooks/useCallbackRefState";
|
||||||
import { t } from "../i18n";
|
import { t } from "../i18n";
|
||||||
@ -512,6 +514,18 @@ const ExcalidrawWrapper = () => {
|
|||||||
languageDetector.cacheUserLanguage(langCode);
|
languageDetector.cacheUserLanguage(langCode);
|
||||||
}, [langCode]);
|
}, [langCode]);
|
||||||
|
|
||||||
|
const [theme, setTheme] = useState<Theme>(
|
||||||
|
() =>
|
||||||
|
localStorage.getItem(STORAGE_KEYS.LOCAL_STORAGE_THEME) ||
|
||||||
|
// FIXME migration from old LS scheme. Can be removed later. #5660
|
||||||
|
importFromLocalStorage().appState?.theme ||
|
||||||
|
THEME.LIGHT,
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
localStorage.setItem(STORAGE_KEYS.LOCAL_STORAGE_THEME, theme);
|
||||||
|
}, [theme]);
|
||||||
|
|
||||||
const onChange = (
|
const onChange = (
|
||||||
elements: readonly ExcalidrawElement[],
|
elements: readonly ExcalidrawElement[],
|
||||||
appState: AppState,
|
appState: AppState,
|
||||||
@ -521,6 +535,8 @@ const ExcalidrawWrapper = () => {
|
|||||||
collabAPI.syncElements(elements);
|
collabAPI.syncElements(elements);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setTheme(appState.theme);
|
||||||
|
|
||||||
// this check is redundant, but since this is a hot path, it's best
|
// this check is redundant, but since this is a hot path, it's best
|
||||||
// not to evaludate the nested expression every time
|
// not to evaludate the nested expression every time
|
||||||
if (!LocalData.isSavePaused()) {
|
if (!LocalData.isSavePaused()) {
|
||||||
@ -710,6 +726,7 @@ const ExcalidrawWrapper = () => {
|
|||||||
onPointerUpdate={collabAPI?.onPointerUpdate}
|
onPointerUpdate={collabAPI?.onPointerUpdate}
|
||||||
UIOptions={{
|
UIOptions={{
|
||||||
canvasActions: {
|
canvasActions: {
|
||||||
|
toggleTheme: true,
|
||||||
export: {
|
export: {
|
||||||
onExportToBackend,
|
onExportToBackend,
|
||||||
renderCustomUI: (elements, appState, files) => {
|
renderCustomUI: (elements, appState, files) => {
|
||||||
@ -739,6 +756,7 @@ const ExcalidrawWrapper = () => {
|
|||||||
handleKeyboardGlobally={true}
|
handleKeyboardGlobally={true}
|
||||||
onLibraryChange={onLibraryChange}
|
onLibraryChange={onLibraryChange}
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
|
theme={theme}
|
||||||
/>
|
/>
|
||||||
{excalidrawAPI && <Collab excalidrawAPI={excalidrawAPI} />}
|
{excalidrawAPI && <Collab excalidrawAPI={excalidrawAPI} />}
|
||||||
{errorMessage && (
|
{errorMessage && (
|
||||||
|
@ -17,11 +17,13 @@ Please add the latest change on the top under the correct section.
|
|||||||
|
|
||||||
#### Features
|
#### Features
|
||||||
|
|
||||||
|
- Support [theme](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#theme) to be semi-controlled [#5660](https://github.com/excalidraw/excalidraw/pull/5660).
|
||||||
- Added support for storing [`customData`](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#storing-custom-data-to-excalidraw-elements) on Excalidraw elements [#5592].
|
- Added support for storing [`customData`](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#storing-custom-data-to-excalidraw-elements) on Excalidraw elements [#5592].
|
||||||
- Added `exportPadding?: number;` to [exportToCanvas](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#exporttocanvas) and [exportToBlob](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#exporttoblob). The default value of the padding is 10.
|
- Added `exportPadding?: number;` to [exportToCanvas](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#exporttocanvas) and [exportToBlob](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#exporttoblob). The default value of the padding is 10.
|
||||||
|
|
||||||
#### Breaking Changes
|
#### Breaking Changes
|
||||||
|
|
||||||
|
- `props.UIOptions.canvasActions.theme` is now renamed to `props.UIOptions.canvasActions.toggleTheme` [#5660](https://github.com/excalidraw/excalidraw/pull/5660).
|
||||||
- `setToastMessage` API is now renamed to `setToast` API and the function signature is also updated [#5427](https://github.com/excalidraw/excalidraw/pull/5427). You can also pass `duration` and `closable` attributes along with `message`.
|
- `setToastMessage` API is now renamed to `setToast` API and the function signature is also updated [#5427](https://github.com/excalidraw/excalidraw/pull/5427). You can also pass `duration` and `closable` attributes along with `message`.
|
||||||
|
|
||||||
## 0.12.0 (2022-07-07)
|
## 0.12.0 (2022-07-07)
|
||||||
|
@ -637,7 +637,9 @@ If supplied, this URL will be used when user tries to install a library from [li
|
|||||||
|
|
||||||
#### `theme`
|
#### `theme`
|
||||||
|
|
||||||
This prop controls Excalidraw's theme. When supplied, the value takes precedence over `intialData.appState.theme`, the theme will be fully controlled by the host app, and users won't be able to toggle it from within the app. You can use [`THEME`](#THEME-1) to specify the theme.
|
This prop controls Excalidraw's theme. When supplied, the value takes precedence over `intialData.appState.theme`, the theme will be fully controlled by the host app, and users won't be able to toggle it from within the app unless `UIOptions.canvasActions.toggleTheme` is set to `true`, in which case the `theme` prop will control Excalidraw's default theme with ability to allow theme switching (you must take care of updating the `theme` prop when you detect a change to `appState.theme` from the [onChange](#onChange) callback).
|
||||||
|
|
||||||
|
You can use [`THEME`](#THEME-1) to specify the theme.
|
||||||
|
|
||||||
#### `name`
|
#### `name`
|
||||||
|
|
||||||
@ -660,7 +662,7 @@ This prop can be used to customise UI of Excalidraw. Currently we support custom
|
|||||||
| `export` | false | [exportOpts](#exportOpts) | <pre>{ saveFileToDisk: true }</pre> | This prop allows to customize the UI inside the export dialog. By default it shows the "saveFileToDisk". If this prop is `false` the export button will not be rendered. For more details visit [`exportOpts`](#exportOpts). |
|
| `export` | false | [exportOpts](#exportOpts) | <pre>{ saveFileToDisk: true }</pre> | This prop allows to customize the UI inside the export dialog. By default it shows the "saveFileToDisk". If this prop is `false` the export button will not be rendered. For more details visit [`exportOpts`](#exportOpts). |
|
||||||
| `loadScene` | boolean | true | Implies whether to show `Load button` |
|
| `loadScene` | boolean | true | Implies whether to show `Load button` |
|
||||||
| `saveToActiveFile` | boolean | true | Implies whether to show `Save button` to save to current file |
|
| `saveToActiveFile` | boolean | true | Implies whether to show `Save button` to save to current file |
|
||||||
| `theme` | boolean | true | Implies whether to show `Theme toggle` |
|
| `toggleTheme` | boolean | null | null | Implies whether to show `Theme toggle`. When defined as `boolean`, takes precedence over [`props.theme`](#theme) to show `Theme toggle` |
|
||||||
| `saveAsImage` | boolean | true | Implies whether to show `Save as image button` |
|
| `saveAsImage` | boolean | true | Implies whether to show `Save as image button` |
|
||||||
|
|
||||||
##### `dockedSidebarBreakpoint`
|
##### `dockedSidebarBreakpoint`
|
||||||
|
@ -56,6 +56,13 @@ const ExcalidrawBase = (props: ExcalidrawProps) => {
|
|||||||
DEFAULT_UI_OPTIONS.canvasActions.export.saveFileToDisk;
|
DEFAULT_UI_OPTIONS.canvasActions.export.saveFileToDisk;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
UIOptions.canvasActions.toggleTheme === null &&
|
||||||
|
typeof theme === "undefined"
|
||||||
|
) {
|
||||||
|
UIOptions.canvasActions.toggleTheme = true;
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Block pinch-zooming on iOS outside of the content area
|
// Block pinch-zooming on iOS outside of the content area
|
||||||
const handleTouchMove = (event: TouchEvent) => {
|
const handleTouchMove = (event: TouchEvent) => {
|
||||||
@ -75,7 +82,7 @@ const ExcalidrawBase = (props: ExcalidrawProps) => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<InitializeApp langCode={langCode}>
|
<InitializeApp langCode={langCode} theme={theme}>
|
||||||
<Provider unstable_createStore={() => jotaiStore} scope={jotaiScope}>
|
<Provider unstable_createStore={() => jotaiStore} scope={jotaiScope}>
|
||||||
<App
|
<App
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
|
@ -88,21 +88,42 @@ describe("<Excalidraw/>", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("Test theme prop", () => {
|
describe("Test theme prop", () => {
|
||||||
it('should show the dark mode toggle when the theme prop is "undefined"', async () => {
|
it("should show the theme toggle by default", async () => {
|
||||||
const { container } = await render(<Excalidraw />);
|
const { container } = await render(<Excalidraw />);
|
||||||
expect(h.state.theme).toBe(THEME.LIGHT);
|
expect(h.state.theme).toBe(THEME.LIGHT);
|
||||||
|
|
||||||
const darkModeToggle = queryByTestId(container, "toggle-dark-mode");
|
const darkModeToggle = queryByTestId(container, "toggle-dark-mode");
|
||||||
|
|
||||||
expect(darkModeToggle).toBeTruthy();
|
expect(darkModeToggle).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not show the dark mode toggle when the theme prop is not "undefined"', async () => {
|
it("should not show theme toggle when the theme prop is defined", async () => {
|
||||||
const { container } = await render(<Excalidraw theme="dark" />);
|
const { container } = await render(<Excalidraw theme="dark" />);
|
||||||
expect(h.state.theme).toBe(THEME.DARK);
|
expect(h.state.theme).toBe(THEME.DARK);
|
||||||
|
|
||||||
expect(queryByTestId(container, "toggle-dark-mode")).toBe(null);
|
expect(queryByTestId(container, "toggle-dark-mode")).toBe(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should show theme mode toggle when `UIOptions.canvasActions.toggleTheme` is true", async () => {
|
||||||
|
const { container } = await render(
|
||||||
|
<Excalidraw
|
||||||
|
theme={THEME.DARK}
|
||||||
|
UIOptions={{ canvasActions: { toggleTheme: true } }}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
expect(h.state.theme).toBe(THEME.DARK);
|
||||||
|
const darkModeToggle = queryByTestId(container, "toggle-dark-mode");
|
||||||
|
expect(darkModeToggle).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not show theme toggle when `UIOptions.canvasActions.toggleTheme` is false", async () => {
|
||||||
|
const { container } = await render(
|
||||||
|
<Excalidraw
|
||||||
|
UIOptions={{ canvasActions: { toggleTheme: false } }}
|
||||||
|
theme={THEME.DARK}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
expect(h.state.theme).toBe(THEME.DARK);
|
||||||
|
const darkModeToggle = queryByTestId(container, "toggle-dark-mode");
|
||||||
|
expect(darkModeToggle).toBeFalsy();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Test name prop", () => {
|
describe("Test name prop", () => {
|
||||||
@ -214,7 +235,7 @@ describe("<Excalidraw/>", () => {
|
|||||||
|
|
||||||
it("should hide the theme toggle when theme is false", async () => {
|
it("should hide the theme toggle when theme is false", async () => {
|
||||||
const { container } = await render(
|
const { container } = await render(
|
||||||
<Excalidraw UIOptions={{ canvasActions: { theme: false } }} />,
|
<Excalidraw UIOptions={{ canvasActions: { toggleTheme: false } }} />,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(queryByTestId(container, "toggle-dark-mode")).toBeNull();
|
expect(queryByTestId(container, "toggle-dark-mode")).toBeNull();
|
||||||
|
@ -344,13 +344,17 @@ export type ExportOpts = {
|
|||||||
) => JSX.Element;
|
) => JSX.Element;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// NOTE at the moment, if action name coressponds to canvasAction prop, its
|
||||||
|
// truthiness value will determine whether the action is rendered or not
|
||||||
|
// (see manager renderAction). We also override canvasAction values in
|
||||||
|
// excalidraw package index.tsx.
|
||||||
type CanvasActions = {
|
type CanvasActions = {
|
||||||
changeViewBackgroundColor?: boolean;
|
changeViewBackgroundColor?: boolean;
|
||||||
clearCanvas?: boolean;
|
clearCanvas?: boolean;
|
||||||
export?: false | ExportOpts;
|
export?: false | ExportOpts;
|
||||||
loadScene?: boolean;
|
loadScene?: boolean;
|
||||||
saveToActiveFile?: boolean;
|
saveToActiveFile?: boolean;
|
||||||
theme?: boolean;
|
toggleTheme?: boolean | null;
|
||||||
saveAsImage?: boolean;
|
saveAsImage?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user