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

@ -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;
}
}