mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
More mobile tweaks (#790)
* Disable text selection * Set content-editable=plaintext-only to disable Touch Bar formatting buttons * Enlarge resize handle tap targets for pen/touch * Make the lock button a button in mobile mode * Use icons instead of Unicode characters; add an alternate toolbar for creating multipoint lines * Allow buttons to hide themselves * Fix heuristic for showing shape actions * Refactor icons * Fix label for edit button * Switch edit button icon * Remove lock button on mobile * Add language selector on mobile * Fix showing edit button on mobile * Fix showing edit button on mobile, part 2 * Fix handle touch regions * Fix scroll-back button position * Allow using the text tool on a text object to start editing it * Fix deletion of last point in line
This commit is contained in:
parent
949c3841ea
commit
0fd3fb4b5b
17 changed files with 272 additions and 222 deletions
169
src/index.tsx
169
src/index.tsx
|
@ -109,6 +109,7 @@ import useIsMobile, { IsMobileProvider } from "./is-mobile";
|
|||
import { copyToAppClipboard, getClipboardContent } from "./clipboard";
|
||||
import { normalizeScroll } from "./scene/data";
|
||||
import { getCenter, getDistance } from "./gesture";
|
||||
import { menu, palette } from "./components/icons";
|
||||
|
||||
let { elements } = createScene();
|
||||
const { history } = createHistory();
|
||||
|
@ -286,9 +287,11 @@ const LayerUI = React.memo(
|
|||
);
|
||||
}
|
||||
|
||||
const showSelectedShapeActions =
|
||||
(appState.editingElement || getSelectedElements(elements).length) &&
|
||||
appState.elementType === "selection";
|
||||
const showSelectedShapeActions = Boolean(
|
||||
appState.editingElement ||
|
||||
getSelectedElements(elements).length ||
|
||||
appState.elementType !== "selection",
|
||||
);
|
||||
|
||||
function renderSelectedShapeActions() {
|
||||
const { elementType, editingElement } = appState;
|
||||
|
@ -386,21 +389,6 @@ const LayerUI = React.memo(
|
|||
);
|
||||
}
|
||||
|
||||
const lockButton = (
|
||||
<LockIcon
|
||||
checked={appState.elementLocked}
|
||||
onChange={() => {
|
||||
setAppState({
|
||||
elementLocked: !appState.elementLocked,
|
||||
elementType: appState.elementLocked
|
||||
? "selection"
|
||||
: appState.elementType,
|
||||
});
|
||||
}}
|
||||
title={t("toolBar.lock")}
|
||||
/>
|
||||
);
|
||||
|
||||
return isMobile ? (
|
||||
<>
|
||||
{appState.openedMenu === "canvas" ? (
|
||||
|
@ -411,13 +399,24 @@ const LayerUI = React.memo(
|
|||
<h2 className="visually-hidden" id="canvas-actions-title">
|
||||
{t("headings.canvasActions")}
|
||||
</h2>
|
||||
<div className="App-mobile-menu-scroller">
|
||||
<div className="App-mobile-menu-scroller panelColumn">
|
||||
<Stack.Col gap={4}>
|
||||
{actionManager.renderAction("loadScene")}
|
||||
{actionManager.renderAction("saveScene")}
|
||||
{renderExportDialog()}
|
||||
{actionManager.renderAction("clearCanvas")}
|
||||
{actionManager.renderAction("changeViewBackgroundColor")}
|
||||
<fieldset>
|
||||
<legend>{t("labels.language")}</legend>
|
||||
<LanguageList
|
||||
onChange={lng => {
|
||||
setLanguage(lng);
|
||||
setAppState({});
|
||||
}}
|
||||
languages={languages}
|
||||
currentLanguage={language}
|
||||
/>
|
||||
</fieldset>
|
||||
</Stack.Col>
|
||||
</div>
|
||||
</section>
|
||||
|
@ -456,61 +455,57 @@ const LayerUI = React.memo(
|
|||
</FixedSideContainer>
|
||||
<footer className="App-toolbar">
|
||||
<div className="App-toolbar-content">
|
||||
<ToolButton
|
||||
type="button"
|
||||
icon={
|
||||
<span style={{ fontSize: "2em", marginTop: "-0.15em" }}>☰</span>
|
||||
}
|
||||
aria-label={t("buttons.menu")}
|
||||
onClick={() =>
|
||||
setAppState(({ openedMenu }: any) => ({
|
||||
openedMenu: openedMenu === "canvas" ? null : "canvas",
|
||||
}))
|
||||
}
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
visibility: isSomeElementSelected(elements)
|
||||
? "visible"
|
||||
: "hidden",
|
||||
}}
|
||||
>
|
||||
{" "}
|
||||
{actionManager.renderAction("deleteSelectedElements")}
|
||||
</div>
|
||||
{lockButton}
|
||||
{actionManager.renderAction("finalize")}
|
||||
<div
|
||||
style={{
|
||||
visibility: isSomeElementSelected(elements)
|
||||
? "visible"
|
||||
: "hidden",
|
||||
}}
|
||||
>
|
||||
<ToolButton
|
||||
type="button"
|
||||
icon={
|
||||
<span style={{ fontSize: "2em", marginTop: "-0.15em" }}>
|
||||
✎
|
||||
</span>
|
||||
}
|
||||
aria-label={t("buttons.menu")}
|
||||
onClick={() =>
|
||||
setAppState(({ openedMenu }: any) => ({
|
||||
openedMenu: openedMenu === "shape" ? null : "shape",
|
||||
}))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
{appState.scrolledOutside && (
|
||||
<button
|
||||
className="scroll-back-to-content"
|
||||
onClick={() => {
|
||||
setAppState({ ...calculateScrollCenter(elements) });
|
||||
}}
|
||||
>
|
||||
{t("buttons.scrollBackToContent")}
|
||||
</button>
|
||||
{appState.multiElement ? (
|
||||
<>
|
||||
{actionManager.renderAction("deleteSelectedElements")}
|
||||
<ToolButton
|
||||
visible={showSelectedShapeActions}
|
||||
type="button"
|
||||
icon={palette}
|
||||
aria-label={t("buttons.edit")}
|
||||
onClick={() =>
|
||||
setAppState(({ openedMenu }: any) => ({
|
||||
openedMenu: openedMenu === "shape" ? null : "shape",
|
||||
}))
|
||||
}
|
||||
/>
|
||||
{actionManager.renderAction("finalize")}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<ToolButton
|
||||
type="button"
|
||||
icon={menu}
|
||||
aria-label={t("buttons.menu")}
|
||||
onClick={() =>
|
||||
setAppState(({ openedMenu }: any) => ({
|
||||
openedMenu: openedMenu === "canvas" ? null : "canvas",
|
||||
}))
|
||||
}
|
||||
/>
|
||||
<ToolButton
|
||||
visible={showSelectedShapeActions}
|
||||
type="button"
|
||||
icon={palette}
|
||||
aria-label={t("buttons.edit")}
|
||||
onClick={() =>
|
||||
setAppState(({ openedMenu }: any) => ({
|
||||
openedMenu: openedMenu === "shape" ? null : "shape",
|
||||
}))
|
||||
}
|
||||
/>
|
||||
{actionManager.renderAction("deleteSelectedElements")}
|
||||
{appState.scrolledOutside && (
|
||||
<button
|
||||
className="scroll-back-to-content"
|
||||
onClick={() => {
|
||||
setAppState({ ...calculateScrollCenter(elements) });
|
||||
}}
|
||||
>
|
||||
{t("buttons.scrollBackToContent")}
|
||||
</button>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</footer>
|
||||
|
@ -545,7 +540,7 @@ const LayerUI = React.memo(
|
|||
</Stack.Col>
|
||||
</Island>
|
||||
</section>
|
||||
{showSelectedShapeActions ? (
|
||||
{showSelectedShapeActions && (
|
||||
<section
|
||||
className="App-right-menu"
|
||||
aria-labelledby="selected-shape-title"
|
||||
|
@ -555,7 +550,7 @@ const LayerUI = React.memo(
|
|||
</h2>
|
||||
<Island padding={4}>{renderSelectedShapeActions()}</Island>
|
||||
</section>
|
||||
) : null}
|
||||
)}
|
||||
</Stack.Col>
|
||||
<section aria-labelledby="shapes-title">
|
||||
<Stack.Col gap={4} align="start">
|
||||
|
@ -566,7 +561,19 @@ const LayerUI = React.memo(
|
|||
</h2>
|
||||
<Stack.Row gap={1}>{renderShapesSwitcher()}</Stack.Row>
|
||||
</Island>
|
||||
{lockButton}
|
||||
<LockIcon
|
||||
checked={appState.elementLocked}
|
||||
onChange={() => {
|
||||
setAppState({
|
||||
elementLocked: !appState.elementLocked,
|
||||
elementType: appState.elementLocked
|
||||
? "selection"
|
||||
: appState.elementType,
|
||||
});
|
||||
}}
|
||||
title={t("toolBar.lock")}
|
||||
isButton={isMobile}
|
||||
/>
|
||||
</Stack.Row>
|
||||
</Stack.Col>
|
||||
</section>
|
||||
|
@ -591,6 +598,7 @@ const LayerUI = React.memo(
|
|||
}}
|
||||
languages={languages}
|
||||
currentLanguage={language}
|
||||
floating
|
||||
/>
|
||||
{appState.scrolledOutside && (
|
||||
<button
|
||||
|
@ -1085,6 +1093,8 @@ export class App extends React.Component<any, AppState> {
|
|||
return;
|
||||
}
|
||||
|
||||
this.setState({ lastPointerDownWith: e.pointerType });
|
||||
|
||||
// pan canvas on wheel button drag or space+drag
|
||||
if (
|
||||
gesture.pointers.length === 0 &&
|
||||
|
@ -1213,6 +1223,7 @@ export class App extends React.Component<any, AppState> {
|
|||
elements,
|
||||
{ x, y },
|
||||
this.state.zoom,
|
||||
e.pointerType,
|
||||
);
|
||||
|
||||
const selectedElements = getSelectedElements(elements);
|
||||
|
@ -1279,6 +1290,9 @@ export class App extends React.Component<any, AppState> {
|
|||
if (this.state.editingElement?.type === "text") {
|
||||
return;
|
||||
}
|
||||
if (elementIsAddedToSelection) {
|
||||
element = hitElement!;
|
||||
}
|
||||
let textX = e.clientX;
|
||||
let textY = e.clientY;
|
||||
if (!e.altKey) {
|
||||
|
@ -2152,6 +2166,7 @@ export class App extends React.Component<any, AppState> {
|
|||
elements,
|
||||
{ x, y },
|
||||
this.state.zoom,
|
||||
e.pointerType,
|
||||
);
|
||||
if (resizeElement && resizeElement.resizeHandle) {
|
||||
document.documentElement.style.cursor = getCursorForResizingElement(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue