diff --git a/src/actions/actionDuplicateSelection.ts b/src/actions/actionDuplicateSelection.tsx similarity index 62% rename from src/actions/actionDuplicateSelection.ts rename to src/actions/actionDuplicateSelection.tsx index 639dcb05..61b4d206 100644 --- a/src/actions/actionDuplicateSelection.ts +++ b/src/actions/actionDuplicateSelection.tsx @@ -1,7 +1,13 @@ +import React from "react"; import { KEYS } from "../keys"; import { register } from "./register"; import { ExcalidrawElement } from "../element/types"; import { duplicateElement } from "../element"; +import { isSomeElementSelected } from "../scene"; +import { ToolButton } from "../components/ToolButton"; +import { clone } from "../components/icons"; +import { t } from "../i18n"; +import { getShortcutKey } from "../utils"; export const actionDuplicateSelection = register({ name: "duplicateSelection", @@ -28,4 +34,16 @@ export const actionDuplicateSelection = register({ }, contextItemLabel: "labels.duplicateSelection", keyTest: (event) => event[KEYS.CTRL_OR_CMD] && event.key === "d", + PanelComponent: ({ elements, appState, updateData }) => ( + updateData(null)} + visible={isSomeElementSelected(elements, appState)} + /> + ), }); diff --git a/src/components/Actions.tsx b/src/components/Actions.tsx index eae97c9d..54c87fc7 100644 --- a/src/components/Actions.tsx +++ b/src/components/Actions.tsx @@ -8,6 +8,7 @@ import { ToolButton } from "./ToolButton"; import { capitalizeString, getShortcutKey } from "../utils"; import { CURSOR_TYPE } from "../constants"; import Stack from "./Stack"; +import useIsMobile from "../is-mobile"; export function SelectedShapeActions({ targetElements, @@ -18,6 +19,8 @@ export function SelectedShapeActions({ renderAction: ActionManager["renderAction"]; elementType: ExcalidrawElement["type"]; }) { + const isMobile = useIsMobile(); + return (
{renderAction("changeStrokeColor")} @@ -59,12 +62,15 @@ export function SelectedShapeActions({ {renderAction("bringForward")}
-
- Layers Actions -
- {renderAction("deleteSelectedElements")} -
-
+ {!isMobile && ( +
+ {t("labels.actions")} +
+ {renderAction("duplicateSelection")} + {renderAction("deleteSelectedElements")} +
+
+ )} ); } diff --git a/src/components/MobileMenu.tsx b/src/components/MobileMenu.tsx index 03d95d89..fbd56f69 100644 --- a/src/components/MobileMenu.tsx +++ b/src/components/MobileMenu.tsx @@ -122,7 +122,9 @@ export function MobileMenu({ {actionManager.renderAction("toggleEditMenu")} {actionManager.renderAction("undo")} {actionManager.renderAction("redo")} - {actionManager.renderAction("finalize")} + {actionManager.renderAction( + appState.multiElement ? "finalize" : "duplicateSelection", + )} {actionManager.renderAction("deleteSelectedElements")} {appState.scrolledOutside && ( diff --git a/src/components/icons.tsx b/src/components/icons.tsx index 12bff754..3360ba3c 100644 --- a/src/components/icons.tsx +++ b/src/components/icons.tsx @@ -210,3 +210,9 @@ export const back = createIcon( 512, { marginLeft: "-0.2rem" }, ); + +export const clone = createIcon( + "M464 0c26.51 0 48 21.49 48 48v288c0 26.51-21.49 48-48 48H176c-26.51 0-48-21.49-48-48V48c0-26.51 21.49-48 48-48h288M176 416c-44.112 0-80-35.888-80-80V128H48c-26.51 0-48 21.49-48 48v288c0 26.51 21.49 48 48 48h288c26.51 0 48-21.49 48-48v-48H176z", + 512, + 512, +); diff --git a/src/locales/en.json b/src/locales/en.json index ffcbf4d9..5c905b6b 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -42,6 +42,7 @@ "canvasBackground": "Canvas background", "drawingCanvas": "Drawing Canvas", "layers": "Layers", + "actions": "Actions", "language": "Language", "createRoom": "Share a live-collaboration session", "duplicateSelection": "Duplicate selected elements" diff --git a/src/styles.scss b/src/styles.scss index ec9b38c9..581702e8 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -92,6 +92,15 @@ canvas { position: absolute; pointer-events: none; } + + .ToolIcon { + margin: 0 5px; + } + + .ToolIcon__icon { + width: 28px; + height: 28px; + } } fieldset {