Add Zen mode for distraction free drawing (#1450)

This commit is contained in:
Aakansha Doshi 2020-04-25 18:43:02 +05:30 committed by GitHub
parent 71e7f130bc
commit 1866074c07
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 222 additions and 39 deletions

View File

@ -46,6 +46,7 @@ export function getDefaultAppState(): AppState {
collaborators: new Map(),
shouldCacheIgnoreZoom: false,
showShortcutsDialog: false,
zenModeEnabled: false,
};
}

View File

@ -192,6 +192,7 @@ class App extends React.Component<any, AppState> {
}
public render() {
const { zenModeEnabled } = this.state;
const canvasDOMWidth = window.innerWidth;
const canvasDOMHeight = window.innerHeight;
@ -217,6 +218,8 @@ class App extends React.Component<any, AppState> {
});
}}
onLockToggle={this.toggleLock}
zenModeEnabled={zenModeEnabled}
toggleZenMode={this.toggleZenMode}
/>
<main>
<canvas
@ -771,6 +774,12 @@ class App extends React.Component<any, AppState> {
}));
};
toggleZenMode = () => {
this.setState({
zenModeEnabled: !this.state.zenModeEnabled,
});
};
private destroySocketClient = () => {
this.setState({
isCollaborating: false,
@ -1072,6 +1081,14 @@ class App extends React.Component<any, AppState> {
});
}
if (
!event[KEYS.CTRL_OR_CMD] &&
event.altKey &&
event.keyCode === KEYS.Z_KEY_CODE
) {
this.toggleZenMode();
}
if (event.code === "KeyC" && event.altKey && event.shiftKey) {
this.copyToClipboardAsPng();
event.preventDefault();

View File

@ -15,6 +15,10 @@
z-index: 2;
}
.FixedSideContainer_side_top.zen-mode {
right: 42px;
}
/* TODO: if these are used, make sure to implement RTL support
.FixedSideContainer_side_left {
left: var(--margin);

View File

@ -5,14 +5,18 @@ import React from "react";
type FixedSideContainerProps = {
children: React.ReactNode;
side: "top" | "left" | "right";
className?: string;
};
export function FixedSideContainer({
children,
side,
className,
}: FixedSideContainerProps) {
return (
<div className={`FixedSideContainer FixedSideContainer_side_${side}`}>
<div
className={`FixedSideContainer FixedSideContainer_side_${side} ${className}`}
>
{children}
</div>
);

View File

@ -6,4 +6,9 @@
border-radius: var(--border-radius-m);
padding: calc(var(--padding) * var(--space-factor));
position: relative;
transition: box-shadow 0.5s ease-in-out;
&.zen-mode {
box-shadow: none;
}
}

View File

@ -1,11 +1,11 @@
import "./Island.css";
import "./Island.scss";
import React from "react";
type IslandProps = {
children: React.ReactNode;
padding?: number;
className?: string;
className?: string | boolean;
style?: object;
};

View File

@ -66,4 +66,48 @@
visibility: visible;
}
}
&__github-corner {
top: 0;
right: 0;
position: absolute;
width: 40px;
}
&__footer {
position: absolute;
bottom: 0px;
right: 0;
width: 190px;
}
.zen-mode-transition {
transition: transform 0.5s ease-in-out;
&.transition-left {
transform: translate(-999px, 0);
}
&.transition-right {
transform: translate(999px, 0px);
}
}
.disable-zen-mode {
height: 30px;
position: absolute;
bottom: 10px;
right: 15px;
font-size: 10px;
padding: 10px;
font-weight: 500;
opacity: 0;
visibility: hidden;
transition: visibility 0s linear 0s, opacity 0.5s;
&--visible {
opacity: 1;
visibility: visible;
transition: visibility 0s linear 300ms, opacity 0.5s;
transition-delay: 0.8s;
}
}
}

View File

@ -41,6 +41,8 @@ interface LayerUIProps {
onUsernameChange: (username: string) => void;
onRoomDestroy: () => void;
onLockToggle: () => void;
zenModeEnabled: boolean;
toggleZenMode: () => void;
}
const LayerUI = ({
@ -53,6 +55,8 @@ const LayerUI = ({
onUsernameChange,
onRoomDestroy,
onLockToggle,
zenModeEnabled,
toggleZenMode,
}: LayerUIProps) => {
const isMobile = useIsMobile();
@ -112,7 +116,10 @@ const LayerUI = ({
};
const renderCanvasActions = () => (
<Section heading="canvasActions">
<Section
heading="canvasActions"
className={`zen-mode-transition ${zenModeEnabled && "transition-left"}`}
>
{/* the zIndex ensures this menu has higher stacking order,
see https://github.com/excalidraw/excalidraw/pull/1445 */}
<Island padding={4} style={{ zIndex: 1 }}>
@ -138,7 +145,10 @@ const LayerUI = ({
);
const renderSelectedShapeActions = () => (
<Section heading="selectedShapeActions">
<Section
heading="selectedShapeActions"
className={`zen-mode-transition ${zenModeEnabled && "transition-left"}`}
>
<Island className={CLASSES.SHAPE_ACTIONS_MENU} padding={4}>
<SelectedShapeActions
appState={appState}
@ -167,7 +177,7 @@ const LayerUI = ({
{(heading) => (
<Stack.Col gap={4} align="start">
<Stack.Row gap={1}>
<Island padding={1}>
<Island padding={1} className={zenModeEnabled && "zen-mode"}>
{heading}
<Stack.Row gap={1}>
<ShapesSwitcher
@ -177,6 +187,7 @@ const LayerUI = ({
</Stack.Row>
</Island>
<LockIcon
zenModeEnabled={zenModeEnabled}
checked={appState.elementLocked}
onChange={onLockToggle}
title={t("toolBar.lock")}
@ -187,34 +198,54 @@ const LayerUI = ({
</Section>
<div />
</div>
<div className="App-menu App-menu_bottom">
<Stack.Col gap={2}>
<Section heading="canvasActions">
<Island padding={1}>
<ZoomActions
renderAction={actionManager.renderAction}
zoom={appState.zoom}
/>
</Island>
{renderEncryptedIcon()}
</Section>
</Stack.Col>
</div>
{
<div
className={`App-menu App-menu_bottom zen-mode-transition ${
zenModeEnabled && "transition-left"
}`}
>
<Stack.Col gap={2}>
<Section heading="canvasActions">
<Island padding={1}>
<ZoomActions
renderAction={actionManager.renderAction}
zoom={appState.zoom}
/>
</Island>
{renderEncryptedIcon()}
</Section>
</Stack.Col>
</div>
}
</FixedSideContainer>
);
};
const renderFooter = () => (
<footer role="contentinfo">
<LanguageList
onChange={(lng) => {
setLanguage(lng);
setAppState({});
}}
languages={languages}
floating
/>
{actionManager.renderAction("toggleShortcuts")}
<footer role="contentinfo" className="layer-ui__wrapper__footer">
<div
className={`zen-mode-transition ${
zenModeEnabled && "transition-right"
}`}
>
<LanguageList
onChange={(lng) => {
setLanguage(lng);
setAppState({});
}}
languages={languages}
floating
/>
{actionManager.renderAction("toggleShortcuts")}
</div>
<button
className={`disable-zen-mode ${
zenModeEnabled && "disable-zen-mode--visible"
}`}
onClick={toggleZenMode}
>
{t("buttons.exitZenMode")}
</button>
{appState.scrolledOutside && (
<button
className="scroll-back-to-content"
@ -255,9 +286,15 @@ const LayerUI = ({
/>
)}
{renderFixedSideContainer()}
<aside>
<GitHubCorner />
</aside>
{
<aside
className={`layer-ui__wrapper__github-corner zen-mode-transition ${
zenModeEnabled && "transition-right"
}`}
>
<GitHubCorner />
</aside>
}
{renderFooter()}
</div>
);

View File

@ -11,6 +11,7 @@ type LockIconProps = {
checked: boolean;
onChange?(): void;
size?: LockIconSize;
zenModeEnabled?: boolean;
};
const DEFAULT_SIZE: LockIconSize = "m";
@ -44,7 +45,9 @@ export function LockIcon(props: LockIconProps) {
return (
<label
className={`ToolIcon ToolIcon__lock ToolIcon_type_floating ${sizeCn}`}
className={`ToolIcon ToolIcon__lock ToolIcon_type_floating ${sizeCn} zen-mode-visibility ${
props.zenModeEnabled && "hidden"
}`}
title={`${props.title} — Q`}
>
<input

View File

@ -236,6 +236,10 @@ export const ShortcutsDialog = ({ onClose }: { onClose?: () => void }) => {
label={t("buttons.toggleFullScreen")}
shortcuts={["F"]}
/>
<Shortcut
label={t("buttons.toggleZenMode")}
shortcuts={["Alt+Z"]}
/>
</ShortcutIsland>
</Column>
<Column>

View File

@ -49,16 +49,16 @@ export const ToolButton = React.forwardRef(function (
<button
className={`ToolIcon_type_button ToolIcon ${sizeCn}${
props.selected ? " ToolIcon--selected" : ""
} ${props.className || ""}`}
} ${props.className || ""} ${
props.visible
? "ToolIcon_type_button--hide"
: "ToolIcon_type_button--show"
}`}
title={props.title}
aria-label={props["aria-label"]}
type="button"
onClick={props.onClick}
ref={innerRef}
style={{
visibility:
props.visible || props.visible == null ? "visible" : "hidden",
}}
>
<div className="ToolIcon__icon" aria-hidden="true">
{props.icon || props.label}

View File

@ -71,6 +71,13 @@
background-color: var(--button-gray-3);
}
}
&--show {
visibility: visible;
}
&--hide {
visibility: hidden;
}
}
.ToolIcon_type_radio,
@ -160,3 +167,15 @@
right: 2px;
}
}
.zen-mode-visibility {
visibility: visible;
opacity: 1;
transition: visibility 0s linear 0s, opacity 0.5s;
&.hidden {
visibility: hidden;
opacity: 0;
transition: visibility 0s linear 300ms, opacity 0.5s;
}
}

View File

@ -15,6 +15,7 @@ export const KEYS = {
QUESTION_MARK: "?",
F_KEY_CODE: 70,
ALT_KEY_CODE: 18,
Z_KEY_CODE: 90,
} as const;
export type Key = keyof typeof KEYS;

View File

@ -79,7 +79,9 @@
"redo": "Redo",
"roomDialog": "Start live collaboration",
"createNewRoom": "Create new room",
"toggleFullScreen": "Toggle full screen"
"toggleFullScreen": "Toggle full screen",
"toggleZenMode": "Toggle zen mode",
"exitZenMode": "Exit zen mode"
},
"alerts": {
"clearReset": "This will clear the whole canvas. Are you sure?",

View File

@ -42,6 +42,7 @@ Object {
"showShortcutsDialog": false,
"username": "",
"viewBackgroundColor": "#ffffff",
"zenModeEnabled": false,
"zoom": 1,
}
`;
@ -244,6 +245,7 @@ Object {
"showShortcutsDialog": false,
"username": "",
"viewBackgroundColor": "#ffffff",
"zenModeEnabled": false,
"zoom": 1,
}
`;
@ -364,6 +366,7 @@ Object {
"showShortcutsDialog": false,
"username": "",
"viewBackgroundColor": "#ffffff",
"zenModeEnabled": false,
"zoom": 1,
}
`;
@ -645,6 +648,7 @@ Object {
"showShortcutsDialog": false,
"username": "",
"viewBackgroundColor": "#ffffff",
"zenModeEnabled": false,
"zoom": 1,
}
`;
@ -808,6 +812,7 @@ Object {
"showShortcutsDialog": false,
"username": "",
"viewBackgroundColor": "#ffffff",
"zenModeEnabled": false,
"zoom": 1,
}
`;
@ -1012,6 +1017,7 @@ Object {
"showShortcutsDialog": false,
"username": "",
"viewBackgroundColor": "#ffffff",
"zenModeEnabled": false,
"zoom": 1,
}
`;
@ -1275,6 +1281,7 @@ Object {
"showShortcutsDialog": false,
"username": "",
"viewBackgroundColor": "#ffffff",
"zenModeEnabled": false,
"zoom": 1,
}
`;
@ -1654,6 +1661,7 @@ Object {
"showShortcutsDialog": false,
"username": "",
"viewBackgroundColor": "#ffffff",
"zenModeEnabled": false,
"zoom": 1,
}
`;
@ -2285,6 +2293,7 @@ Object {
"showShortcutsDialog": false,
"username": "",
"viewBackgroundColor": "#ffffff",
"zenModeEnabled": false,
"zoom": 1,
}
`;
@ -2405,6 +2414,7 @@ Object {
"showShortcutsDialog": false,
"username": "",
"viewBackgroundColor": "#ffffff",
"zenModeEnabled": false,
"zoom": 1,
}
`;
@ -2525,6 +2535,7 @@ Object {
"showShortcutsDialog": false,
"username": "",
"viewBackgroundColor": "#ffffff",
"zenModeEnabled": false,
"zoom": 1,
}
`;
@ -2645,6 +2656,7 @@ Object {
"showShortcutsDialog": false,
"username": "",
"viewBackgroundColor": "#ffffff",
"zenModeEnabled": false,
"zoom": 1,
}
`;
@ -2787,6 +2799,7 @@ Object {
"showShortcutsDialog": false,
"username": "",
"viewBackgroundColor": "#ffffff",
"zenModeEnabled": false,
"zoom": 1,
}
`;
@ -2929,6 +2942,7 @@ Object {
"showShortcutsDialog": false,
"username": "",
"viewBackgroundColor": "#ffffff",
"zenModeEnabled": false,
"zoom": 1,
}
`;
@ -3071,6 +3085,7 @@ Object {
"showShortcutsDialog": false,
"username": "",
"viewBackgroundColor": "#ffffff",
"zenModeEnabled": false,
"zoom": 1,
}
`;
@ -3191,6 +3206,7 @@ Object {
"showShortcutsDialog": false,
"username": "",
"viewBackgroundColor": "#ffffff",
"zenModeEnabled": false,
"zoom": 1,
}
`;
@ -3311,6 +3327,7 @@ Object {
"showShortcutsDialog": false,
"username": "",
"viewBackgroundColor": "#ffffff",
"zenModeEnabled": false,
"zoom": 1,
}
`;
@ -3453,6 +3470,7 @@ Object {
"showShortcutsDialog": false,
"username": "",
"viewBackgroundColor": "#ffffff",
"zenModeEnabled": false,
"zoom": 1,
}
`;
@ -3573,6 +3591,7 @@ Object {
"showShortcutsDialog": false,
"username": "",
"viewBackgroundColor": "#ffffff",
"zenModeEnabled": false,
"zoom": 1,
}
`;
@ -3646,6 +3665,7 @@ Object {
"showShortcutsDialog": false,
"username": "",
"viewBackgroundColor": "#ffffff",
"zenModeEnabled": false,
"zoom": 1,
}
`;
@ -4549,6 +4569,7 @@ Object {
"showShortcutsDialog": false,
"username": "",
"viewBackgroundColor": "#ffffff",
"zenModeEnabled": false,
"zoom": 1,
}
`;
@ -4982,6 +5003,7 @@ Object {
"showShortcutsDialog": false,
"username": "",
"viewBackgroundColor": "#ffffff",
"zenModeEnabled": false,
"zoom": 1,
}
`;
@ -5320,6 +5342,7 @@ Object {
"showShortcutsDialog": false,
"username": "",
"viewBackgroundColor": "#ffffff",
"zenModeEnabled": false,
"zoom": 1,
}
`;
@ -5567,6 +5590,7 @@ Object {
"showShortcutsDialog": false,
"username": "",
"viewBackgroundColor": "#ffffff",
"zenModeEnabled": false,
"zoom": 1,
}
`;
@ -5743,6 +5767,7 @@ Object {
"showShortcutsDialog": false,
"username": "",
"viewBackgroundColor": "#ffffff",
"zenModeEnabled": false,
"zoom": 1,
}
`;
@ -6596,6 +6621,7 @@ Object {
"showShortcutsDialog": false,
"username": "",
"viewBackgroundColor": "#ffffff",
"zenModeEnabled": false,
"zoom": 1,
}
`;
@ -7338,6 +7364,7 @@ Object {
"showShortcutsDialog": false,
"username": "",
"viewBackgroundColor": "#ffffff",
"zenModeEnabled": false,
"zoom": 1,
}
`;
@ -7973,6 +8000,7 @@ Object {
"showShortcutsDialog": false,
"username": "",
"viewBackgroundColor": "#ffffff",
"zenModeEnabled": false,
"zoom": 1,
}
`;
@ -8506,6 +8534,7 @@ Object {
"showShortcutsDialog": false,
"username": "",
"viewBackgroundColor": "#ffffff",
"zenModeEnabled": false,
"zoom": 1,
}
`;
@ -8988,6 +9017,7 @@ Object {
"showShortcutsDialog": false,
"username": "",
"viewBackgroundColor": "#ffffff",
"zenModeEnabled": false,
"zoom": 1,
}
`;
@ -9373,6 +9403,7 @@ Object {
"showShortcutsDialog": false,
"username": "",
"viewBackgroundColor": "#ffffff",
"zenModeEnabled": false,
"zoom": 1,
}
`;
@ -9665,6 +9696,7 @@ Object {
"showShortcutsDialog": false,
"username": "",
"viewBackgroundColor": "#ffffff",
"zenModeEnabled": false,
"zoom": 1,
}
`;
@ -9884,6 +9916,7 @@ Object {
"showShortcutsDialog": false,
"username": "",
"viewBackgroundColor": "#ffffff",
"zenModeEnabled": false,
"zoom": 1,
}
`;
@ -10794,6 +10827,7 @@ Object {
"showShortcutsDialog": false,
"username": "",
"viewBackgroundColor": "#ffffff",
"zenModeEnabled": false,
"zoom": 1,
}
`;
@ -11591,6 +11625,7 @@ Object {
"showShortcutsDialog": false,
"username": "",
"viewBackgroundColor": "#ffffff",
"zenModeEnabled": false,
"zoom": 1,
}
`;
@ -12279,6 +12314,7 @@ Object {
"showShortcutsDialog": false,
"username": "",
"viewBackgroundColor": "#ffffff",
"zenModeEnabled": false,
"zoom": 1,
}
`;
@ -12858,6 +12894,7 @@ Object {
"showShortcutsDialog": false,
"username": "",
"viewBackgroundColor": "#ffffff",
"zenModeEnabled": false,
"zoom": 1,
}
`;
@ -13242,6 +13279,7 @@ Object {
"showShortcutsDialog": false,
"username": "",
"viewBackgroundColor": "#ffffff",
"zenModeEnabled": false,
"zoom": 1,
}
`;
@ -13299,6 +13337,7 @@ Object {
"showShortcutsDialog": false,
"username": "",
"viewBackgroundColor": "#ffffff",
"zenModeEnabled": false,
"zoom": 1,
}
`;
@ -13356,6 +13395,7 @@ Object {
"showShortcutsDialog": false,
"username": "",
"viewBackgroundColor": "#ffffff",
"zenModeEnabled": false,
"zoom": 1,
}
`;
@ -13780,6 +13820,7 @@ Object {
"showShortcutsDialog": false,
"username": "",
"viewBackgroundColor": "#ffffff",
"zenModeEnabled": false,
"zoom": 1,
}
`;

View File

@ -64,6 +64,7 @@ export type AppState = {
>;
shouldCacheIgnoreZoom: boolean;
showShortcutsDialog: boolean;
zenModeEnabled: boolean;
};
export type PointerCoords = Readonly<{