improve discoverability

pull/8613/head
Ryan Di 5 months ago
parent 3f00762a77
commit 80ff1562b8

@ -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 (
<ToolButton
type="button"
icon={cropIcon}
title={label}
aria-label={label}
onClick={() => updateData(null)}
/>
);
},
});

@ -88,3 +88,5 @@ export { actionToggleElementLock } from "./actionElementLock";
export { actionToggleLinearEditor } from "./actionLinearEditor"; export { actionToggleLinearEditor } from "./actionLinearEditor";
export { actionToggleSearchMenu } from "./actionToggleSearchMenu"; export { actionToggleSearchMenu } from "./actionToggleSearchMenu";
export { actionToggleCropEditor } from "./actionCropEditor";

@ -138,7 +138,8 @@ export type ActionName =
| "commandPalette" | "commandPalette"
| "autoResize" | "autoResize"
| "elementStats" | "elementStats"
| "searchMenu"; | "searchMenu"
| "cropEditor";
export type PanelComponentProps = { export type PanelComponentProps = {
elements: readonly ExcalidrawElement[]; elements: readonly ExcalidrawElement[];

@ -26,6 +26,7 @@ import { trackEvent } from "../analytics";
import { import {
hasBoundTextElement, hasBoundTextElement,
isElbowArrow, isElbowArrow,
isImageElement,
isLinearElement, isLinearElement,
isTextElement, isTextElement,
} from "../element/typeChecks"; } from "../element/typeChecks";
@ -127,6 +128,11 @@ export const SelectedShapeActions = ({
isLinearElement(targetElements[0]) && isLinearElement(targetElements[0]) &&
!isElbowArrow(targetElements[0]); !isElbowArrow(targetElements[0]);
const showCropEditorAction =
!appState.croppingElement &&
targetElements.length === 1 &&
isImageElement(targetElements[0]);
return ( return (
<div className="panelColumn"> <div className="panelColumn">
<div> <div>
@ -245,6 +251,7 @@ export const SelectedShapeActions = ({
{renderAction("group")} {renderAction("group")}
{renderAction("ungroup")} {renderAction("ungroup")}
{showLinkIcon && renderAction("hyperlink")} {showLinkIcon && renderAction("hyperlink")}
{showCropEditorAction && renderAction("cropEditor")}
{showLineEditorAction && renderAction("toggleLinearEditor")} {showLineEditorAction && renderAction("toggleLinearEditor")}
</div> </div>
</fieldset> </fieldset>

@ -279,6 +279,7 @@ function CommandPaletteInner({
actionManager.actions.increaseFontSize, actionManager.actions.increaseFontSize,
actionManager.actions.decreaseFontSize, actionManager.actions.decreaseFontSize,
actionManager.actions.toggleLinearEditor, actionManager.actions.toggleLinearEditor,
actionManager.actions.cropEditor,
actionLink, actionLink,
].map((action: Action) => ].map((action: Action) =>
actionToCommand( actionToCommand(

@ -222,6 +222,16 @@ export const HelpDialog = ({ onClose }: { onClose?: () => void }) => {
]} ]}
isOr={false} isOr={false}
/> />
<Shortcut
label={t("helpDialog.cropStart")}
shortcuts={[t("helpDialog.doubleClick"), getShortcutKey("Enter")]}
isOr={true}
/>
<Shortcut
label={t("helpDialog.cropFinish")}
shortcuts={[getShortcutKey("Enter"), getShortcutKey("Escape")]}
isOr={true}
/>
<Shortcut label={t("toolBar.lock")} shortcuts={[KEYS.Q]} /> <Shortcut label={t("toolBar.lock")} shortcuts={[KEYS.Q]} />
<Shortcut <Shortcut
label={t("helpDialog.preventBinding")} label={t("helpDialog.preventBinding")}

@ -100,6 +100,14 @@ const getHints = ({
return t("hints.text_editing"); return t("hints.text_editing");
} }
if (appState.croppingElement) {
return t("hints.leaveCropEditor");
}
if (selectedElements.length === 1 && isImageElement(selectedElements[0])) {
return t("hints.enterCropEditor");
}
if (activeTool.type === "selection") { if (activeTool.type === "selection") {
if ( if (
appState.selectionElement && appState.selectionElement &&

@ -2147,3 +2147,12 @@ export const upIcon = createIcon(
</g>, </g>,
tablerIconProps, tablerIconProps,
); );
export const cropIcon = createIcon(
<g strokeWidth="1.25">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M8 5v10a1 1 0 0 0 1 1h10" />
<path d="M5 8h10a1 1 0 0 1 1 1v10" />
</g>,
modifiedTablerIconProps,
);

@ -328,7 +328,9 @@
"deepBoxSelect": "Hold CtrlOrCmd to deep select, and to prevent dragging", "deepBoxSelect": "Hold CtrlOrCmd to deep select, and to prevent dragging",
"eraserRevert": "Hold Alt to revert the elements marked for deletion", "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.", "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": { "canvasError": {
"cannotShowPreview": "Cannot show preview", "cannotShowPreview": "Cannot show preview",
@ -399,7 +401,9 @@
"zoomToSelection": "Zoom to selection", "zoomToSelection": "Zoom to selection",
"toggleElementLock": "Lock/unlock selection", "toggleElementLock": "Lock/unlock selection",
"movePageUpDown": "Move page up/down", "movePageUpDown": "Move page up/down",
"movePageLeftRight": "Move page left/right" "movePageLeftRight": "Move page left/right",
"cropStart": "Crop image",
"cropFinish": "Finish image cropping"
}, },
"clearCanvasDialog": { "clearCanvasDialog": {
"title": "Clear canvas" "title": "Clear canvas"

Loading…
Cancel
Save