From 3d617958cc194b0e994f65c04bd81427fabc7345 Mon Sep 17 00:00:00 2001 From: David Luzar <luzar.david@gmail.com> Date: Tue, 19 Sep 2023 16:01:40 +0200 Subject: [PATCH] fix: improperly disabling UI pointer-events on canvas interaction (#7005) Co-authored-by: Aakansha Doshi <aakansha1216@gmail.com> --- src/components/App.tsx | 21 +++++++++++++++---- src/components/FixedSideContainer.scss | 2 +- src/components/LayerUI.scss | 10 ++++++--- src/components/LayerUI.tsx | 10 ++------- src/components/Sidebar/Sidebar.scss | 2 ++ src/components/Stats.scss | 2 +- src/components/UserList.scss | 2 +- src/components/footer/Footer.tsx | 2 +- src/components/footer/FooterCenter.scss | 3 ++- .../welcome-screen/WelcomeScreen.scss | 6 +++--- src/constants.ts | 8 +++++++ src/css/styles.scss | 14 ++++++------- .../components/AppWelcomeScreen.tsx | 3 ++- 13 files changed, 54 insertions(+), 31 deletions(-) diff --git a/src/components/App.tsx b/src/components/App.tsx index 373ff7b66f..367c8f7f67 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -85,6 +85,7 @@ import { VERTICAL_ALIGN, YOUTUBE_STATES, ZOOM_STEP, + POINTER_EVENTS, } from "../constants"; import { exportCanvas, loadFromBlob } from "../data"; import Library, { distributeLibraryItemsOnSquareGrid } from "../data/library"; @@ -857,7 +858,9 @@ class App extends React.Component<AppProps, AppState> { width: isVisible ? `${el.width}px` : 0, height: isVisible ? `${el.height}px` : 0, transform: isVisible ? `rotate(${el.angle}rad)` : "none", - pointerEvents: isActive ? "auto" : "none", + pointerEvents: isActive + ? POINTER_EVENTS.enabled + : POINTER_EVENTS.disabled, }} > {isHovered && ( @@ -1081,9 +1084,9 @@ class App extends React.Component<AppProps, AppState> { whiteSpace: "nowrap", textOverflow: "ellipsis", cursor: CURSOR_TYPE.MOVE, - // disable all interaction (e.g. cursor change) when in view - // mode - pointerEvents: this.state.viewModeEnabled ? "none" : "all", + pointerEvents: this.state.viewModeEnabled + ? POINTER_EVENTS.disabled + : POINTER_EVENTS.inheritFromUI, }} onPointerDown={(event) => this.handleCanvasPointerDown(event)} onWheel={(event) => this.handleWheel(event)} @@ -1125,6 +1128,16 @@ class App extends React.Component<AppProps, AppState> { "excalidraw--view-mode": this.state.viewModeEnabled, "excalidraw--mobile": this.device.isMobile, })} + style={{ + ["--ui-pointerEvents" as any]: + this.state.selectionElement || + this.state.draggingElement || + this.state.resizingElement || + (this.state.editingElement && + !isTextElement(this.state.editingElement)) + ? POINTER_EVENTS.disabled + : POINTER_EVENTS.enabled, + }} ref={this.excalidrawContainerRef} onDrop={this.handleAppOnDrop} tabIndex={0} diff --git a/src/components/FixedSideContainer.scss b/src/components/FixedSideContainer.scss index a485d6d252..62d77d5056 100644 --- a/src/components/FixedSideContainer.scss +++ b/src/components/FixedSideContainer.scss @@ -7,7 +7,7 @@ } .FixedSideContainer > * { - pointer-events: all; + pointer-events: var(--ui-pointerEvents); } .FixedSideContainer_side_top { diff --git a/src/components/LayerUI.scss b/src/components/LayerUI.scss index 73e3922945..8898b0f83d 100644 --- a/src/components/LayerUI.scss +++ b/src/components/LayerUI.scss @@ -91,13 +91,17 @@ visibility: visible; transition: visibility 0s linear 300ms, opacity 0.5s; transition-delay: 0.8s; + + pointer-events: var(--ui-pointerEvents); } } .layer-ui__wrapper__footer-left, - .layer-ui__wrapper__footer-right, - .disable-zen-mode--visible { - pointer-events: all; + .footer-center, + .layer-ui__wrapper__footer-right { + & > * { + pointer-events: var(--ui-pointerEvents); + } } .layer-ui__wrapper__footer-right { diff --git a/src/components/LayerUI.tsx b/src/components/LayerUI.tsx index 26be77aefa..02f360e255 100644 --- a/src/components/LayerUI.tsx +++ b/src/components/LayerUI.tsx @@ -2,7 +2,7 @@ import clsx from "clsx"; import React from "react"; import { ActionManager } from "../actions/manager"; import { CLASSES, DEFAULT_SIDEBAR, LIBRARY_SIDEBAR_WIDTH } from "../constants"; -import { isTextElement, showSelectedShapeActions } from "../element"; +import { showSelectedShapeActions } from "../element"; import { NonDeletedExcalidrawElement } from "../element/types"; import { Language, t } from "../i18n"; import { calculateScrollCenter } from "../scene"; @@ -427,13 +427,7 @@ const LayerUI = ({ {!device.isMobile && ( <> <div - className={clsx("layer-ui__wrapper", { - "disable-pointerEvents": - appState.draggingElement || - appState.resizingElement || - (appState.editingElement && - !isTextElement(appState.editingElement)), - })} + className="layer-ui__wrapper" style={ appState.openSidebar && isSidebarDocked && diff --git a/src/components/Sidebar/Sidebar.scss b/src/components/Sidebar/Sidebar.scss index 200e94ef46..abb6f00959 100644 --- a/src/components/Sidebar/Sidebar.scss +++ b/src/components/Sidebar/Sidebar.scss @@ -17,6 +17,8 @@ background-color: var(--sidebar-bg-color); box-shadow: var(--sidebar-shadow); + pointer-events: var(--ui-pointerEvents); + :root[dir="rtl"] & { left: 0; right: auto; diff --git a/src/components/Stats.scss b/src/components/Stats.scss index 0a2f6b62dc..187a47a418 100644 --- a/src/components/Stats.scss +++ b/src/components/Stats.scss @@ -7,7 +7,7 @@ right: 12px; font-size: 12px; z-index: 10; - pointer-events: all; + pointer-events: var(--ui-pointerEvents); h3 { margin: 0 24px 8px 0; diff --git a/src/components/UserList.scss b/src/components/UserList.scss index e224673c08..bafa7ad3ae 100644 --- a/src/components/UserList.scss +++ b/src/components/UserList.scss @@ -26,7 +26,7 @@ } .UserList > * { - pointer-events: all; + pointer-events: var(--ui-pointerEvents); } .UserList_mobile { diff --git a/src/components/footer/Footer.tsx b/src/components/footer/Footer.tsx index 173ea451ad..85844aad6d 100644 --- a/src/components/footer/Footer.tsx +++ b/src/components/footer/Footer.tsx @@ -73,7 +73,7 @@ const Footer = ({ <FooterCenterTunnel.Out /> <div className={clsx("layer-ui__wrapper__footer-right zen-mode-transition", { - "transition-right disable-pointerEvents": appState.zenModeEnabled, + "transition-right": appState.zenModeEnabled, })} > <div style={{ position: "relative" }}> diff --git a/src/components/footer/FooterCenter.scss b/src/components/footer/FooterCenter.scss index 1e17db6c56..ce65659227 100644 --- a/src/components/footer/FooterCenter.scss +++ b/src/components/footer/FooterCenter.scss @@ -1,10 +1,11 @@ .footer-center { pointer-events: none; & > * { - pointer-events: all; + pointer-events: var(--ui-pointerEvents); } display: flex; width: 100%; justify-content: flex-start; + margin-inline-end: 0.6rem; } diff --git a/src/components/welcome-screen/WelcomeScreen.scss b/src/components/welcome-screen/WelcomeScreen.scss index a1b88d8bde..8f1a09bf31 100644 --- a/src/components/welcome-screen/WelcomeScreen.scss +++ b/src/components/welcome-screen/WelcomeScreen.scss @@ -161,7 +161,7 @@ .welcome-screen-menu-item { box-sizing: border-box; - pointer-events: all; + pointer-events: var(--ui-pointerEvents); color: var(--color-gray-50); font-size: 0.875rem; @@ -202,7 +202,7 @@ } } - &:not(:active) .welcome-screen-menu-item:hover { + .welcome-screen-menu-item:hover { text-decoration: none; background: var(--color-gray-10); @@ -246,7 +246,7 @@ } } - &:not(:active) .welcome-screen-menu-item:hover { + .welcome-screen-menu-item:hover { background: var(--color-gray-85); .welcome-screen-menu-item__shortcut { diff --git a/src/constants.ts b/src/constants.ts index 46ae25b401..90fc667581 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -41,6 +41,14 @@ export const POINTER_BUTTON = { TOUCH: -1, } as const; +export const POINTER_EVENTS = { + enabled: "all", + disabled: "none", + // asserted as any so it can be freely assigned to React Element + // "pointerEnvets" CSS prop + inheritFromUI: "var(--ui-pointerEvents)" as any, +} as const; + export enum EVENT { COPY = "copy", PASTE = "paste", diff --git a/src/css/styles.scss b/src/css/styles.scss index bb90633201..4ca58af4f7 100644 --- a/src/css/styles.scss +++ b/src/css/styles.scss @@ -253,7 +253,7 @@ max-height: 100%; display: flex; flex-direction: column; - pointer-events: initial; + pointer-events: var(--ui-pointerEvents); .panelColumn { padding: 8px 8px 0 8px; @@ -301,7 +301,7 @@ pointer-events: none !important; & > * { - pointer-events: all; + pointer-events: var(--ui-pointerEvents); } } @@ -312,16 +312,16 @@ cursor: default; pointer-events: none !important; + & > * { + pointer-events: var(--ui-pointerEvents); + } + @media (min-width: 1536px) { grid-template-columns: 1fr 1fr 1fr; grid-gap: 3rem; } } - .layer-ui__wrapper:not(.disable-pointerEvents) .App-menu_top > * { - pointer-events: all; - } - .App-menu_top > *:first-child { justify-self: flex-start; } @@ -436,7 +436,7 @@ left: 50%; bottom: 30px; transform: translateX(-50%); - pointer-events: all; + pointer-events: var(--ui-pointerEvents); font-family: inherit; &:hover { diff --git a/src/excalidraw-app/components/AppWelcomeScreen.tsx b/src/excalidraw-app/components/AppWelcomeScreen.tsx index 699c3ba88d..72b688d9bd 100644 --- a/src/excalidraw-app/components/AppWelcomeScreen.tsx +++ b/src/excalidraw-app/components/AppWelcomeScreen.tsx @@ -3,6 +3,7 @@ import { PlusPromoIcon } from "../../components/icons"; import { useI18n } from "../../i18n"; import { WelcomeScreen } from "../../packages/excalidraw/index"; import { isExcalidrawPlusSignedUser } from "../app_constants"; +import { POINTER_EVENTS } from "../../constants"; export const AppWelcomeScreen: React.FC<{ setCollabDialogShown: (toggle: boolean) => any; @@ -18,7 +19,7 @@ export const AppWelcomeScreen: React.FC<{ if (bit === "Excalidraw+") { return ( <a - style={{ pointerEvents: "all" }} + style={{ pointerEvents: POINTER_EVENTS.inheritFromUI }} href={`${ import.meta.env.VITE_APP_PLUS_APP }?utm_source=excalidraw&utm_medium=app&utm_content=welcomeScreenSignedInUser`}