update mobile menu layout
This commit is contained in:
parent
62be53845e
commit
e6da5b213d
@ -10,7 +10,13 @@ export const hasBackground = (type: ElementOrToolType) =>
|
|||||||
type === "freedraw";
|
type === "freedraw";
|
||||||
|
|
||||||
export const hasStrokeColor = (type: ElementOrToolType) =>
|
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) =>
|
export const hasStrokeWidth = (type: ElementOrToolType) =>
|
||||||
type === "rectangle" ||
|
type === "rectangle" ||
|
||||||
|
|||||||
@ -78,6 +78,8 @@ import {
|
|||||||
DotsHorizontalIcon,
|
DotsHorizontalIcon,
|
||||||
} from "./icons";
|
} from "./icons";
|
||||||
|
|
||||||
|
import { Island } from "./Island";
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
AppClassProperties,
|
AppClassProperties,
|
||||||
AppProps,
|
AppProps,
|
||||||
@ -86,7 +88,6 @@ import type {
|
|||||||
AppState,
|
AppState,
|
||||||
} from "../types";
|
} from "../types";
|
||||||
import type { ActionManager } from "../actions/manager";
|
import type { ActionManager } from "../actions/manager";
|
||||||
import { Island } from "./Island";
|
|
||||||
|
|
||||||
// Common CSS class combinations
|
// Common CSS class combinations
|
||||||
const PROPERTIES_CLASSES = clsx([
|
const PROPERTIES_CLASSES = clsx([
|
||||||
|
|||||||
@ -2488,6 +2488,8 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
// but not too narrow (> MQ_MAX_WIDTH_MOBILE)
|
// but not too narrow (> MQ_MAX_WIDTH_MOBILE)
|
||||||
this.isTabletBreakpoint(editorWidth, editorHeight) && isMobileOrTablet()
|
this.isTabletBreakpoint(editorWidth, editorHeight) && isMobileOrTablet()
|
||||||
? "compact"
|
? "compact"
|
||||||
|
: this.isMobileBreakpoint(editorWidth, editorHeight)
|
||||||
|
? "mobile"
|
||||||
: "full",
|
: "full",
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -6489,6 +6491,10 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
this.setAppState({ snapLines: [] });
|
this.setAppState({ snapLines: [] });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.state.openPopup) {
|
||||||
|
this.setState({ openPopup: null });
|
||||||
|
}
|
||||||
|
|
||||||
this.updateGestureOnPointerDown(event);
|
this.updateGestureOnPointerDown(event);
|
||||||
|
|
||||||
// if dragging element is freedraw and another pointerdown event occurs
|
// if dragging element is freedraw and another pointerdown event occurs
|
||||||
|
|||||||
@ -582,13 +582,10 @@ const LayerUI = ({
|
|||||||
renderJSONExportDialog={renderJSONExportDialog}
|
renderJSONExportDialog={renderJSONExportDialog}
|
||||||
renderImageExportDialog={renderImageExportDialog}
|
renderImageExportDialog={renderImageExportDialog}
|
||||||
setAppState={setAppState}
|
setAppState={setAppState}
|
||||||
onLockToggle={onLockToggle}
|
|
||||||
onHandToolToggle={onHandToolToggle}
|
onHandToolToggle={onHandToolToggle}
|
||||||
onPenModeToggle={onPenModeToggle}
|
onPenModeToggle={onPenModeToggle}
|
||||||
renderTopRightUI={renderTopRightUI}
|
renderTopRightUI={renderTopRightUI}
|
||||||
renderCustomStats={renderCustomStats}
|
|
||||||
renderSidebars={renderSidebars}
|
renderSidebars={renderSidebars}
|
||||||
device={device}
|
|
||||||
renderWelcomeScreen={renderWelcomeScreen}
|
renderWelcomeScreen={renderWelcomeScreen}
|
||||||
UIOptions={UIOptions}
|
UIOptions={UIOptions}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -1,33 +1,23 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import { showSelectedShapeActions } from "@excalidraw/element";
|
|
||||||
|
|
||||||
import type { NonDeletedExcalidrawElement } from "@excalidraw/element/types";
|
import type { NonDeletedExcalidrawElement } from "@excalidraw/element/types";
|
||||||
|
|
||||||
import { isHandToolActive } from "../appState";
|
|
||||||
import { useTunnels } from "../context/tunnels";
|
import { useTunnels } from "../context/tunnels";
|
||||||
import { t } from "../i18n";
|
import { t } from "../i18n";
|
||||||
import { calculateScrollCenter } from "../scene";
|
import { calculateScrollCenter } from "../scene";
|
||||||
import { SCROLLBAR_WIDTH, SCROLLBAR_MARGIN } from "../scene/scrollbars";
|
import { SCROLLBAR_WIDTH, SCROLLBAR_MARGIN } from "../scene/scrollbars";
|
||||||
|
|
||||||
import { SelectedShapeActions, ShapesSwitcher } from "./Actions";
|
import { MobileShapeActions } from "./Actions";
|
||||||
import { MobileToolBar } from "./MobileToolBar";
|
import { MobileToolBar } from "./MobileToolBar";
|
||||||
import { FixedSideContainer } from "./FixedSideContainer";
|
import { FixedSideContainer } from "./FixedSideContainer";
|
||||||
import { HandButton } from "./HandButton";
|
|
||||||
import { HintViewer } from "./HintViewer";
|
|
||||||
import { Island } from "./Island";
|
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 { ActionManager } from "../actions/manager";
|
||||||
import type {
|
import type {
|
||||||
AppClassProperties,
|
AppClassProperties,
|
||||||
AppProps,
|
AppProps,
|
||||||
AppState,
|
AppState,
|
||||||
Device,
|
|
||||||
ExcalidrawProps,
|
|
||||||
UIAppState,
|
UIAppState,
|
||||||
} from "../types";
|
} from "../types";
|
||||||
import type { JSX } from "react";
|
import type { JSX } from "react";
|
||||||
@ -39,7 +29,6 @@ type MobileMenuProps = {
|
|||||||
renderImageExportDialog: () => React.ReactNode;
|
renderImageExportDialog: () => React.ReactNode;
|
||||||
setAppState: React.Component<any, AppState>["setState"];
|
setAppState: React.Component<any, AppState>["setState"];
|
||||||
elements: readonly NonDeletedExcalidrawElement[];
|
elements: readonly NonDeletedExcalidrawElement[];
|
||||||
onLockToggle: () => void;
|
|
||||||
onHandToolToggle: () => void;
|
onHandToolToggle: () => void;
|
||||||
onPenModeToggle: AppClassProperties["togglePenMode"];
|
onPenModeToggle: AppClassProperties["togglePenMode"];
|
||||||
|
|
||||||
@ -47,9 +36,7 @@ type MobileMenuProps = {
|
|||||||
isMobile: boolean,
|
isMobile: boolean,
|
||||||
appState: UIAppState,
|
appState: UIAppState,
|
||||||
) => JSX.Element | null;
|
) => JSX.Element | null;
|
||||||
renderCustomStats?: ExcalidrawProps["renderCustomStats"];
|
|
||||||
renderSidebars: () => JSX.Element | null;
|
renderSidebars: () => JSX.Element | null;
|
||||||
device: Device;
|
|
||||||
renderWelcomeScreen: boolean;
|
renderWelcomeScreen: boolean;
|
||||||
UIOptions: AppProps["UIOptions"];
|
UIOptions: AppProps["UIOptions"];
|
||||||
app: AppClassProperties;
|
app: AppClassProperties;
|
||||||
@ -60,14 +47,10 @@ export const MobileMenu = ({
|
|||||||
elements,
|
elements,
|
||||||
actionManager,
|
actionManager,
|
||||||
setAppState,
|
setAppState,
|
||||||
onLockToggle,
|
|
||||||
onHandToolToggle,
|
onHandToolToggle,
|
||||||
onPenModeToggle,
|
|
||||||
|
|
||||||
renderTopRightUI,
|
renderTopRightUI,
|
||||||
renderCustomStats,
|
|
||||||
renderSidebars,
|
renderSidebars,
|
||||||
device,
|
|
||||||
renderWelcomeScreen,
|
renderWelcomeScreen,
|
||||||
UIOptions,
|
UIOptions,
|
||||||
app,
|
app,
|
||||||
@ -79,20 +62,15 @@ export const MobileMenu = ({
|
|||||||
} = useTunnels();
|
} = useTunnels();
|
||||||
const renderToolbar = () => {
|
const renderToolbar = () => {
|
||||||
return (
|
return (
|
||||||
<div>
|
|
||||||
{/* {renderWelcomeScreen && <WelcomeScreenCenterTunnel.Out />} */}
|
|
||||||
<MobileToolBar
|
<MobileToolBar
|
||||||
appState={appState}
|
appState={appState}
|
||||||
app={app}
|
app={app}
|
||||||
actionManager={actionManager}
|
|
||||||
onHandToolToggle={onHandToolToggle}
|
onHandToolToggle={onHandToolToggle}
|
||||||
UIOptions={UIOptions}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderAppToolbar = () => {
|
const renderAppTopBar = () => {
|
||||||
if (
|
if (
|
||||||
appState.viewModeEnabled ||
|
appState.viewModeEnabled ||
|
||||||
appState.openDialog?.name === "elementLinkSelector"
|
appState.openDialog?.name === "elementLinkSelector"
|
||||||
@ -104,51 +82,54 @@ export const MobileMenu = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const topRightUI = renderTopRightUI?.(true, appState);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="App-toolbar-content">
|
<div
|
||||||
|
className="App-toolbar-content"
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "row",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
gap: 16,
|
||||||
|
}}
|
||||||
|
>
|
||||||
<MainMenuTunnel.Out />
|
<MainMenuTunnel.Out />
|
||||||
{renderTopRightUI?.(true, appState)}
|
</div>
|
||||||
|
{topRightUI ? topRightUI : <DefaultSidebarTriggerTunnel.Out />}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* {renderSidebars()} */}
|
{renderSidebars()}
|
||||||
|
|
||||||
<FixedSideContainer side="top" className="App-top-bar">
|
<FixedSideContainer side="top" className="App-top-bar">
|
||||||
{renderAppToolbar()}
|
{renderAppTopBar()}
|
||||||
<HintViewer
|
|
||||||
appState={appState}
|
|
||||||
isMobile={true}
|
|
||||||
device={device}
|
|
||||||
app={app}
|
|
||||||
/>
|
|
||||||
{renderWelcomeScreen && <WelcomeScreenCenterTunnel.Out />}
|
{renderWelcomeScreen && <WelcomeScreenCenterTunnel.Out />}
|
||||||
</FixedSideContainer>
|
</FixedSideContainer>
|
||||||
<div
|
<div
|
||||||
className="App-bottom-bar"
|
className="App-bottom-bar"
|
||||||
style={{
|
style={{
|
||||||
marginBottom: SCROLLBAR_WIDTH + SCROLLBAR_MARGIN * 2,
|
marginBottom: SCROLLBAR_WIDTH + SCROLLBAR_MARGIN,
|
||||||
marginLeft: SCROLLBAR_WIDTH + SCROLLBAR_MARGIN * 2,
|
|
||||||
marginRight: SCROLLBAR_WIDTH + SCROLLBAR_MARGIN * 2,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Island padding={0}>
|
<MobileShapeActions
|
||||||
{appState.openMenu === "shape" &&
|
|
||||||
!appState.viewModeEnabled &&
|
|
||||||
appState.openDialog?.name !== "elementLinkSelector" &&
|
|
||||||
showSelectedShapeActions(appState, elements) ? (
|
|
||||||
<Section className="App-mobile-menu" heading="selectedShapeActions">
|
|
||||||
<SelectedShapeActions
|
|
||||||
appState={appState}
|
appState={appState}
|
||||||
elementsMap={app.scene.getNonDeletedElementsMap()}
|
elementsMap={app.scene.getNonDeletedElementsMap()}
|
||||||
renderAction={actionManager.renderAction}
|
renderAction={actionManager.renderAction}
|
||||||
app={app}
|
app={app}
|
||||||
|
setAppState={setAppState}
|
||||||
/>
|
/>
|
||||||
</Section>
|
|
||||||
) : null}
|
<Island className="App-toolbar">
|
||||||
<footer className="App-toolbar">
|
|
||||||
{!appState.viewModeEnabled &&
|
{!appState.viewModeEnabled &&
|
||||||
appState.openDialog?.name !== "elementLinkSelector" &&
|
appState.openDialog?.name !== "elementLinkSelector" &&
|
||||||
renderToolbar()}
|
renderToolbar()}
|
||||||
@ -167,7 +148,6 @@ export const MobileMenu = ({
|
|||||||
{t("buttons.scrollBackToContent")}
|
{t("buttons.scrollBackToContent")}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</footer>
|
|
||||||
</Island>
|
</Island>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@ -3,6 +3,14 @@ import clsx from "clsx";
|
|||||||
|
|
||||||
import { KEYS, capitalizeString } from "@excalidraw/common";
|
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 { HandButton } from "./HandButton";
|
||||||
import { ToolButton } from "./ToolButton";
|
import { ToolButton } from "./ToolButton";
|
||||||
import DropdownMenu from "./dropdownMenu/DropdownMenu";
|
import DropdownMenu from "./dropdownMenu/DropdownMenu";
|
||||||
@ -28,16 +36,11 @@ import {
|
|||||||
MagicIcon,
|
MagicIcon,
|
||||||
} from "./icons";
|
} 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 "./ToolIcon.scss";
|
||||||
import "./MobileToolBar.scss";
|
import "./MobileToolBar.scss";
|
||||||
|
|
||||||
|
import type { AppClassProperties, UIAppState } from "../types";
|
||||||
|
|
||||||
const SHAPE_TOOLS = [
|
const SHAPE_TOOLS = [
|
||||||
{
|
{
|
||||||
type: "rectangle",
|
type: "rectangle",
|
||||||
|
|||||||
@ -1,11 +1,13 @@
|
|||||||
import React, { useEffect, useRef, useState } from "react";
|
import React, { useEffect, useRef, useState } from "react";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
|
|
||||||
|
import { capitalizeString, CLASSES } from "@excalidraw/common";
|
||||||
|
|
||||||
|
import { trackEvent } from "../analytics";
|
||||||
|
|
||||||
import { ToolButton } from "./ToolButton";
|
import { ToolButton } from "./ToolButton";
|
||||||
|
|
||||||
import type { AppClassProperties } from "../types";
|
import type { AppClassProperties } from "../types";
|
||||||
import { capitalizeString, CLASSES } from "@excalidraw/common";
|
|
||||||
import { trackEvent } from "../analytics";
|
|
||||||
|
|
||||||
type ToolOption = {
|
type ToolOption = {
|
||||||
type: string;
|
type: string;
|
||||||
|
|||||||
@ -239,16 +239,18 @@ body.excalidraw-cursor-resize * {
|
|||||||
|
|
||||||
.App-bottom-bar {
|
.App-bottom-bar {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
// account for margins
|
||||||
|
width: calc(100% - 28px);
|
||||||
|
max-width: 400px;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 0;
|
left: 50%;
|
||||||
right: 0;
|
transform: translateX(-50%);
|
||||||
--bar-padding: calc(4 * var(--space-factor));
|
--bar-padding: calc(4 * var(--space-factor));
|
||||||
z-index: 4;
|
z-index: 4;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-end;
|
flex-direction: column;
|
||||||
|
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
||||||
> .Island {
|
> .Island {
|
||||||
@ -262,7 +264,6 @@ body.excalidraw-cursor-resize * {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.App-toolbar {
|
.App-toolbar {
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
||||||
|
|||||||
@ -43,6 +43,10 @@
|
|||||||
--lg-icon-size: 1rem;
|
--lg-icon-size: 1rem;
|
||||||
--editor-container-padding: 1rem;
|
--editor-container-padding: 1rem;
|
||||||
|
|
||||||
|
@include isMobile {
|
||||||
|
--editor-container-padding: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
@media screen and (min-device-width: 1921px) {
|
@media screen and (min-device-width: 1921px) {
|
||||||
--lg-button-size: 2.5rem;
|
--lg-button-size: 2.5rem;
|
||||||
--lg-icon-size: 1.25rem;
|
--lg-icon-size: 1.25rem;
|
||||||
|
|||||||
@ -6101,7 +6101,7 @@ exports[`history > multiplayer undo/redo > should iterate through the history wh
|
|||||||
"offsetTop": 0,
|
"offsetTop": 0,
|
||||||
"openDialog": null,
|
"openDialog": null,
|
||||||
"openMenu": null,
|
"openMenu": null,
|
||||||
"openPopup": "elementBackground",
|
"openPopup": null,
|
||||||
"openSidebar": null,
|
"openSidebar": null,
|
||||||
"originSnapOffset": null,
|
"originSnapOffset": null,
|
||||||
"pasteDialog": {
|
"pasteDialog": {
|
||||||
@ -11961,7 +11961,7 @@ exports[`history > singleplayer undo/redo > should create entry when selecting f
|
|||||||
"offsetTop": 0,
|
"offsetTop": 0,
|
||||||
"openDialog": null,
|
"openDialog": null,
|
||||||
"openMenu": null,
|
"openMenu": null,
|
||||||
"openPopup": "elementStroke",
|
"openPopup": null,
|
||||||
"openSidebar": null,
|
"openSidebar": null,
|
||||||
"originSnapOffset": null,
|
"originSnapOffset": null,
|
||||||
"pasteDialog": {
|
"pasteDialog": {
|
||||||
@ -15533,7 +15533,7 @@ exports[`history > singleplayer undo/redo > should not override appstate changes
|
|||||||
"offsetTop": 0,
|
"offsetTop": 0,
|
||||||
"openDialog": null,
|
"openDialog": null,
|
||||||
"openMenu": null,
|
"openMenu": null,
|
||||||
"openPopup": "elementBackground",
|
"openPopup": null,
|
||||||
"openSidebar": null,
|
"openSidebar": null,
|
||||||
"originSnapOffset": null,
|
"originSnapOffset": null,
|
||||||
"pasteDialog": {
|
"pasteDialog": {
|
||||||
|
|||||||
@ -7384,7 +7384,7 @@ exports[`regression tests > given a selected element A and a not selected elemen
|
|||||||
"offsetTop": 0,
|
"offsetTop": 0,
|
||||||
"openDialog": null,
|
"openDialog": null,
|
||||||
"openMenu": null,
|
"openMenu": null,
|
||||||
"openPopup": "elementBackground",
|
"openPopup": null,
|
||||||
"openSidebar": null,
|
"openSidebar": null,
|
||||||
"originSnapOffset": null,
|
"originSnapOffset": null,
|
||||||
"pasteDialog": {
|
"pasteDialog": {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user