feat: add view mode in Excalidraw (#2840)
Co-authored-by: Lipis <lipiridis@gmail.com>
This commit is contained in:
parent
2b1b62d8f2
commit
675da16ca4
22
src/actions/actionToggleViewMode.tsx
Normal file
22
src/actions/actionToggleViewMode.tsx
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { CODES, KEYS } from "../keys";
|
||||||
|
import { register } from "./register";
|
||||||
|
import { trackEvent } from "../analytics";
|
||||||
|
|
||||||
|
export const actionToggleViewMode = register({
|
||||||
|
name: "viewMode",
|
||||||
|
perform(elements, appState) {
|
||||||
|
trackEvent("view", "mode", "view");
|
||||||
|
return {
|
||||||
|
appState: {
|
||||||
|
...appState,
|
||||||
|
viewModeEnabled: !this.checked!(appState),
|
||||||
|
selectedElementIds: {},
|
||||||
|
},
|
||||||
|
commitToHistory: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
checked: (appState) => appState.viewModeEnabled,
|
||||||
|
contextItemLabel: "labels.viewMode",
|
||||||
|
keyTest: (event) =>
|
||||||
|
!event[KEYS.CTRL_OR_CMD] && event.altKey && event.code === CODES.R,
|
||||||
|
});
|
@ -7,11 +7,11 @@ import {
|
|||||||
ActionResult,
|
ActionResult,
|
||||||
} from "./types";
|
} from "./types";
|
||||||
import { ExcalidrawElement } from "../element/types";
|
import { ExcalidrawElement } from "../element/types";
|
||||||
import { AppState } from "../types";
|
import { AppState, ExcalidrawProps } from "../types";
|
||||||
|
|
||||||
// This is the <App> component, but for now we don't care about anything but its
|
// This is the <App> component, but for now we don't care about anything but its
|
||||||
// `canvas` state.
|
// `canvas` state.
|
||||||
type App = { canvas: HTMLCanvasElement | null };
|
type App = { canvas: HTMLCanvasElement | null; props: ExcalidrawProps };
|
||||||
|
|
||||||
export class ActionManager implements ActionsManagerInterface {
|
export class ActionManager implements ActionsManagerInterface {
|
||||||
actions = {} as ActionsManagerInterface["actions"];
|
actions = {} as ActionsManagerInterface["actions"];
|
||||||
@ -66,6 +66,12 @@ export class ActionManager implements ActionsManagerInterface {
|
|||||||
if (data.length === 0) {
|
if (data.length === 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
const { viewModeEnabled } = this.getAppState();
|
||||||
|
if (viewModeEnabled) {
|
||||||
|
if (data[0].name !== "viewMode") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
this.updater(
|
this.updater(
|
||||||
|
@ -22,7 +22,8 @@ export type ShortcutName =
|
|||||||
| "gridMode"
|
| "gridMode"
|
||||||
| "zenMode"
|
| "zenMode"
|
||||||
| "stats"
|
| "stats"
|
||||||
| "addToLibrary";
|
| "addToLibrary"
|
||||||
|
| "viewMode";
|
||||||
|
|
||||||
const shortcutMap: Record<ShortcutName, string[]> = {
|
const shortcutMap: Record<ShortcutName, string[]> = {
|
||||||
cut: [getShortcutKey("CtrlOrCmd+X")],
|
cut: [getShortcutKey("CtrlOrCmd+X")],
|
||||||
@ -56,6 +57,7 @@ const shortcutMap: Record<ShortcutName, string[]> = {
|
|||||||
zenMode: [getShortcutKey("Alt+Z")],
|
zenMode: [getShortcutKey("Alt+Z")],
|
||||||
stats: [],
|
stats: [],
|
||||||
addToLibrary: [],
|
addToLibrary: [],
|
||||||
|
viewMode: [getShortcutKey("Alt+R")],
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getShortcutFromShortcutName = (name: ShortcutName) => {
|
export const getShortcutFromShortcutName = (name: ShortcutName) => {
|
||||||
|
@ -84,7 +84,8 @@ export type ActionName =
|
|||||||
| "alignVerticallyCentered"
|
| "alignVerticallyCentered"
|
||||||
| "alignHorizontallyCentered"
|
| "alignHorizontallyCentered"
|
||||||
| "distributeHorizontally"
|
| "distributeHorizontally"
|
||||||
| "distributeVertically";
|
| "distributeVertically"
|
||||||
|
| "viewMode";
|
||||||
|
|
||||||
export interface Action {
|
export interface Action {
|
||||||
name: ActionName;
|
name: ActionName;
|
||||||
|
@ -72,6 +72,7 @@ export const getDefaultAppState = (): Omit<
|
|||||||
width: window.innerWidth,
|
width: window.innerWidth,
|
||||||
zenModeEnabled: false,
|
zenModeEnabled: false,
|
||||||
zoom: { value: 1 as NormalizedZoomValue, translation: { x: 0, y: 0 } },
|
zoom: { value: 1 as NormalizedZoomValue, translation: { x: 0, y: 0 } },
|
||||||
|
viewModeEnabled: false,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -151,6 +152,7 @@ const APP_STATE_STORAGE_CONF = (<
|
|||||||
width: { browser: false, export: false },
|
width: { browser: false, export: false },
|
||||||
zenModeEnabled: { browser: true, export: false },
|
zenModeEnabled: { browser: true, export: false },
|
||||||
zoom: { browser: true, export: false },
|
zoom: { browser: true, export: false },
|
||||||
|
viewModeEnabled: { browser: false, export: false },
|
||||||
});
|
});
|
||||||
|
|
||||||
const _clearAppStateForStorage = <ExportType extends "export" | "browser">(
|
const _clearAppStateForStorage = <ExportType extends "export" | "browser">(
|
||||||
|
@ -2,6 +2,8 @@ import { Point, simplify } from "points-on-curve";
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { RoughCanvas } from "roughjs/bin/canvas";
|
import { RoughCanvas } from "roughjs/bin/canvas";
|
||||||
import rough from "roughjs/bin/rough";
|
import rough from "roughjs/bin/rough";
|
||||||
|
import clsx from "clsx";
|
||||||
|
|
||||||
import "../actions";
|
import "../actions";
|
||||||
import {
|
import {
|
||||||
actionAddToLibrary,
|
actionAddToLibrary,
|
||||||
@ -175,10 +177,11 @@ import {
|
|||||||
withBatchedUpdates,
|
withBatchedUpdates,
|
||||||
} from "../utils";
|
} from "../utils";
|
||||||
import { isMobile } from "../is-mobile";
|
import { isMobile } from "../is-mobile";
|
||||||
import ContextMenu from "./ContextMenu";
|
import ContextMenu, { ContextMenuOption } from "./ContextMenu";
|
||||||
import LayerUI from "./LayerUI";
|
import LayerUI from "./LayerUI";
|
||||||
import { Stats } from "./Stats";
|
import { Stats } from "./Stats";
|
||||||
import { Toast } from "./Toast";
|
import { Toast } from "./Toast";
|
||||||
|
import { actionToggleViewMode } from "../actions/actionToggleViewMode";
|
||||||
|
|
||||||
const { history } = createHistory();
|
const { history } = createHistory();
|
||||||
|
|
||||||
@ -295,6 +298,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
offsetLeft,
|
offsetLeft,
|
||||||
offsetTop,
|
offsetTop,
|
||||||
excalidrawRef,
|
excalidrawRef,
|
||||||
|
viewModeEnabled = false,
|
||||||
} = props;
|
} = props;
|
||||||
this.state = {
|
this.state = {
|
||||||
...defaultAppState,
|
...defaultAppState,
|
||||||
@ -302,6 +306,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
...this.getCanvasOffsets({ offsetLeft, offsetTop }),
|
...this.getCanvasOffsets({ offsetLeft, offsetTop }),
|
||||||
|
viewModeEnabled,
|
||||||
};
|
};
|
||||||
if (excalidrawRef) {
|
if (excalidrawRef) {
|
||||||
const readyPromise =
|
const readyPromise =
|
||||||
@ -342,6 +347,62 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
this.actionManager.registerAction(createRedoAction(history));
|
this.actionManager.registerAction(createRedoAction(history));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private renderCanvas() {
|
||||||
|
const canvasScale = window.devicePixelRatio;
|
||||||
|
const {
|
||||||
|
width: canvasDOMWidth,
|
||||||
|
height: canvasDOMHeight,
|
||||||
|
viewModeEnabled,
|
||||||
|
} = this.state;
|
||||||
|
const canvasWidth = canvasDOMWidth * canvasScale;
|
||||||
|
const canvasHeight = canvasDOMHeight * canvasScale;
|
||||||
|
if (viewModeEnabled) {
|
||||||
|
return (
|
||||||
|
<canvas
|
||||||
|
id="canvas"
|
||||||
|
style={{
|
||||||
|
width: canvasDOMWidth,
|
||||||
|
height: canvasDOMHeight,
|
||||||
|
cursor: "grabbing",
|
||||||
|
}}
|
||||||
|
width={canvasWidth}
|
||||||
|
height={canvasHeight}
|
||||||
|
ref={this.handleCanvasRef}
|
||||||
|
onContextMenu={this.handleCanvasContextMenu}
|
||||||
|
onPointerMove={this.handleCanvasPointerMove}
|
||||||
|
onPointerUp={this.removePointer}
|
||||||
|
onPointerCancel={this.removePointer}
|
||||||
|
onTouchMove={this.handleTouchMove}
|
||||||
|
onPointerDown={this.handleCanvasPointerDown}
|
||||||
|
>
|
||||||
|
{t("labels.drawingCanvas")}
|
||||||
|
</canvas>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<canvas
|
||||||
|
id="canvas"
|
||||||
|
style={{
|
||||||
|
width: canvasDOMWidth,
|
||||||
|
height: canvasDOMHeight,
|
||||||
|
}}
|
||||||
|
width={canvasWidth}
|
||||||
|
height={canvasHeight}
|
||||||
|
ref={this.handleCanvasRef}
|
||||||
|
onContextMenu={this.handleCanvasContextMenu}
|
||||||
|
onPointerDown={this.handleCanvasPointerDown}
|
||||||
|
onDoubleClick={this.handleCanvasDoubleClick}
|
||||||
|
onPointerMove={this.handleCanvasPointerMove}
|
||||||
|
onPointerUp={this.removePointer}
|
||||||
|
onPointerCancel={this.removePointer}
|
||||||
|
onTouchMove={this.handleTouchMove}
|
||||||
|
onDrop={this.handleCanvasOnDrop}
|
||||||
|
>
|
||||||
|
{t("labels.drawingCanvas")}
|
||||||
|
</canvas>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const {
|
const {
|
||||||
zenModeEnabled,
|
zenModeEnabled,
|
||||||
@ -349,20 +410,19 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
height: canvasDOMHeight,
|
height: canvasDOMHeight,
|
||||||
offsetTop,
|
offsetTop,
|
||||||
offsetLeft,
|
offsetLeft,
|
||||||
|
viewModeEnabled,
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
const { onCollabButtonClick, onExportToBackend, renderFooter } = this.props;
|
const { onCollabButtonClick, onExportToBackend, renderFooter } = this.props;
|
||||||
const canvasScale = window.devicePixelRatio;
|
|
||||||
|
|
||||||
const canvasWidth = canvasDOMWidth * canvasScale;
|
|
||||||
const canvasHeight = canvasDOMHeight * canvasScale;
|
|
||||||
|
|
||||||
const DEFAULT_PASTE_X = canvasDOMWidth / 2;
|
const DEFAULT_PASTE_X = canvasDOMWidth / 2;
|
||||||
const DEFAULT_PASTE_Y = canvasDOMHeight / 2;
|
const DEFAULT_PASTE_Y = canvasDOMHeight / 2;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="excalidraw"
|
className={clsx("excalidraw", {
|
||||||
|
"excalidraw--view-mode": viewModeEnabled,
|
||||||
|
})}
|
||||||
ref={this.excalidrawContainerRef}
|
ref={this.excalidrawContainerRef}
|
||||||
style={{
|
style={{
|
||||||
width: canvasDOMWidth,
|
width: canvasDOMWidth,
|
||||||
@ -392,6 +452,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
isCollaborating={this.props.isCollaborating || false}
|
isCollaborating={this.props.isCollaborating || false}
|
||||||
onExportToBackend={onExportToBackend}
|
onExportToBackend={onExportToBackend}
|
||||||
renderCustomFooter={renderFooter}
|
renderCustomFooter={renderFooter}
|
||||||
|
viewModeEnabled={viewModeEnabled}
|
||||||
/>
|
/>
|
||||||
{this.state.showStats && (
|
{this.state.showStats && (
|
||||||
<Stats
|
<Stats
|
||||||
@ -406,28 +467,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
clearToast={this.clearToast}
|
clearToast={this.clearToast}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<main>
|
<main>{this.renderCanvas()}</main>
|
||||||
<canvas
|
|
||||||
id="canvas"
|
|
||||||
style={{
|
|
||||||
width: canvasDOMWidth,
|
|
||||||
height: canvasDOMHeight,
|
|
||||||
}}
|
|
||||||
width={canvasWidth}
|
|
||||||
height={canvasHeight}
|
|
||||||
ref={this.handleCanvasRef}
|
|
||||||
onContextMenu={this.handleCanvasContextMenu}
|
|
||||||
onPointerDown={this.handleCanvasPointerDown}
|
|
||||||
onDoubleClick={this.handleCanvasDoubleClick}
|
|
||||||
onPointerMove={this.handleCanvasPointerMove}
|
|
||||||
onPointerUp={this.removePointer}
|
|
||||||
onPointerCancel={this.removePointer}
|
|
||||||
onTouchMove={this.handleTouchMove}
|
|
||||||
onDrop={this.handleCanvasOnDrop}
|
|
||||||
>
|
|
||||||
{t("labels.drawingCanvas")}
|
|
||||||
</canvas>
|
|
||||||
</main>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -467,6 +507,13 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
if (actionResult.commitToHistory) {
|
if (actionResult.commitToHistory) {
|
||||||
history.resumeRecording();
|
history.resumeRecording();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let viewModeEnabled = actionResult?.appState?.viewModeEnabled || false;
|
||||||
|
|
||||||
|
if (typeof this.props.viewModeEnabled !== "undefined") {
|
||||||
|
viewModeEnabled = this.props.viewModeEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
this.setState(
|
this.setState(
|
||||||
(state) => ({
|
(state) => ({
|
||||||
...actionResult.appState,
|
...actionResult.appState,
|
||||||
@ -476,6 +523,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
height: state.height,
|
height: state.height,
|
||||||
offsetTop: state.offsetTop,
|
offsetTop: state.offsetTop,
|
||||||
offsetLeft: state.offsetLeft,
|
offsetLeft: state.offsetLeft,
|
||||||
|
viewModeEnabled,
|
||||||
}),
|
}),
|
||||||
() => {
|
() => {
|
||||||
if (actionResult.syncHistory) {
|
if (actionResult.syncHistory) {
|
||||||
@ -658,7 +706,6 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.scene.addCallback(this.onSceneUpdated);
|
this.scene.addCallback(this.onSceneUpdated);
|
||||||
|
|
||||||
this.addEventListeners();
|
this.addEventListeners();
|
||||||
|
|
||||||
// optim to avoid extra render on init
|
// optim to avoid extra render on init
|
||||||
@ -725,25 +772,16 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private addEventListeners() {
|
private addEventListeners() {
|
||||||
|
this.removeEventListeners();
|
||||||
document.addEventListener(EVENT.COPY, this.onCopy);
|
document.addEventListener(EVENT.COPY, this.onCopy);
|
||||||
document.addEventListener(EVENT.PASTE, this.pasteFromClipboard);
|
|
||||||
document.addEventListener(EVENT.CUT, this.onCut);
|
|
||||||
|
|
||||||
document.addEventListener(EVENT.KEYDOWN, this.onKeyDown, false);
|
document.addEventListener(EVENT.KEYDOWN, this.onKeyDown, false);
|
||||||
document.addEventListener(EVENT.KEYUP, this.onKeyUp, { passive: true });
|
document.addEventListener(EVENT.KEYUP, this.onKeyUp, { passive: true });
|
||||||
document.addEventListener(
|
document.addEventListener(
|
||||||
EVENT.MOUSE_MOVE,
|
EVENT.MOUSE_MOVE,
|
||||||
this.updateCurrentCursorPosition,
|
this.updateCurrentCursorPosition,
|
||||||
);
|
);
|
||||||
window.addEventListener(EVENT.RESIZE, this.onResize, false);
|
|
||||||
window.addEventListener(EVENT.UNLOAD, this.onUnload, false);
|
|
||||||
window.addEventListener(EVENT.BLUR, this.onBlur, false);
|
|
||||||
window.addEventListener(EVENT.DRAG_OVER, this.disableEvent, false);
|
|
||||||
window.addEventListener(EVENT.DROP, this.disableEvent, false);
|
|
||||||
|
|
||||||
// rerender text elements on font load to fix #637 && #1553
|
// rerender text elements on font load to fix #637 && #1553
|
||||||
document.fonts?.addEventListener?.("loadingdone", this.onFontLoaded);
|
document.fonts?.addEventListener?.("loadingdone", this.onFontLoaded);
|
||||||
|
|
||||||
// Safari-only desktop pinch zoom
|
// Safari-only desktop pinch zoom
|
||||||
document.addEventListener(
|
document.addEventListener(
|
||||||
EVENT.GESTURE_START,
|
EVENT.GESTURE_START,
|
||||||
@ -760,6 +798,18 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
this.onGestureEnd as any,
|
this.onGestureEnd as any,
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
if (this.state.viewModeEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener(EVENT.PASTE, this.pasteFromClipboard);
|
||||||
|
document.addEventListener(EVENT.CUT, this.onCut);
|
||||||
|
|
||||||
|
window.addEventListener(EVENT.RESIZE, this.onResize, false);
|
||||||
|
window.addEventListener(EVENT.UNLOAD, this.onUnload, false);
|
||||||
|
window.addEventListener(EVENT.BLUR, this.onBlur, false);
|
||||||
|
window.addEventListener(EVENT.DRAG_OVER, this.disableEvent, false);
|
||||||
|
window.addEventListener(EVENT.DROP, this.disableEvent, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps: ExcalidrawProps, prevState: AppState) {
|
componentDidUpdate(prevProps: ExcalidrawProps, prevState: AppState) {
|
||||||
@ -782,6 +832,17 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (prevProps.viewModeEnabled !== this.props.viewModeEnabled) {
|
||||||
|
this.setState(
|
||||||
|
{ viewModeEnabled: !!this.props.viewModeEnabled },
|
||||||
|
this.addEventListeners,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prevState.viewModeEnabled !== this.state.viewModeEnabled) {
|
||||||
|
this.addEventListeners();
|
||||||
|
}
|
||||||
|
|
||||||
document
|
document
|
||||||
.querySelector(".excalidraw")
|
.querySelector(".excalidraw")
|
||||||
?.classList.toggle("Appearance_dark", this.state.appearance === "dark");
|
?.classList.toggle("Appearance_dark", this.state.appearance === "dark");
|
||||||
@ -1134,10 +1195,6 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
this.actionManager.executeAction(actionToggleZenMode);
|
this.actionManager.executeAction(actionToggleZenMode);
|
||||||
};
|
};
|
||||||
|
|
||||||
toggleGridMode = () => {
|
|
||||||
this.actionManager.executeAction(actionToggleGridMode);
|
|
||||||
};
|
|
||||||
|
|
||||||
toggleStats = () => {
|
toggleStats = () => {
|
||||||
if (!this.state.showStats) {
|
if (!this.state.showStats) {
|
||||||
trackEvent("dialog", "stats");
|
trackEvent("dialog", "stats");
|
||||||
@ -1232,14 +1289,18 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event[KEYS.CTRL_OR_CMD]) {
|
|
||||||
this.setState({ isBindingEnabled: false });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.actionManager.handleKeyDown(event)) {
|
if (this.actionManager.handleKeyDown(event)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.state.viewModeEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event[KEYS.CTRL_OR_CMD]) {
|
||||||
|
this.setState({ isBindingEnabled: false });
|
||||||
|
}
|
||||||
|
|
||||||
if (event.code === CODES.NINE) {
|
if (event.code === CODES.NINE) {
|
||||||
this.setState({ isLibraryOpen: !this.state.isLibraryOpen });
|
this.setState({ isLibraryOpen: !this.state.isLibraryOpen });
|
||||||
}
|
}
|
||||||
@ -2046,6 +2107,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
|
|
||||||
lastPointerUp = onPointerUp;
|
lastPointerUp = onPointerUp;
|
||||||
|
|
||||||
|
if (!this.state.viewModeEnabled) {
|
||||||
window.addEventListener(EVENT.POINTER_MOVE, onPointerMove);
|
window.addEventListener(EVENT.POINTER_MOVE, onPointerMove);
|
||||||
window.addEventListener(EVENT.POINTER_UP, onPointerUp);
|
window.addEventListener(EVENT.POINTER_UP, onPointerUp);
|
||||||
window.addEventListener(EVENT.KEYDOWN, onKeyDown);
|
window.addEventListener(EVENT.KEYDOWN, onKeyDown);
|
||||||
@ -2054,6 +2116,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
pointerDownState.eventListeners.onUp = onPointerUp;
|
pointerDownState.eventListeners.onUp = onPointerUp;
|
||||||
pointerDownState.eventListeners.onKeyUp = onKeyUp;
|
pointerDownState.eventListeners.onKeyUp = onKeyUp;
|
||||||
pointerDownState.eventListeners.onKeyDown = onKeyDown;
|
pointerDownState.eventListeners.onKeyDown = onKeyDown;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private maybeOpenContextMenuAfterPointerDownOnTouchDevices = (
|
private maybeOpenContextMenuAfterPointerDownOnTouchDevices = (
|
||||||
@ -2103,7 +2166,8 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
!(
|
!(
|
||||||
gesture.pointers.size === 0 &&
|
gesture.pointers.size === 0 &&
|
||||||
(event.button === POINTER_BUTTON.WHEEL ||
|
(event.button === POINTER_BUTTON.WHEEL ||
|
||||||
(event.button === POINTER_BUTTON.MAIN && isHoldingSpace))
|
(event.button === POINTER_BUTTON.MAIN && isHoldingSpace) ||
|
||||||
|
this.state.viewModeEnabled)
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
@ -3590,7 +3654,36 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
|
|
||||||
const elements = this.scene.getElements();
|
const elements = this.scene.getElements();
|
||||||
const element = this.getElementAtPosition(x, y);
|
const element = this.getElementAtPosition(x, y);
|
||||||
|
const options: ContextMenuOption[] = [];
|
||||||
|
if (probablySupportsClipboardBlob && elements.length > 0) {
|
||||||
|
options.push(actionCopyAsPng);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (probablySupportsClipboardWriteText && elements.length > 0) {
|
||||||
|
options.push(actionCopyAsSvg);
|
||||||
|
}
|
||||||
if (!element) {
|
if (!element) {
|
||||||
|
const viewModeOptions: ContextMenuOption[] = [
|
||||||
|
...options,
|
||||||
|
actionToggleStats,
|
||||||
|
];
|
||||||
|
|
||||||
|
if (typeof this.props.viewModeEnabled === "undefined") {
|
||||||
|
viewModeOptions.push(actionToggleViewMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
ContextMenu.push({
|
||||||
|
options: viewModeOptions,
|
||||||
|
top: clientY,
|
||||||
|
left: clientX,
|
||||||
|
actionManager: this.actionManager,
|
||||||
|
appState: this.state,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.state.viewModeEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ContextMenu.push({
|
ContextMenu.push({
|
||||||
options: [
|
options: [
|
||||||
_isMobile &&
|
_isMobile &&
|
||||||
@ -3618,6 +3711,8 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
separator,
|
separator,
|
||||||
actionToggleGridMode,
|
actionToggleGridMode,
|
||||||
actionToggleZenMode,
|
actionToggleZenMode,
|
||||||
|
typeof this.props.viewModeEnabled === "undefined" &&
|
||||||
|
actionToggleViewMode,
|
||||||
actionToggleStats,
|
actionToggleStats,
|
||||||
],
|
],
|
||||||
top: clientY,
|
top: clientY,
|
||||||
@ -3632,6 +3727,17 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
this.setState({ selectedElementIds: { [element.id]: true } });
|
this.setState({ selectedElementIds: { [element.id]: true } });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.state.viewModeEnabled) {
|
||||||
|
ContextMenu.push({
|
||||||
|
options: [navigator.clipboard && actionCopy, ...options],
|
||||||
|
top: clientY,
|
||||||
|
left: clientX,
|
||||||
|
actionManager: this.actionManager,
|
||||||
|
appState: this.state,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ContextMenu.push({
|
ContextMenu.push({
|
||||||
options: [
|
options: [
|
||||||
_isMobile && actionCut,
|
_isMobile && actionCut,
|
||||||
@ -3648,8 +3754,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
contextItemLabel: "labels.paste",
|
contextItemLabel: "labels.paste",
|
||||||
},
|
},
|
||||||
_isMobile && separator,
|
_isMobile && separator,
|
||||||
probablySupportsClipboardBlob && actionCopyAsPng,
|
...options,
|
||||||
probablySupportsClipboardWriteText && actionCopyAsSvg,
|
|
||||||
separator,
|
separator,
|
||||||
actionCopyStyles,
|
actionCopyStyles,
|
||||||
actionPasteStyles,
|
actionPasteStyles,
|
||||||
|
@ -13,7 +13,7 @@ import { Action } from "../actions/types";
|
|||||||
import { ActionManager } from "../actions/manager";
|
import { ActionManager } from "../actions/manager";
|
||||||
import { AppState } from "../types";
|
import { AppState } from "../types";
|
||||||
|
|
||||||
type ContextMenuOption = "separator" | Action;
|
export type ContextMenuOption = "separator" | Action;
|
||||||
|
|
||||||
type ContextMenuProps = {
|
type ContextMenuProps = {
|
||||||
options: ContextMenuOption[];
|
options: ContextMenuOption[];
|
||||||
|
@ -227,6 +227,10 @@ export const HelpDialog = ({ onClose }: { onClose?: () => void }) => {
|
|||||||
label={t("labels.gridMode")}
|
label={t("labels.gridMode")}
|
||||||
shortcuts={[getShortcutKey("CtrlOrCmd+'")]}
|
shortcuts={[getShortcutKey("CtrlOrCmd+'")]}
|
||||||
/>
|
/>
|
||||||
|
<Shortcut
|
||||||
|
label={t("labels.viewMode")}
|
||||||
|
shortcuts={[getShortcutKey("Alt+R")]}
|
||||||
|
/>
|
||||||
</ShortcutIsland>
|
</ShortcutIsland>
|
||||||
</Column>
|
</Column>
|
||||||
<Column>
|
<Column>
|
||||||
|
@ -61,6 +61,7 @@ interface LayerUIProps {
|
|||||||
canvas: HTMLCanvasElement | null,
|
canvas: HTMLCanvasElement | null,
|
||||||
) => void;
|
) => void;
|
||||||
renderCustomFooter?: (isMobile: boolean) => JSX.Element;
|
renderCustomFooter?: (isMobile: boolean) => JSX.Element;
|
||||||
|
viewModeEnabled: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const useOnClickOutside = (
|
const useOnClickOutside = (
|
||||||
@ -299,6 +300,7 @@ const LayerUI = ({
|
|||||||
isCollaborating,
|
isCollaborating,
|
||||||
onExportToBackend,
|
onExportToBackend,
|
||||||
renderCustomFooter,
|
renderCustomFooter,
|
||||||
|
viewModeEnabled,
|
||||||
}: LayerUIProps) => {
|
}: LayerUIProps) => {
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
|
|
||||||
@ -358,6 +360,28 @@ const LayerUI = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const renderViewModeCanvasActions = () => {
|
||||||
|
return (
|
||||||
|
<Section
|
||||||
|
heading="canvasActions"
|
||||||
|
className={clsx("zen-mode-transition", {
|
||||||
|
"transition-left": zenModeEnabled,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{/* the zIndex ensures this menu has higher stacking order,
|
||||||
|
see https://github.com/excalidraw/excalidraw/pull/1445 */}
|
||||||
|
<Island padding={2} style={{ zIndex: 1 }}>
|
||||||
|
<Stack.Col gap={4}>
|
||||||
|
<Stack.Row gap={1} justifyContent="space-between">
|
||||||
|
{actionManager.renderAction("saveScene")}
|
||||||
|
{actionManager.renderAction("saveAsScene")}
|
||||||
|
{renderExportDialog()}
|
||||||
|
</Stack.Row>
|
||||||
|
</Stack.Col>
|
||||||
|
</Island>
|
||||||
|
</Section>
|
||||||
|
);
|
||||||
|
};
|
||||||
const renderCanvasActions = () => (
|
const renderCanvasActions = () => (
|
||||||
<Section
|
<Section
|
||||||
heading="canvasActions"
|
heading="canvasActions"
|
||||||
@ -448,9 +472,12 @@ const LayerUI = ({
|
|||||||
gap={4}
|
gap={4}
|
||||||
className={clsx({ "disable-pointerEvents": zenModeEnabled })}
|
className={clsx({ "disable-pointerEvents": zenModeEnabled })}
|
||||||
>
|
>
|
||||||
{renderCanvasActions()}
|
{viewModeEnabled
|
||||||
|
? renderViewModeCanvasActions()
|
||||||
|
: renderCanvasActions()}
|
||||||
{shouldRenderSelectedShapeActions && renderSelectedShapeActions()}
|
{shouldRenderSelectedShapeActions && renderSelectedShapeActions()}
|
||||||
</Stack.Col>
|
</Stack.Col>
|
||||||
|
{!viewModeEnabled && (
|
||||||
<Section heading="shapes">
|
<Section heading="shapes">
|
||||||
{(heading) => (
|
{(heading) => (
|
||||||
<Stack.Col gap={4} align="start">
|
<Stack.Col gap={4} align="start">
|
||||||
@ -480,6 +507,7 @@ const LayerUI = ({
|
|||||||
</Stack.Col>
|
</Stack.Col>
|
||||||
)}
|
)}
|
||||||
</Section>
|
</Section>
|
||||||
|
)}
|
||||||
<UserList
|
<UserList
|
||||||
className={clsx("zen-mode-transition", {
|
className={clsx("zen-mode-transition", {
|
||||||
"transition-right": zenModeEnabled,
|
"transition-right": zenModeEnabled,
|
||||||
@ -524,6 +552,20 @@ const LayerUI = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const renderGitHubCorner = () => {
|
||||||
|
return (
|
||||||
|
<aside
|
||||||
|
className={clsx(
|
||||||
|
"layer-ui__wrapper__github-corner zen-mode-transition",
|
||||||
|
{
|
||||||
|
"transition-right": zenModeEnabled,
|
||||||
|
},
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<GitHubCorner appearance={appState.appearance} />
|
||||||
|
</aside>
|
||||||
|
);
|
||||||
|
};
|
||||||
const renderFooter = () => (
|
const renderFooter = () => (
|
||||||
<footer role="contentinfo" className="layer-ui__wrapper__footer">
|
<footer role="contentinfo" className="layer-ui__wrapper__footer">
|
||||||
<div
|
<div
|
||||||
@ -599,6 +641,7 @@ const LayerUI = ({
|
|||||||
canvas={canvas}
|
canvas={canvas}
|
||||||
isCollaborating={isCollaborating}
|
isCollaborating={isCollaborating}
|
||||||
renderCustomFooter={renderCustomFooter}
|
renderCustomFooter={renderCustomFooter}
|
||||||
|
viewModeEnabled={viewModeEnabled}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
@ -610,18 +653,7 @@ const LayerUI = ({
|
|||||||
{dialogs}
|
{dialogs}
|
||||||
{renderFixedSideContainer()}
|
{renderFixedSideContainer()}
|
||||||
{renderBottomAppMenu()}
|
{renderBottomAppMenu()}
|
||||||
{
|
{renderGitHubCorner()}
|
||||||
<aside
|
|
||||||
className={clsx(
|
|
||||||
"layer-ui__wrapper__github-corner zen-mode-transition",
|
|
||||||
{
|
|
||||||
"transition-right": zenModeEnabled,
|
|
||||||
},
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<GitHubCorner appearance={appState.appearance} />
|
|
||||||
</aside>
|
|
||||||
}
|
|
||||||
{renderFooter()}
|
{renderFooter()}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -29,6 +29,7 @@ type MobileMenuProps = {
|
|||||||
canvas: HTMLCanvasElement | null;
|
canvas: HTMLCanvasElement | null;
|
||||||
isCollaborating: boolean;
|
isCollaborating: boolean;
|
||||||
renderCustomFooter?: (isMobile: boolean) => JSX.Element;
|
renderCustomFooter?: (isMobile: boolean) => JSX.Element;
|
||||||
|
viewModeEnabled: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const MobileMenu = ({
|
export const MobileMenu = ({
|
||||||
@ -43,8 +44,10 @@ export const MobileMenu = ({
|
|||||||
canvas,
|
canvas,
|
||||||
isCollaborating,
|
isCollaborating,
|
||||||
renderCustomFooter,
|
renderCustomFooter,
|
||||||
}: MobileMenuProps) => (
|
viewModeEnabled,
|
||||||
<>
|
}: MobileMenuProps) => {
|
||||||
|
const renderFixedSideContainer = () => {
|
||||||
|
return (
|
||||||
<FixedSideContainer side="top">
|
<FixedSideContainer side="top">
|
||||||
<Section heading="shapes">
|
<Section heading="shapes">
|
||||||
{(heading) => (
|
{(heading) => (
|
||||||
@ -72,6 +75,68 @@ export const MobileMenu = ({
|
|||||||
</Section>
|
</Section>
|
||||||
<HintViewer appState={appState} elements={elements} />
|
<HintViewer appState={appState} elements={elements} />
|
||||||
</FixedSideContainer>
|
</FixedSideContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderAppToolbar = () => {
|
||||||
|
if (viewModeEnabled) {
|
||||||
|
return (
|
||||||
|
<div className="App-toolbar-content">
|
||||||
|
{actionManager.renderAction("toggleCanvasMenu")}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className="App-toolbar-content">
|
||||||
|
{actionManager.renderAction("toggleCanvasMenu")}
|
||||||
|
{actionManager.renderAction("toggleEditMenu")}
|
||||||
|
{actionManager.renderAction("undo")}
|
||||||
|
{actionManager.renderAction("redo")}
|
||||||
|
{actionManager.renderAction(
|
||||||
|
appState.multiElement ? "finalize" : "duplicateSelection",
|
||||||
|
)}
|
||||||
|
{actionManager.renderAction("deleteSelectedElements")}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderCanvasActions = () => {
|
||||||
|
if (viewModeEnabled) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{actionManager.renderAction("saveScene")}
|
||||||
|
{actionManager.renderAction("saveAsScene")}
|
||||||
|
{exportButton}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{actionManager.renderAction("loadScene")}
|
||||||
|
{actionManager.renderAction("saveScene")}
|
||||||
|
{actionManager.renderAction("saveAsScene")}
|
||||||
|
{exportButton}
|
||||||
|
{actionManager.renderAction("clearCanvas")}
|
||||||
|
{onCollabButtonClick && (
|
||||||
|
<CollabButton
|
||||||
|
isCollaborating={isCollaborating}
|
||||||
|
collaboratorCount={appState.collaborators.size}
|
||||||
|
onClick={onCollabButtonClick}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{
|
||||||
|
<BackgroundPickerAndDarkModeToggle
|
||||||
|
actionManager={actionManager}
|
||||||
|
appState={appState}
|
||||||
|
setAppState={setAppState}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{!viewModeEnabled && renderFixedSideContainer()}
|
||||||
<div
|
<div
|
||||||
className="App-bottom-bar"
|
className="App-bottom-bar"
|
||||||
style={{
|
style={{
|
||||||
@ -85,30 +150,16 @@ export const MobileMenu = ({
|
|||||||
<Section className="App-mobile-menu" heading="canvasActions">
|
<Section className="App-mobile-menu" heading="canvasActions">
|
||||||
<div className="panelColumn">
|
<div className="panelColumn">
|
||||||
<Stack.Col gap={4}>
|
<Stack.Col gap={4}>
|
||||||
{actionManager.renderAction("loadScene")}
|
{renderCanvasActions()}
|
||||||
{actionManager.renderAction("saveScene")}
|
|
||||||
{actionManager.renderAction("saveAsScene")}
|
|
||||||
{exportButton}
|
|
||||||
{actionManager.renderAction("clearCanvas")}
|
|
||||||
{onCollabButtonClick && (
|
|
||||||
<CollabButton
|
|
||||||
isCollaborating={isCollaborating}
|
|
||||||
collaboratorCount={appState.collaborators.size}
|
|
||||||
onClick={onCollabButtonClick}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<BackgroundPickerAndDarkModeToggle
|
|
||||||
actionManager={actionManager}
|
|
||||||
appState={appState}
|
|
||||||
setAppState={setAppState}
|
|
||||||
/>
|
|
||||||
{renderCustomFooter?.(true)}
|
{renderCustomFooter?.(true)}
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>{t("labels.collaborators")}</legend>
|
<legend>{t("labels.collaborators")}</legend>
|
||||||
<UserList mobile>
|
<UserList mobile>
|
||||||
{Array.from(appState.collaborators)
|
{Array.from(appState.collaborators)
|
||||||
// Collaborator is either not initialized or is actually the current user.
|
// Collaborator is either not initialized or is actually the current user.
|
||||||
.filter(([_, client]) => Object.keys(client).length !== 0)
|
.filter(
|
||||||
|
([_, client]) => Object.keys(client).length !== 0,
|
||||||
|
)
|
||||||
.map(([clientId, client]) => (
|
.map(([clientId, client]) => (
|
||||||
<React.Fragment key={clientId}>
|
<React.Fragment key={clientId}>
|
||||||
{actionManager.renderAction(
|
{actionManager.renderAction(
|
||||||
@ -123,6 +174,7 @@ export const MobileMenu = ({
|
|||||||
</div>
|
</div>
|
||||||
</Section>
|
</Section>
|
||||||
) : appState.openMenu === "shape" &&
|
) : appState.openMenu === "shape" &&
|
||||||
|
!viewModeEnabled &&
|
||||||
showSelectedShapeActions(appState, elements) ? (
|
showSelectedShapeActions(appState, elements) ? (
|
||||||
<Section className="App-mobile-menu" heading="selectedShapeActions">
|
<Section className="App-mobile-menu" heading="selectedShapeActions">
|
||||||
<SelectedShapeActions
|
<SelectedShapeActions
|
||||||
@ -134,16 +186,7 @@ export const MobileMenu = ({
|
|||||||
</Section>
|
</Section>
|
||||||
) : null}
|
) : null}
|
||||||
<footer className="App-toolbar">
|
<footer className="App-toolbar">
|
||||||
<div className="App-toolbar-content">
|
{renderAppToolbar()}
|
||||||
{actionManager.renderAction("toggleCanvasMenu")}
|
|
||||||
{actionManager.renderAction("toggleEditMenu")}
|
|
||||||
{actionManager.renderAction("undo")}
|
|
||||||
{actionManager.renderAction("redo")}
|
|
||||||
{actionManager.renderAction(
|
|
||||||
appState.multiElement ? "finalize" : "duplicateSelection",
|
|
||||||
)}
|
|
||||||
{actionManager.renderAction("deleteSelectedElements")}
|
|
||||||
</div>
|
|
||||||
{appState.scrolledOutside && !appState.openMenu && (
|
{appState.scrolledOutside && !appState.openMenu && (
|
||||||
<button
|
<button
|
||||||
className="scroll-back-to-content"
|
className="scroll-back-to-content"
|
||||||
@ -160,4 +203,5 @@ export const MobileMenu = ({
|
|||||||
</Island>
|
</Island>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
@ -492,6 +492,13 @@
|
|||||||
pointer-events: none !important;
|
pointer-events: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.excalidraw--view-mode {
|
||||||
|
.App-menu {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@media print {
|
@media print {
|
||||||
.App-bottom-bar,
|
.App-bottom-bar,
|
||||||
.FixedSideContainer,
|
.FixedSideContainer,
|
||||||
|
@ -7,7 +7,8 @@ export const showSelectedShapeActions = (
|
|||||||
elements: readonly NonDeletedExcalidrawElement[],
|
elements: readonly NonDeletedExcalidrawElement[],
|
||||||
) =>
|
) =>
|
||||||
Boolean(
|
Boolean(
|
||||||
appState.editingElement ||
|
!appState.viewModeEnabled &&
|
||||||
|
(appState.editingElement ||
|
||||||
getSelectedElements(elements, appState).length ||
|
getSelectedElements(elements, appState).length ||
|
||||||
appState.elementType !== "selection",
|
appState.elementType !== "selection"),
|
||||||
);
|
);
|
||||||
|
@ -21,6 +21,7 @@ export const CODES = {
|
|||||||
V: "KeyV",
|
V: "KeyV",
|
||||||
X: "KeyX",
|
X: "KeyX",
|
||||||
Z: "KeyZ",
|
Z: "KeyZ",
|
||||||
|
R: "KeyR",
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const KEYS = {
|
export const KEYS = {
|
||||||
|
@ -91,7 +91,8 @@
|
|||||||
"centerVertically": "Center vertically",
|
"centerVertically": "Center vertically",
|
||||||
"centerHorizontally": "Center horizontally",
|
"centerHorizontally": "Center horizontally",
|
||||||
"distributeHorizontally": "Distribute horizontally",
|
"distributeHorizontally": "Distribute horizontally",
|
||||||
"distributeVertically": "Distribute vertically"
|
"distributeVertically": "Distribute vertically",
|
||||||
|
"viewMode": "View mode"
|
||||||
},
|
},
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"clearReset": "Reset the canvas",
|
"clearReset": "Reset the canvas",
|
||||||
|
@ -16,12 +16,16 @@ Please add the latest change on the top under the correct section.
|
|||||||
|
|
||||||
## Excalidraw API
|
## Excalidraw API
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Add `viewModeEnabled` prop which enabled the view mode [#2840](https://github.com/excalidraw/excalidraw/pull/2840). When this prop is used, the view mode will not show up in context menu is so it is fully controlled by host.
|
||||||
- Expose `getAppState` on `excalidrawRef` [#2834](https://github.com/excalidraw/excalidraw/pull/2834).
|
- Expose `getAppState` on `excalidrawRef` [#2834](https://github.com/excalidraw/excalidraw/pull/2834).
|
||||||
|
|
||||||
## Excalidraw Library
|
## Excalidraw Library
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
|
- Add view mode [#2840](https://github.com/excalidraw/excalidraw/pull/2840).
|
||||||
- Remove `copy`, `cut`, and `paste` actions from contextmenu [#2872](https://github.com/excalidraw/excalidraw/pull/2872)
|
- Remove `copy`, `cut`, and `paste` actions from contextmenu [#2872](https://github.com/excalidraw/excalidraw/pull/2872)
|
||||||
- Support `Ctrl-Y` shortcut to redo on Windows [#2831](https://github.com/excalidraw/excalidraw/pull/2831).
|
- Support `Ctrl-Y` shortcut to redo on Windows [#2831](https://github.com/excalidraw/excalidraw/pull/2831).
|
||||||
|
|
||||||
|
@ -138,6 +138,7 @@ export default function App() {
|
|||||||
| [`onExportToBackend`](#onExportToBackend) | Function | | Callback triggered when link button is clicked on export dialog |
|
| [`onExportToBackend`](#onExportToBackend) | Function | | Callback triggered when link button is clicked on export dialog |
|
||||||
| [`langCode`](#langCode) | string | `en` | Language code string |
|
| [`langCode`](#langCode) | string | `en` | Language code string |
|
||||||
| [`renderFooter `](#renderFooter) | Function | | Function that renders custom UI footer |
|
| [`renderFooter `](#renderFooter) | Function | | Function that renders custom UI footer |
|
||||||
|
| [`viewModeEnabled`](#viewModeEnabled) | boolean | false | This implies if the app is in view mode. |
|
||||||
|
|
||||||
### `Extra API's`
|
### `Extra API's`
|
||||||
|
|
||||||
@ -330,3 +331,7 @@ import { defaultLang, languages } from "@excalidraw/excalidraw";
|
|||||||
#### `renderFooter`
|
#### `renderFooter`
|
||||||
|
|
||||||
A function that renders (returns JSX) custom UI footer. For example, you can use this to render a language picker that was previously being rendered by Excalidraw itself (for now, you'll need to implement your own language picker).
|
A function that renders (returns JSX) custom UI footer. For example, you can use this to render a language picker that was previously being rendered by Excalidraw itself (for now, you'll need to implement your own language picker).
|
||||||
|
|
||||||
|
#### `viewModeEnabled`
|
||||||
|
|
||||||
|
This prop indicates if the app is in `view mode`. When this prop is used, the `view mode` will not show up in context menu is so it is fully controlled by host. Also the value of this prop if passed will be used over the value of `intialData.appState.viewModeEnabled`
|
||||||
|
@ -26,6 +26,7 @@ const Excalidraw = (props: ExcalidrawProps) => {
|
|||||||
onExportToBackend,
|
onExportToBackend,
|
||||||
renderFooter,
|
renderFooter,
|
||||||
langCode = defaultLang.code,
|
langCode = defaultLang.code,
|
||||||
|
viewModeEnabled,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -64,6 +65,7 @@ const Excalidraw = (props: ExcalidrawProps) => {
|
|||||||
onExportToBackend={onExportToBackend}
|
onExportToBackend={onExportToBackend}
|
||||||
renderFooter={renderFooter}
|
renderFooter={renderFooter}
|
||||||
langCode={langCode}
|
langCode={langCode}
|
||||||
|
viewModeEnabled={viewModeEnabled}
|
||||||
/>
|
/>
|
||||||
</IsMobileProvider>
|
</IsMobileProvider>
|
||||||
</InitializeApp>
|
</InitializeApp>
|
||||||
@ -81,7 +83,6 @@ const areEqual = (
|
|||||||
|
|
||||||
const prevKeys = Object.keys(prevProps) as (keyof typeof prev)[];
|
const prevKeys = Object.keys(prevProps) as (keyof typeof prev)[];
|
||||||
const nextKeys = Object.keys(nextProps) as (keyof typeof next)[];
|
const nextKeys = Object.keys(nextProps) as (keyof typeof next)[];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
prevUser?.name === nextUser?.name &&
|
prevUser?.name === nextUser?.name &&
|
||||||
prevKeys.length === nextKeys.length &&
|
prevKeys.length === nextKeys.length &&
|
||||||
@ -89,6 +90,10 @@ const areEqual = (
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Excalidraw.defaultProps = {
|
||||||
|
lanCode: defaultLang.code,
|
||||||
|
};
|
||||||
|
|
||||||
const forwardedRefComp = forwardRef<
|
const forwardedRefComp = forwardRef<
|
||||||
ExcalidrawAPIRefValue,
|
ExcalidrawAPIRefValue,
|
||||||
PublicExcalidrawProps
|
PublicExcalidrawProps
|
||||||
|
@ -373,12 +373,14 @@ export const renderScene = (
|
|||||||
sceneState.zoom,
|
sceneState.zoom,
|
||||||
"mouse", // when we render we don't know which pointer type so use mouse
|
"mouse", // when we render we don't know which pointer type so use mouse
|
||||||
);
|
);
|
||||||
|
if (!appState.viewModeEnabled) {
|
||||||
renderTransformHandles(
|
renderTransformHandles(
|
||||||
context,
|
context,
|
||||||
sceneState,
|
sceneState,
|
||||||
transformHandles,
|
transformHandles,
|
||||||
locallySelectedElements[0].angle,
|
locallySelectedElements[0].angle,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
} else if (locallySelectedElements.length > 1 && !appState.isRotating) {
|
} else if (locallySelectedElements.length > 1 && !appState.isRotating) {
|
||||||
const dashedLinePadding = 4 / sceneState.zoom.value;
|
const dashedLinePadding = 4 / sceneState.zoom.value;
|
||||||
context.fillStyle = oc.white;
|
context.fillStyle = oc.white;
|
||||||
|
@ -76,6 +76,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -542,6 +543,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -990,6 +992,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -1766,6 +1769,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -1973,6 +1977,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -2424,6 +2429,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -2672,6 +2678,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -2837,6 +2844,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -3309,6 +3317,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -3618,6 +3627,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -3822,6 +3832,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -4062,6 +4073,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -4313,6 +4325,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -4714,6 +4727,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -4985,6 +4999,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -5310,6 +5325,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -5494,6 +5510,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -5656,6 +5673,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -6114,6 +6132,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -6423,6 +6442,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -8460,6 +8480,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -8821,6 +8842,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -9072,6 +9094,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -9324,6 +9347,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -9632,6 +9656,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -9794,6 +9819,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -9956,6 +9982,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -10118,6 +10145,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -10310,6 +10338,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -10502,6 +10531,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -10694,6 +10724,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -10886,6 +10917,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -11048,6 +11080,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -11210,6 +11243,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -11402,6 +11436,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -11564,6 +11599,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -11767,6 +11803,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -12474,6 +12511,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -12721,6 +12759,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -12819,6 +12858,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -12919,6 +12959,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -13081,6 +13122,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -13387,6 +13429,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -13693,6 +13736,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": "Copied styles.",
|
"toastMessage": "Copied styles.",
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -13853,6 +13897,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -14049,6 +14094,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -14302,6 +14348,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -14618,6 +14665,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": "Copied styles.",
|
"toastMessage": "Copied styles.",
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -15455,6 +15503,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -15761,6 +15810,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -16071,6 +16121,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -16447,6 +16498,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -16618,6 +16670,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -16931,6 +16984,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -17171,6 +17225,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -17426,6 +17481,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -17741,6 +17797,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -17841,6 +17898,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -18014,6 +18072,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -18820,6 +18879,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -18922,6 +18982,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -19698,6 +19759,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -20099,6 +20161,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -20346,6 +20409,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -20446,6 +20510,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -20940,6 +21005,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
@ -21038,6 +21104,7 @@ Object {
|
|||||||
"suggestedBindings": Array [],
|
"suggestedBindings": Array [],
|
||||||
"toastMessage": null,
|
"toastMessage": null,
|
||||||
"viewBackgroundColor": "#ffffff",
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"viewModeEnabled": false,
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
"zenModeEnabled": false,
|
"zenModeEnabled": false,
|
||||||
"zoom": Object {
|
"zoom": Object {
|
||||||
|
@ -623,6 +623,7 @@ describe("regression tests", () => {
|
|||||||
"selectAll",
|
"selectAll",
|
||||||
"gridMode",
|
"gridMode",
|
||||||
"zenMode",
|
"zenMode",
|
||||||
|
"viewMode",
|
||||||
"stats",
|
"stats",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -85,6 +85,7 @@ export type AppState = {
|
|||||||
zenModeEnabled: boolean;
|
zenModeEnabled: boolean;
|
||||||
appearance: "light" | "dark";
|
appearance: "light" | "dark";
|
||||||
gridSize: number | null;
|
gridSize: number | null;
|
||||||
|
viewModeEnabled: boolean;
|
||||||
|
|
||||||
/** top-most selected groups (i.e. does not include nested groups) */
|
/** top-most selected groups (i.e. does not include nested groups) */
|
||||||
selectedGroupIds: { [groupId: string]: boolean };
|
selectedGroupIds: { [groupId: string]: boolean };
|
||||||
@ -181,6 +182,7 @@ export interface ExcalidrawProps {
|
|||||||
) => void;
|
) => void;
|
||||||
renderFooter?: (isMobile: boolean) => JSX.Element;
|
renderFooter?: (isMobile: boolean) => JSX.Element;
|
||||||
langCode?: Language["code"];
|
langCode?: Language["code"];
|
||||||
|
viewModeEnabled?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SceneData = {
|
export type SceneData = {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user