diff --git a/src/actions/actionCanvas.tsx b/src/actions/actionCanvas.tsx index 12b0c765..addc69d1 100644 --- a/src/actions/actionCanvas.tsx +++ b/src/actions/actionCanvas.tsx @@ -4,12 +4,14 @@ import { getDefaultAppState } from "../appState"; import { trash, zoomIn, zoomOut, resetZoom } from "../components/icons"; import { ToolButton } from "../components/ToolButton"; import { t } from "../i18n"; -import { getNormalizedZoom } from "../scene"; +import { getNormalizedZoom, getSelectedElements } from "../scene"; +import { getNonDeletedElements } from "../element"; import { CODES, KEYS } from "../keys"; import { getShortcutKey } from "../utils"; import useIsMobile from "../is-mobile"; import { register } from "./register"; import { newElementWith } from "../element/mutateElement"; +import { ExcalidrawElement } from "../element/types"; import { AppState, NormalizedZoomValue } from "../types"; import { getCommonBounds } from "../element"; import { getNewZoom } from "../scene/zoom"; @@ -204,38 +206,60 @@ const zoomValueToFitBoundsOnViewport = ( return clampedZoomValueToFitElements as NormalizedZoomValue; }; +const zoomToFitElements = ( + elements: readonly ExcalidrawElement[], + appState: Readonly, + zoomToSelection: boolean, +) => { + const nonDeletedElements = getNonDeletedElements(elements); + const selectedElements = getSelectedElements(nonDeletedElements, appState); + + const commonBounds = + zoomToSelection && selectedElements.length > 0 + ? getCommonBounds(selectedElements) + : getCommonBounds(nonDeletedElements); + + const zoomValue = zoomValueToFitBoundsOnViewport(commonBounds, { + width: appState.width, + height: appState.height, + }); + const newZoom = getNewZoom(zoomValue, appState.zoom); + const action = zoomToSelection ? "selection" : "fit"; + + const [x1, y1, x2, y2] = commonBounds; + const centerX = (x1 + x2) / 2; + const centerY = (y1 + y2) / 2; + trackEvent(EVENT_ACTION, "zoom", action, newZoom.value * 100); + return { + appState: { + ...appState, + ...centerScrollOn({ + scenePoint: { x: centerX, y: centerY }, + viewportDimensions: { + width: appState.width, + height: appState.height, + }, + zoom: newZoom, + }), + zoom: newZoom, + }, + commitToHistory: false, + }; +}; + +export const actionZoomToSelected = register({ + name: "zoomToSelection", + perform: (elements, appState) => zoomToFitElements(elements, appState, true), + keyTest: (event) => + event.code === CODES.TWO && + event.shiftKey && + !event.altKey && + !event[KEYS.CTRL_OR_CMD], +}); + export const actionZoomToFit = register({ name: "zoomToFit", - perform: (elements, appState) => { - const nonDeletedElements = elements.filter((element) => !element.isDeleted); - const commonBounds = getCommonBounds(nonDeletedElements); - - const zoomValue = zoomValueToFitBoundsOnViewport(commonBounds, { - width: appState.width, - height: appState.height, - }); - const newZoom = getNewZoom(zoomValue, appState.zoom); - - const [x1, y1, x2, y2] = commonBounds; - const centerX = (x1 + x2) / 2; - const centerY = (y1 + y2) / 2; - trackEvent(EVENT_ACTION, "zoom", "fit", newZoom.value * 100); - return { - appState: { - ...appState, - ...centerScrollOn({ - scenePoint: { x: centerX, y: centerY }, - viewportDimensions: { - width: appState.width, - height: appState.height, - }, - zoom: newZoom, - }), - zoom: newZoom, - }, - commitToHistory: false, - }; - }, + perform: (elements, appState) => zoomToFitElements(elements, appState, false), keyTest: (event) => event.code === CODES.ONE && event.shiftKey && diff --git a/src/actions/types.ts b/src/actions/types.ts index 25108fd2..ca30f567 100644 --- a/src/actions/types.ts +++ b/src/actions/types.ts @@ -58,6 +58,7 @@ export type ActionName = | "zoomOut" | "resetZoom" | "zoomToFit" + | "zoomToSelection" | "changeFontFamily" | "changeTextAlign" | "toggleFullScreen" diff --git a/src/components/ShortcutsDialog.tsx b/src/components/ShortcutsDialog.tsx index 4eb9155e..7e2cfad9 100644 --- a/src/components/ShortcutsDialog.tsx +++ b/src/components/ShortcutsDialog.tsx @@ -206,6 +206,10 @@ export const ShortcutsDialog = ({ onClose }: { onClose?: () => void }) => { label={t("shortcutsDialog.zoomToFit")} shortcuts={["Shift+1"]} /> + ## [Unreleased] ### Features +- Add zoom to selection [#2522](https://github.com/excalidraw/excalidraw/pull/2522) - Insert Library items in the middle of the screen [#2527](https://github.com/excalidraw/excalidraw/pull/2527) - Show shortcut context menu [#2501](https://github.com/excalidraw/excalidraw/pull/2501) - Aligns arrowhead schemas [#2517](https://github.com/excalidraw/excalidraw/pull/2517)