From 80ff1562b873793684998bcfbe1bf56f467c645e Mon Sep 17 00:00:00 2001 From: Ryan Di Date: Mon, 7 Oct 2024 21:21:06 +0800 Subject: [PATCH] improve discoverability --- .../excalidraw/actions/actionCropEditor.tsx | 56 +++++++++++++++++++ packages/excalidraw/actions/index.ts | 2 + packages/excalidraw/actions/types.ts | 3 +- packages/excalidraw/components/Actions.tsx | 7 +++ .../CommandPalette/CommandPalette.tsx | 1 + packages/excalidraw/components/HelpDialog.tsx | 10 ++++ packages/excalidraw/components/HintViewer.tsx | 8 +++ packages/excalidraw/components/icons.tsx | 9 +++ packages/excalidraw/locales/en.json | 8 ++- 9 files changed, 101 insertions(+), 3 deletions(-) create mode 100644 packages/excalidraw/actions/actionCropEditor.tsx diff --git a/packages/excalidraw/actions/actionCropEditor.tsx b/packages/excalidraw/actions/actionCropEditor.tsx new file mode 100644 index 000000000..2757c851a --- /dev/null +++ b/packages/excalidraw/actions/actionCropEditor.tsx @@ -0,0 +1,56 @@ +import { register } from "./register"; +import { cropIcon } from "../components/icons"; +import { StoreAction } from "../store"; +import { ToolButton } from "../components/ToolButton"; +import { t } from "../i18n"; +import { isImageElement } from "../element/typeChecks"; +import { ExcalidrawImageElement } from "../element/types"; + +export const actionToggleCropEditor = register({ + name: "cropEditor", + label: "helpDialog.cropStart", + icon: cropIcon, + paletteName: "Finish image cropping", + viewMode: true, + trackEvent: { category: "menu" }, + keywords: ["image", "crop"], + perform(elements, appState, _, app) { + const selectedElement = app.scene.getSelectedElements({ + selectedElementIds: appState.selectedElementIds, + includeBoundTextElement: true, + })[0] as ExcalidrawImageElement; + + return { + appState: { + ...appState, + isCropping: false, + croppingElement: selectedElement, + }, + storeAction: StoreAction.CAPTURE, + }; + }, + predicate: (elements, appState, _, app) => { + const selectedElements = app.scene.getSelectedElements(appState); + if ( + !appState.croppingElement && + selectedElements.length === 1 && + isImageElement(selectedElements[0]) + ) { + return true; + } + return false; + }, + PanelComponent: ({ appState, updateData, app }) => { + const label = t("helpDialog.cropStart"); + + return ( + updateData(null)} + /> + ); + }, +}); diff --git a/packages/excalidraw/actions/index.ts b/packages/excalidraw/actions/index.ts index eff5de297..a556bfbea 100644 --- a/packages/excalidraw/actions/index.ts +++ b/packages/excalidraw/actions/index.ts @@ -88,3 +88,5 @@ export { actionToggleElementLock } from "./actionElementLock"; export { actionToggleLinearEditor } from "./actionLinearEditor"; export { actionToggleSearchMenu } from "./actionToggleSearchMenu"; + +export { actionToggleCropEditor } from "./actionCropEditor"; diff --git a/packages/excalidraw/actions/types.ts b/packages/excalidraw/actions/types.ts index 15364d21f..7f5229925 100644 --- a/packages/excalidraw/actions/types.ts +++ b/packages/excalidraw/actions/types.ts @@ -138,7 +138,8 @@ export type ActionName = | "commandPalette" | "autoResize" | "elementStats" - | "searchMenu"; + | "searchMenu" + | "cropEditor"; export type PanelComponentProps = { elements: readonly ExcalidrawElement[]; diff --git a/packages/excalidraw/components/Actions.tsx b/packages/excalidraw/components/Actions.tsx index b818d1a23..de813151c 100644 --- a/packages/excalidraw/components/Actions.tsx +++ b/packages/excalidraw/components/Actions.tsx @@ -26,6 +26,7 @@ import { trackEvent } from "../analytics"; import { hasBoundTextElement, isElbowArrow, + isImageElement, isLinearElement, isTextElement, } from "../element/typeChecks"; @@ -127,6 +128,11 @@ export const SelectedShapeActions = ({ isLinearElement(targetElements[0]) && !isElbowArrow(targetElements[0]); + const showCropEditorAction = + !appState.croppingElement && + targetElements.length === 1 && + isImageElement(targetElements[0]); + return (
@@ -245,6 +251,7 @@ export const SelectedShapeActions = ({ {renderAction("group")} {renderAction("ungroup")} {showLinkIcon && renderAction("hyperlink")} + {showCropEditorAction && renderAction("cropEditor")} {showLineEditorAction && renderAction("toggleLinearEditor")}
diff --git a/packages/excalidraw/components/CommandPalette/CommandPalette.tsx b/packages/excalidraw/components/CommandPalette/CommandPalette.tsx index e732acfb5..9333c8f65 100644 --- a/packages/excalidraw/components/CommandPalette/CommandPalette.tsx +++ b/packages/excalidraw/components/CommandPalette/CommandPalette.tsx @@ -279,6 +279,7 @@ function CommandPaletteInner({ actionManager.actions.increaseFontSize, actionManager.actions.decreaseFontSize, actionManager.actions.toggleLinearEditor, + actionManager.actions.cropEditor, actionLink, ].map((action: Action) => actionToCommand( diff --git a/packages/excalidraw/components/HelpDialog.tsx b/packages/excalidraw/components/HelpDialog.tsx index 886c7bb7a..577d8f187 100644 --- a/packages/excalidraw/components/HelpDialog.tsx +++ b/packages/excalidraw/components/HelpDialog.tsx @@ -222,6 +222,16 @@ export const HelpDialog = ({ onClose }: { onClose?: () => void }) => { ]} isOr={false} /> + + , tablerIconProps, ); + +export const cropIcon = createIcon( + + + + + , + modifiedTablerIconProps, +); diff --git a/packages/excalidraw/locales/en.json b/packages/excalidraw/locales/en.json index e4c5eea44..694029946 100644 --- a/packages/excalidraw/locales/en.json +++ b/packages/excalidraw/locales/en.json @@ -328,7 +328,9 @@ "deepBoxSelect": "Hold CtrlOrCmd to deep select, and to prevent dragging", "eraserRevert": "Hold Alt to revert the elements marked for deletion", "firefox_clipboard_write": "This feature can likely be enabled by setting the \"dom.events.asyncClipboard.clipboardItem\" flag to \"true\". To change the browser flags in Firefox, visit the \"about:config\" page.", - "disableSnapping": "Hold CtrlOrCmd to disable snapping" + "disableSnapping": "Hold CtrlOrCmd to disable snapping", + "enterCropEditor": "Double click the image or press ENTER to crop the image", + "leaveCropEditor": "Click outside the image or press ENTER or ESCAPE to finish cropping" }, "canvasError": { "cannotShowPreview": "Cannot show preview", @@ -399,7 +401,9 @@ "zoomToSelection": "Zoom to selection", "toggleElementLock": "Lock/unlock selection", "movePageUpDown": "Move page up/down", - "movePageLeftRight": "Move page left/right" + "movePageLeftRight": "Move page left/right", + "cropStart": "Crop image", + "cropFinish": "Finish image cropping" }, "clearCanvasDialog": { "title": "Clear canvas"