From e6da5b213d7050b972d36a8ca7d7a35376343fe9 Mon Sep 17 00:00:00 2001 From: Ryan Di Date: Fri, 19 Sep 2025 14:03:52 +1000 Subject: [PATCH] update mobile menu layout --- packages/element/src/comparisons.ts | 8 +- packages/excalidraw/components/Actions.tsx | 3 +- packages/excalidraw/components/App.tsx | 6 + packages/excalidraw/components/LayerUI.tsx | 3 - packages/excalidraw/components/MobileMenu.tsx | 138 ++++++++---------- .../excalidraw/components/MobileToolBar.tsx | 17 ++- .../excalidraw/components/ToolWithPopup.tsx | 6 +- packages/excalidraw/css/styles.scss | 13 +- packages/excalidraw/css/theme.scss | 4 + .../tests/__snapshots__/history.test.tsx.snap | 6 +- .../regressionTests.test.tsx.snap | 2 +- 11 files changed, 103 insertions(+), 103 deletions(-) diff --git a/packages/element/src/comparisons.ts b/packages/element/src/comparisons.ts index 75fac889d..c15e1ca4b 100644 --- a/packages/element/src/comparisons.ts +++ b/packages/element/src/comparisons.ts @@ -10,7 +10,13 @@ export const hasBackground = (type: ElementOrToolType) => type === "freedraw"; export const hasStrokeColor = (type: ElementOrToolType) => - type !== "image" && type !== "frame" && type !== "magicframe"; + type === "rectangle" || + type === "ellipse" || + type === "diamond" || + type === "freedraw" || + type === "arrow" || + type === "line" || + type === "text"; export const hasStrokeWidth = (type: ElementOrToolType) => type === "rectangle" || diff --git a/packages/excalidraw/components/Actions.tsx b/packages/excalidraw/components/Actions.tsx index e3eaf6aee..c514b2255 100644 --- a/packages/excalidraw/components/Actions.tsx +++ b/packages/excalidraw/components/Actions.tsx @@ -78,6 +78,8 @@ import { DotsHorizontalIcon, } from "./icons"; +import { Island } from "./Island"; + import type { AppClassProperties, AppProps, @@ -86,7 +88,6 @@ import type { AppState, } from "../types"; import type { ActionManager } from "../actions/manager"; -import { Island } from "./Island"; // Common CSS class combinations const PROPERTIES_CLASSES = clsx([ diff --git a/packages/excalidraw/components/App.tsx b/packages/excalidraw/components/App.tsx index 3bbfdca6e..da873435e 100644 --- a/packages/excalidraw/components/App.tsx +++ b/packages/excalidraw/components/App.tsx @@ -2488,6 +2488,8 @@ class App extends React.Component { // but not too narrow (> MQ_MAX_WIDTH_MOBILE) this.isTabletBreakpoint(editorWidth, editorHeight) && isMobileOrTablet() ? "compact" + : this.isMobileBreakpoint(editorWidth, editorHeight) + ? "mobile" : "full", }); @@ -6489,6 +6491,10 @@ class App extends React.Component { this.setAppState({ snapLines: [] }); } + if (this.state.openPopup) { + this.setState({ openPopup: null }); + } + this.updateGestureOnPointerDown(event); // if dragging element is freedraw and another pointerdown event occurs diff --git a/packages/excalidraw/components/LayerUI.tsx b/packages/excalidraw/components/LayerUI.tsx index 674809532..9fcb18f19 100644 --- a/packages/excalidraw/components/LayerUI.tsx +++ b/packages/excalidraw/components/LayerUI.tsx @@ -582,13 +582,10 @@ const LayerUI = ({ renderJSONExportDialog={renderJSONExportDialog} renderImageExportDialog={renderImageExportDialog} setAppState={setAppState} - onLockToggle={onLockToggle} onHandToolToggle={onHandToolToggle} onPenModeToggle={onPenModeToggle} renderTopRightUI={renderTopRightUI} - renderCustomStats={renderCustomStats} renderSidebars={renderSidebars} - device={device} renderWelcomeScreen={renderWelcomeScreen} UIOptions={UIOptions} /> diff --git a/packages/excalidraw/components/MobileMenu.tsx b/packages/excalidraw/components/MobileMenu.tsx index b27a2ad67..7aa53bcc4 100644 --- a/packages/excalidraw/components/MobileMenu.tsx +++ b/packages/excalidraw/components/MobileMenu.tsx @@ -1,33 +1,23 @@ import React from "react"; -import { showSelectedShapeActions } from "@excalidraw/element"; - import type { NonDeletedExcalidrawElement } from "@excalidraw/element/types"; -import { isHandToolActive } from "../appState"; import { useTunnels } from "../context/tunnels"; import { t } from "../i18n"; import { calculateScrollCenter } from "../scene"; import { SCROLLBAR_WIDTH, SCROLLBAR_MARGIN } from "../scene/scrollbars"; -import { SelectedShapeActions, ShapesSwitcher } from "./Actions"; +import { MobileShapeActions } from "./Actions"; import { MobileToolBar } from "./MobileToolBar"; import { FixedSideContainer } from "./FixedSideContainer"; -import { HandButton } from "./HandButton"; -import { HintViewer } from "./HintViewer"; + import { Island } from "./Island"; -import { LockButton } from "./LockButton"; -import { PenModeButton } from "./PenModeButton"; -import { Section } from "./Section"; -import Stack from "./Stack"; import type { ActionManager } from "../actions/manager"; import type { AppClassProperties, AppProps, AppState, - Device, - ExcalidrawProps, UIAppState, } from "../types"; import type { JSX } from "react"; @@ -39,7 +29,6 @@ type MobileMenuProps = { renderImageExportDialog: () => React.ReactNode; setAppState: React.Component["setState"]; elements: readonly NonDeletedExcalidrawElement[]; - onLockToggle: () => void; onHandToolToggle: () => void; onPenModeToggle: AppClassProperties["togglePenMode"]; @@ -47,9 +36,7 @@ type MobileMenuProps = { isMobile: boolean, appState: UIAppState, ) => JSX.Element | null; - renderCustomStats?: ExcalidrawProps["renderCustomStats"]; renderSidebars: () => JSX.Element | null; - device: Device; renderWelcomeScreen: boolean; UIOptions: AppProps["UIOptions"]; app: AppClassProperties; @@ -60,14 +47,10 @@ export const MobileMenu = ({ elements, actionManager, setAppState, - onLockToggle, onHandToolToggle, - onPenModeToggle, renderTopRightUI, - renderCustomStats, renderSidebars, - device, renderWelcomeScreen, UIOptions, app, @@ -79,20 +62,15 @@ export const MobileMenu = ({ } = useTunnels(); const renderToolbar = () => { return ( -
- {/* {renderWelcomeScreen && } */} - -
+ ); }; - const renderAppToolbar = () => { + const renderAppTopBar = () => { if ( appState.viewModeEnabled || appState.openDialog?.name === "elementLinkSelector" @@ -104,70 +82,72 @@ export const MobileMenu = ({ ); } + const topRightUI = renderTopRightUI?.(true, appState); + return ( -
- - {renderTopRightUI?.(true, appState)} +
+
+ +
+ {topRightUI ? topRightUI : }
); }; return ( <> - {/* {renderSidebars()} */} - + {renderSidebars()} - {renderAppToolbar()} - + {renderAppTopBar()} {renderWelcomeScreen && }
- - {appState.openMenu === "shape" && - !appState.viewModeEnabled && - appState.openDialog?.name !== "elementLinkSelector" && - showSelectedShapeActions(appState, elements) ? ( -
- -
- ) : null} -
- {!appState.viewModeEnabled && - appState.openDialog?.name !== "elementLinkSelector" && - renderToolbar()} - {appState.scrolledOutside && - !appState.openMenu && - !appState.openSidebar && ( - - )} -
+ + + + {!appState.viewModeEnabled && + appState.openDialog?.name !== "elementLinkSelector" && + renderToolbar()} + {appState.scrolledOutside && + !appState.openMenu && + !appState.openSidebar && ( + + )}
diff --git a/packages/excalidraw/components/MobileToolBar.tsx b/packages/excalidraw/components/MobileToolBar.tsx index af3799e53..397220c66 100644 --- a/packages/excalidraw/components/MobileToolBar.tsx +++ b/packages/excalidraw/components/MobileToolBar.tsx @@ -3,6 +3,14 @@ import clsx from "clsx"; import { KEYS, capitalizeString } from "@excalidraw/common"; +import { trackEvent } from "../analytics"; + +import { t } from "../i18n"; + +import { isHandToolActive } from "../appState"; + +import { useTunnels } from "../context/tunnels"; + import { HandButton } from "./HandButton"; import { ToolButton } from "./ToolButton"; import DropdownMenu from "./dropdownMenu/DropdownMenu"; @@ -28,16 +36,11 @@ import { MagicIcon, } from "./icons"; -import { trackEvent } from "../analytics"; -import { t } from "../i18n"; -import { isHandToolActive } from "../appState"; -import { useTunnels } from "../context/tunnels"; - -import type { AppClassProperties, UIAppState } from "../types"; - import "./ToolIcon.scss"; import "./MobileToolBar.scss"; +import type { AppClassProperties, UIAppState } from "../types"; + const SHAPE_TOOLS = [ { type: "rectangle", diff --git a/packages/excalidraw/components/ToolWithPopup.tsx b/packages/excalidraw/components/ToolWithPopup.tsx index 2df305c3d..14a2efc92 100644 --- a/packages/excalidraw/components/ToolWithPopup.tsx +++ b/packages/excalidraw/components/ToolWithPopup.tsx @@ -1,11 +1,13 @@ import React, { useEffect, useRef, useState } from "react"; import clsx from "clsx"; +import { capitalizeString, CLASSES } from "@excalidraw/common"; + +import { trackEvent } from "../analytics"; + import { ToolButton } from "./ToolButton"; import type { AppClassProperties } from "../types"; -import { capitalizeString, CLASSES } from "@excalidraw/common"; -import { trackEvent } from "../analytics"; type ToolOption = { type: string; diff --git a/packages/excalidraw/css/styles.scss b/packages/excalidraw/css/styles.scss index 7b658bc03..b11daf0a6 100644 --- a/packages/excalidraw/css/styles.scss +++ b/packages/excalidraw/css/styles.scss @@ -239,16 +239,18 @@ body.excalidraw-cursor-resize * { .App-bottom-bar { position: absolute; - top: 0; + // account for margins + width: calc(100% - 28px); + max-width: 400px; bottom: 0; - left: 0; - right: 0; + left: 50%; + transform: translateX(-50%); --bar-padding: calc(4 * var(--space-factor)); z-index: 4; display: flex; - align-items: flex-end; + flex-direction: column; + pointer-events: none; - display: flex; justify-content: center; > .Island { @@ -262,7 +264,6 @@ body.excalidraw-cursor-resize * { } .App-toolbar { - width: 100%; display: flex; justify-content: center; diff --git a/packages/excalidraw/css/theme.scss b/packages/excalidraw/css/theme.scss index 1d6a56966..ee50900fe 100644 --- a/packages/excalidraw/css/theme.scss +++ b/packages/excalidraw/css/theme.scss @@ -43,6 +43,10 @@ --lg-icon-size: 1rem; --editor-container-padding: 1rem; + @include isMobile { + --editor-container-padding: 0.75rem; + } + @media screen and (min-device-width: 1921px) { --lg-button-size: 2.5rem; --lg-icon-size: 1.25rem; diff --git a/packages/excalidraw/tests/__snapshots__/history.test.tsx.snap b/packages/excalidraw/tests/__snapshots__/history.test.tsx.snap index e799a58b1..52c5398db 100644 --- a/packages/excalidraw/tests/__snapshots__/history.test.tsx.snap +++ b/packages/excalidraw/tests/__snapshots__/history.test.tsx.snap @@ -6101,7 +6101,7 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh "offsetTop": 0, "openDialog": null, "openMenu": null, - "openPopup": "elementBackground", + "openPopup": null, "openSidebar": null, "originSnapOffset": null, "pasteDialog": { @@ -11961,7 +11961,7 @@ exports[`history > singleplayer undo/redo > should create entry when selecting f "offsetTop": 0, "openDialog": null, "openMenu": null, - "openPopup": "elementStroke", + "openPopup": null, "openSidebar": null, "originSnapOffset": null, "pasteDialog": { @@ -15533,7 +15533,7 @@ exports[`history > singleplayer undo/redo > should not override appstate changes "offsetTop": 0, "openDialog": null, "openMenu": null, - "openPopup": "elementBackground", + "openPopup": null, "openSidebar": null, "originSnapOffset": null, "pasteDialog": { diff --git a/packages/excalidraw/tests/__snapshots__/regressionTests.test.tsx.snap b/packages/excalidraw/tests/__snapshots__/regressionTests.test.tsx.snap index 761fbc54d..c10e4a5e4 100644 --- a/packages/excalidraw/tests/__snapshots__/regressionTests.test.tsx.snap +++ b/packages/excalidraw/tests/__snapshots__/regressionTests.test.tsx.snap @@ -7384,7 +7384,7 @@ exports[`regression tests > given a selected element A and a not selected elemen "offsetTop": 0, "openDialog": null, "openMenu": null, - "openPopup": "elementBackground", + "openPopup": null, "openSidebar": null, "originSnapOffset": null, "pasteDialog": {