You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
success/packages/excalidraw/actions/actionCanvas.tsx

495 lines
14 KiB
TypeScript

import { ColorPicker } from "../components/ColorPicker/ColorPicker";
import { ZoomInIcon, ZoomOutIcon } from "../components/icons";
import { ToolButton } from "../components/ToolButton";
import { CURSOR_TYPE, MIN_ZOOM, THEME, ZOOM_STEP } from "../constants";
import { getCommonBounds, getNonDeletedElements } from "../element";
import { ExcalidrawElement } from "../element/types";
import { t } from "../i18n";
import { CODES, KEYS } from "../keys";
import { getNormalizedZoom } from "../scene";
import { centerScrollOn } from "../scene/scroll";
import { getStateForZoom } from "../scene/zoom";
import { AppState, NormalizedZoomValue } from "../types";
import { getShortcutKey, updateActiveTool } from "../utils";
import { register } from "./register";
import { Tooltip } from "../components/Tooltip";
import { newElementWith } from "../element/mutateElement";
import {
getDefaultAppState,
isEraserActive,
isHandToolActive,
} from "../appState";
import { DEFAULT_CANVAS_BACKGROUND_PICKS } from "../colors";
import { SceneBounds } from "../element/bounds";
import { setCursor } from "../cursor";
export const actionChangeViewBackgroundColor = register({
name: "changeViewBackgroundColor",
trackEvent: false,
predicate: (elements, appState, props, app) => {
return (
!!app.props.UIOptions.canvasActions.changeViewBackgroundColor &&
!appState.viewModeEnabled
);
},
Fix issues related to history (#701) * Separate UI from Canvas * Explicitly define history recording * ActionManager: Set syncActionState during construction instead of in every call * Add commit to history flag to necessary actions * Disable undoing during multiElement * Write custom equality function for UI component to render it only when specific props and elements change * Remove stale comments about history skipping * Stop undo/redoing when in resizing element mode * wip * correctly reset resizingElement & add undo check * Separate selection element from the rest of the array and stop redrawing the UI when dragging the selection * Remove selectionElement from local storage * Remove unnecessary readonly type casting in actionFinalize * Fix undo / redo for multi points * Fix an issue that did not update history when elements were locked * Disable committing to history for noops - deleteSelected without deleting anything - Basic selection * Use generateEntry only inside history and pass elements and appstate to history * Update component after every history resume * Remove last item from the history only if in multi mode * Resume recording when element type is not selection * ensure we prevent hotkeys only on writable elements * Remove selection clearing from history * Remove one point arrows as they are invisibly small * Remove shape of elements from local storage * Fix removing invisible element from the array * add missing history resuming cases & simplify slice * fix lint * don't regenerate elements if no elements deselected * regenerate elements array on selection * reset state.selectionElement unconditionally * Use getter instead of passing appState and scene data through functions to actions * fix import Co-authored-by: David Luzar <luzar.david@gmail.com>
5 years ago
perform: (_, appState, value) => {
return {
appState: { ...appState, ...value },
commitToHistory: !!value.viewBackgroundColor,
};
},
PanelComponent: ({ elements, appState, updateData, appProps }) => {
// FIXME move me to src/components/mainMenu/DefaultItems.tsx
return (
<ColorPicker
palette={null}
topPicks={DEFAULT_CANVAS_BACKGROUND_PICKS}
label={t("labels.canvasBackground")}
type="canvasBackground"
color={appState.viewBackgroundColor}
onChange={(color) => updateData({ viewBackgroundColor: color })}
data-testid="canvas-background-picker"
elements={elements}
appState={appState}
updateData={updateData}
/>
);
},
});
export const actionClearCanvas = register({
name: "clearCanvas",
trackEvent: { category: "canvas" },
predicate: (elements, appState, props, app) => {
return (
!!app.props.UIOptions.canvasActions.clearCanvas &&
!appState.viewModeEnabled
);
},
perform: (elements, appState, _, app) => {
app.imageCache.clear();
return {
elements: elements.map((element) =>
newElementWith(element, { isDeleted: true }),
),
appState: {
...getDefaultAppState(),
files: {},
theme: appState.theme,
penMode: appState.penMode,
penDetected: appState.penDetected,
exportBackground: appState.exportBackground,
exportEmbedScene: appState.exportEmbedScene,
gridSize: appState.gridSize,
showStats: appState.showStats,
pasteDialog: appState.pasteDialog,
activeTool:
appState.activeTool.type === "image"
? { ...appState.activeTool, type: "selection" }
: appState.activeTool,
},
commitToHistory: true,
};
},
});
export const actionZoomIn = register({
name: "zoomIn",
viewMode: true,
trackEvent: { category: "canvas" },
perform: (_elements, appState, _, app) => {
return {
appState: {
...appState,
...getStateForZoom(
{
viewportX: appState.width / 2 + appState.offsetLeft,
viewportY: appState.height / 2 + appState.offsetTop,
nextZoom: getNormalizedZoom(appState.zoom.value + ZOOM_STEP),
},
appState,
),
userToFollow: null,
},
commitToHistory: false,
};
},
PanelComponent: ({ updateData }) => (
<ToolButton
type="button"
feat: editor redesign 🔥 (#5780) * Placed eraser into shape switcher (top toolbar). Redesigned top toolbar. * Redesigned zoom and undo-redo buttons. * Started redesigning left toolbar. * Redesigned help dialog. * Colour picker now somewhat in line with new design * [WIP] Changed a bunch of icons. TODO: organise new icons. * [WIP] Organised a bunch of icons. Still some to do * [WIP] Started working on hamburger menu. * Fixed some bugs with hamburger menu. * Menu and left toolbar positioning. * Added some more items to hamburger menu. * Changed some icons. * Modal/dialog styling & bunch of fixes. * Some more dialog improvements & fixes. * Mobile menu changes. * Menu can now be closed with outside click. * Collab avatars and button changes. * Icon sizing. Left toolbar positioning. * Implemented welcome screen rendering logic. * [WIP] Welcome screen content + design. * Some more welcome screen content and design. * Merge fixes. * Tweaked icon set. * Welcome screen darkmode fix. * Content updates. * Various small fixes & adjustments. Moved language selection into menu. Fixed some problematic icons. Slightly moved encryption icon. * Sidebar header redesign. * Libraries content rendering logic + some styling. * Somem more library sidebar styling. * Publish library dialog styling. * scroll-back-to-content btn styling * ColorPicker positioning. * Library button styling. * ColorPicker positioning "fix". * Misc adjustments. * PenMode button changes. * Trying to make mobile somewhat usable. * Added a couple of icons. * Added some shortcuts. * Prevent welcome screen flickering. Fix issue with welcome screen interactivity. Don't show sidebar button when docked. * Icon sizing on smaller screens. * Sidebar styling changes. * Alignment button... well... alignments. * Fix inconsistent padding in left toolbar. * HintViewer changes. * Hamburger menu changes. * Move encryption badge back to its original pos. * Arrowhead changes. Active state, colours + stronger shadow. * Added new custom font. * Fixed bug with library button not rendering. * Fixed issue with lang selection colours. * Add tooltips for undo, redo. * Address some dark mode contrast issues. * (Re)introduce counter for selectedItems in sidebar * [WIP] Tweaked bounding box colour & padding. * Dashed bounding box for remote clients. * Some more bounding box tweaks. * Removed docking animation for now... * Address some RTL issues. * Welcome screen responsiveness. * use lighter selection color in dark mode & align naming * use rounded corners for transform handles * use lighter gray for welcomeScreen text in dark mode * disable selection on dialog buttons * change selection button icon * fix library item width being flexible * library: visually align spinner with first section heading * lint * fix scrollbar color in dark mode & make thinner * adapt properties panel max-height * add shrotcut label to save-to-current-file * fix unrelated `useOutsideClick` firing for active modal * add promo color to e+ menu item * fix type * lowered button size * fix transform handles raidus not accounting for zoom * attempt fix for excal logo on safari * final fix for excal logo on safari * fixing fhd resolution button sized * remove TODO shortcut * Collab related styling changes. Expanding avatar list no longer offsets top toolbar. Added active state & collaborator count badge for collab button. * Tweaked collab button active colours. * Added active style to collab btn in hamburger menu * Remove unnecessary comment. * Added back promo link for non (signed in) E+ users * Go to E+ button added for signed in E+ users. * Close menu & dropdown on modal close. * tweak icons & fix rendering on smaller sizes [part one] * align welcomeScreen icons with other UI * switch icon resize mq to `device-width` * disable welcomeScreen items `:hover` when selecting on canvas * change selection box color and style * reduce selection padding and fix group selection styling * improve collab cursor styling - make name borders round - hide status when "active" - remove black/gray colors * add Twitter to hamburger menu * align collab button * add shortcut for image export dialog * revert yarn.lock * fix more tabler icons * slightly better-looking penMode button * change penMode button & tooltip * revert hamburger menu icon * align padding on lang picker & canvas bg * updated robot txt to allow twitter bot and fb bot * added new OG and tweaked the OG state * add tooltip to collab button * align style for scroll-to-content button * fix pointer-events around toolbar * fix decor arrow positioning and RTL * fix welcomeScreen-item active state in dark mode * change `load` button copy * prevent shadow anim when opening a docked sidebar * update E+ links ga params * show redirect-to-eplus welcomeScreen subheading for signed-in users * make more generic * add ga for eplus redirect button * change copy and icons for hamburger export buttons * update snaps * trim the username to account for trailing spaces * tweaks around decor breakpoints * fix linear element editor test * remove .env change * remove `it.only` Co-authored-by: dwelle <luzar.david@gmail.com> Co-authored-by: Maielo <maielo.mv@gmail.com> Co-authored-by: Aakansha Doshi <aakansha1216@gmail.com>
2 years ago
className="zoom-in-button zoom-button"
icon={ZoomInIcon}
title={`${t("buttons.zoomIn")}${getShortcutKey("CtrlOrCmd++")}`}
aria-label={t("buttons.zoomIn")}
onClick={() => {
updateData(null);
}}
/>
),
keyTest: (event) =>
(event.code === CODES.EQUAL || event.code === CODES.NUM_ADD) &&
(event[KEYS.CTRL_OR_CMD] || event.shiftKey),
});
export const actionZoomOut = register({
name: "zoomOut",
viewMode: true,
trackEvent: { category: "canvas" },
perform: (_elements, appState, _, app) => {
return {
appState: {
...appState,
...getStateForZoom(
{
viewportX: appState.width / 2 + appState.offsetLeft,
viewportY: appState.height / 2 + appState.offsetTop,
nextZoom: getNormalizedZoom(appState.zoom.value - ZOOM_STEP),
},
appState,
),
userToFollow: null,
},
commitToHistory: false,
};
},
PanelComponent: ({ updateData }) => (
<ToolButton
type="button"
feat: editor redesign 🔥 (#5780) * Placed eraser into shape switcher (top toolbar). Redesigned top toolbar. * Redesigned zoom and undo-redo buttons. * Started redesigning left toolbar. * Redesigned help dialog. * Colour picker now somewhat in line with new design * [WIP] Changed a bunch of icons. TODO: organise new icons. * [WIP] Organised a bunch of icons. Still some to do * [WIP] Started working on hamburger menu. * Fixed some bugs with hamburger menu. * Menu and left toolbar positioning. * Added some more items to hamburger menu. * Changed some icons. * Modal/dialog styling & bunch of fixes. * Some more dialog improvements & fixes. * Mobile menu changes. * Menu can now be closed with outside click. * Collab avatars and button changes. * Icon sizing. Left toolbar positioning. * Implemented welcome screen rendering logic. * [WIP] Welcome screen content + design. * Some more welcome screen content and design. * Merge fixes. * Tweaked icon set. * Welcome screen darkmode fix. * Content updates. * Various small fixes & adjustments. Moved language selection into menu. Fixed some problematic icons. Slightly moved encryption icon. * Sidebar header redesign. * Libraries content rendering logic + some styling. * Somem more library sidebar styling. * Publish library dialog styling. * scroll-back-to-content btn styling * ColorPicker positioning. * Library button styling. * ColorPicker positioning "fix". * Misc adjustments. * PenMode button changes. * Trying to make mobile somewhat usable. * Added a couple of icons. * Added some shortcuts. * Prevent welcome screen flickering. Fix issue with welcome screen interactivity. Don't show sidebar button when docked. * Icon sizing on smaller screens. * Sidebar styling changes. * Alignment button... well... alignments. * Fix inconsistent padding in left toolbar. * HintViewer changes. * Hamburger menu changes. * Move encryption badge back to its original pos. * Arrowhead changes. Active state, colours + stronger shadow. * Added new custom font. * Fixed bug with library button not rendering. * Fixed issue with lang selection colours. * Add tooltips for undo, redo. * Address some dark mode contrast issues. * (Re)introduce counter for selectedItems in sidebar * [WIP] Tweaked bounding box colour & padding. * Dashed bounding box for remote clients. * Some more bounding box tweaks. * Removed docking animation for now... * Address some RTL issues. * Welcome screen responsiveness. * use lighter selection color in dark mode & align naming * use rounded corners for transform handles * use lighter gray for welcomeScreen text in dark mode * disable selection on dialog buttons * change selection button icon * fix library item width being flexible * library: visually align spinner with first section heading * lint * fix scrollbar color in dark mode & make thinner * adapt properties panel max-height * add shrotcut label to save-to-current-file * fix unrelated `useOutsideClick` firing for active modal * add promo color to e+ menu item * fix type * lowered button size * fix transform handles raidus not accounting for zoom * attempt fix for excal logo on safari * final fix for excal logo on safari * fixing fhd resolution button sized * remove TODO shortcut * Collab related styling changes. Expanding avatar list no longer offsets top toolbar. Added active state & collaborator count badge for collab button. * Tweaked collab button active colours. * Added active style to collab btn in hamburger menu * Remove unnecessary comment. * Added back promo link for non (signed in) E+ users * Go to E+ button added for signed in E+ users. * Close menu & dropdown on modal close. * tweak icons & fix rendering on smaller sizes [part one] * align welcomeScreen icons with other UI * switch icon resize mq to `device-width` * disable welcomeScreen items `:hover` when selecting on canvas * change selection box color and style * reduce selection padding and fix group selection styling * improve collab cursor styling - make name borders round - hide status when "active" - remove black/gray colors * add Twitter to hamburger menu * align collab button * add shortcut for image export dialog * revert yarn.lock * fix more tabler icons * slightly better-looking penMode button * change penMode button & tooltip * revert hamburger menu icon * align padding on lang picker & canvas bg * updated robot txt to allow twitter bot and fb bot * added new OG and tweaked the OG state * add tooltip to collab button * align style for scroll-to-content button * fix pointer-events around toolbar * fix decor arrow positioning and RTL * fix welcomeScreen-item active state in dark mode * change `load` button copy * prevent shadow anim when opening a docked sidebar * update E+ links ga params * show redirect-to-eplus welcomeScreen subheading for signed-in users * make more generic * add ga for eplus redirect button * change copy and icons for hamburger export buttons * update snaps * trim the username to account for trailing spaces * tweaks around decor breakpoints * fix linear element editor test * remove .env change * remove `it.only` Co-authored-by: dwelle <luzar.david@gmail.com> Co-authored-by: Maielo <maielo.mv@gmail.com> Co-authored-by: Aakansha Doshi <aakansha1216@gmail.com>
2 years ago
className="zoom-out-button zoom-button"
icon={ZoomOutIcon}
title={`${t("buttons.zoomOut")}${getShortcutKey("CtrlOrCmd+-")}`}
aria-label={t("buttons.zoomOut")}
onClick={() => {
updateData(null);
}}
/>
),
keyTest: (event) =>
(event.code === CODES.MINUS || event.code === CODES.NUM_SUBTRACT) &&
(event[KEYS.CTRL_OR_CMD] || event.shiftKey),
});
export const actionResetZoom = register({
name: "resetZoom",
viewMode: true,
trackEvent: { category: "canvas" },
perform: (_elements, appState, _, app) => {
return {
appState: {
...appState,
...getStateForZoom(
{
viewportX: appState.width / 2 + appState.offsetLeft,
viewportY: appState.height / 2 + appState.offsetTop,
nextZoom: getNormalizedZoom(1),
},
appState,
),
userToFollow: null,
},
commitToHistory: false,
};
},
PanelComponent: ({ updateData, appState }) => (
<Tooltip label={t("buttons.resetZoom")} style={{ height: "100%" }}>
<ToolButton
type="button"
feat: editor redesign 🔥 (#5780) * Placed eraser into shape switcher (top toolbar). Redesigned top toolbar. * Redesigned zoom and undo-redo buttons. * Started redesigning left toolbar. * Redesigned help dialog. * Colour picker now somewhat in line with new design * [WIP] Changed a bunch of icons. TODO: organise new icons. * [WIP] Organised a bunch of icons. Still some to do * [WIP] Started working on hamburger menu. * Fixed some bugs with hamburger menu. * Menu and left toolbar positioning. * Added some more items to hamburger menu. * Changed some icons. * Modal/dialog styling & bunch of fixes. * Some more dialog improvements & fixes. * Mobile menu changes. * Menu can now be closed with outside click. * Collab avatars and button changes. * Icon sizing. Left toolbar positioning. * Implemented welcome screen rendering logic. * [WIP] Welcome screen content + design. * Some more welcome screen content and design. * Merge fixes. * Tweaked icon set. * Welcome screen darkmode fix. * Content updates. * Various small fixes & adjustments. Moved language selection into menu. Fixed some problematic icons. Slightly moved encryption icon. * Sidebar header redesign. * Libraries content rendering logic + some styling. * Somem more library sidebar styling. * Publish library dialog styling. * scroll-back-to-content btn styling * ColorPicker positioning. * Library button styling. * ColorPicker positioning "fix". * Misc adjustments. * PenMode button changes. * Trying to make mobile somewhat usable. * Added a couple of icons. * Added some shortcuts. * Prevent welcome screen flickering. Fix issue with welcome screen interactivity. Don't show sidebar button when docked. * Icon sizing on smaller screens. * Sidebar styling changes. * Alignment button... well... alignments. * Fix inconsistent padding in left toolbar. * HintViewer changes. * Hamburger menu changes. * Move encryption badge back to its original pos. * Arrowhead changes. Active state, colours + stronger shadow. * Added new custom font. * Fixed bug with library button not rendering. * Fixed issue with lang selection colours. * Add tooltips for undo, redo. * Address some dark mode contrast issues. * (Re)introduce counter for selectedItems in sidebar * [WIP] Tweaked bounding box colour & padding. * Dashed bounding box for remote clients. * Some more bounding box tweaks. * Removed docking animation for now... * Address some RTL issues. * Welcome screen responsiveness. * use lighter selection color in dark mode & align naming * use rounded corners for transform handles * use lighter gray for welcomeScreen text in dark mode * disable selection on dialog buttons * change selection button icon * fix library item width being flexible * library: visually align spinner with first section heading * lint * fix scrollbar color in dark mode & make thinner * adapt properties panel max-height * add shrotcut label to save-to-current-file * fix unrelated `useOutsideClick` firing for active modal * add promo color to e+ menu item * fix type * lowered button size * fix transform handles raidus not accounting for zoom * attempt fix for excal logo on safari * final fix for excal logo on safari * fixing fhd resolution button sized * remove TODO shortcut * Collab related styling changes. Expanding avatar list no longer offsets top toolbar. Added active state & collaborator count badge for collab button. * Tweaked collab button active colours. * Added active style to collab btn in hamburger menu * Remove unnecessary comment. * Added back promo link for non (signed in) E+ users * Go to E+ button added for signed in E+ users. * Close menu & dropdown on modal close. * tweak icons & fix rendering on smaller sizes [part one] * align welcomeScreen icons with other UI * switch icon resize mq to `device-width` * disable welcomeScreen items `:hover` when selecting on canvas * change selection box color and style * reduce selection padding and fix group selection styling * improve collab cursor styling - make name borders round - hide status when "active" - remove black/gray colors * add Twitter to hamburger menu * align collab button * add shortcut for image export dialog * revert yarn.lock * fix more tabler icons * slightly better-looking penMode button * change penMode button & tooltip * revert hamburger menu icon * align padding on lang picker & canvas bg * updated robot txt to allow twitter bot and fb bot * added new OG and tweaked the OG state * add tooltip to collab button * align style for scroll-to-content button * fix pointer-events around toolbar * fix decor arrow positioning and RTL * fix welcomeScreen-item active state in dark mode * change `load` button copy * prevent shadow anim when opening a docked sidebar * update E+ links ga params * show redirect-to-eplus welcomeScreen subheading for signed-in users * make more generic * add ga for eplus redirect button * change copy and icons for hamburger export buttons * update snaps * trim the username to account for trailing spaces * tweaks around decor breakpoints * fix linear element editor test * remove .env change * remove `it.only` Co-authored-by: dwelle <luzar.david@gmail.com> Co-authored-by: Maielo <maielo.mv@gmail.com> Co-authored-by: Aakansha Doshi <aakansha1216@gmail.com>
2 years ago
className="reset-zoom-button zoom-button"
title={t("buttons.resetZoom")}
aria-label={t("buttons.resetZoom")}
onClick={() => {
updateData(null);
}}
>
{(appState.zoom.value * 100).toFixed(0)}%
</ToolButton>
</Tooltip>
),
keyTest: (event) =>
(event.code === CODES.ZERO || event.code === CODES.NUM_ZERO) &&
(event[KEYS.CTRL_OR_CMD] || event.shiftKey),
});
const zoomValueToFitBoundsOnViewport = (
bounds: SceneBounds,
viewportDimensions: { width: number; height: number },
) => {
const [x1, y1, x2, y2] = bounds;
const commonBoundsWidth = x2 - x1;
const zoomValueForWidth = viewportDimensions.width / commonBoundsWidth;
const commonBoundsHeight = y2 - y1;
const zoomValueForHeight = viewportDimensions.height / commonBoundsHeight;
const smallestZoomValue = Math.min(zoomValueForWidth, zoomValueForHeight);
const zoomAdjustedToSteps =
Math.floor(smallestZoomValue / ZOOM_STEP) * ZOOM_STEP;
const clampedZoomValueToFitElements = Math.min(
Math.max(zoomAdjustedToSteps, MIN_ZOOM),
1,
);
return clampedZoomValueToFitElements as NormalizedZoomValue;
};
export const zoomToFitBounds = ({
bounds,
appState,
fitToViewport = false,
viewportZoomFactor = 0.7,
}: {
bounds: SceneBounds;
appState: Readonly<AppState>;
/** whether to fit content to viewport (beyond >100%) */
fitToViewport: boolean;
/** zoom content to cover X of the viewport, when fitToViewport=true */
viewportZoomFactor?: number;
}) => {
const [x1, y1, x2, y2] = bounds;
const centerX = (x1 + x2) / 2;
const centerY = (y1 + y2) / 2;
let newZoomValue;
let scrollX;
let scrollY;
if (fitToViewport) {
const commonBoundsWidth = x2 - x1;
const commonBoundsHeight = y2 - y1;
newZoomValue =
Math.min(
appState.width / commonBoundsWidth,
appState.height / commonBoundsHeight,
) * Math.min(1, Math.max(viewportZoomFactor, 0.1));
// Apply clamping to newZoomValue to be between 10% and 3000%
newZoomValue = Math.min(
Math.max(newZoomValue, 0.1),
30.0,
) as NormalizedZoomValue;
let appStateWidth = appState.width;
if (appState.openSidebar) {
const sidebarDOMElem = document.querySelector(
".sidebar",
) as HTMLElement | null;
const sidebarWidth = sidebarDOMElem?.offsetWidth ?? 0;
const isRTL = document.documentElement.getAttribute("dir") === "rtl";
appStateWidth = !isRTL
? appState.width - sidebarWidth
: appState.width + sidebarWidth;
}
scrollX = (appStateWidth / 2) * (1 / newZoomValue) - centerX;
scrollY = (appState.height / 2) * (1 / newZoomValue) - centerY;
} else {
newZoomValue = zoomValueToFitBoundsOnViewport(bounds, {
width: appState.width,
height: appState.height,
});
const centerScroll = centerScrollOn({
scenePoint: { x: centerX, y: centerY },
viewportDimensions: {
width: appState.width,
height: appState.height,
},
zoom: { value: newZoomValue },
});
scrollX = centerScroll.scrollX;
scrollY = centerScroll.scrollY;
}
return {
appState: {
...appState,
scrollX,
scrollY,
zoom: { value: newZoomValue },
},
commitToHistory: false,
};
};
export const zoomToFit = ({
targetElements,
appState,
fitToViewport,
viewportZoomFactor,
}: {
targetElements: readonly ExcalidrawElement[];
appState: Readonly<AppState>;
/** whether to fit content to viewport (beyond >100%) */
fitToViewport: boolean;
/** zoom content to cover X of the viewport, when fitToViewport=true */
viewportZoomFactor?: number;
}) => {
const commonBounds = getCommonBounds(getNonDeletedElements(targetElements));
return zoomToFitBounds({
bounds: commonBounds,
appState,
fitToViewport,
viewportZoomFactor,
});
};
// Note, this action differs from actionZoomToFitSelection in that it doesn't
// zoom beyond 100%. In other words, if the content is smaller than viewport
// size, it won't be zoomed in.
export const actionZoomToFitSelectionInViewport = register({
name: "zoomToFitSelectionInViewport",
trackEvent: { category: "canvas" },
perform: (elements, appState, _, app) => {
const selectedElements = app.scene.getSelectedElements(appState);
return zoomToFit({
targetElements: selectedElements.length ? selectedElements : elements,
appState: {
...appState,
userToFollow: null,
},
fitToViewport: false,
});
},
// NOTE shift-2 should have been assigned actionZoomToFitSelection.
// TBD on how proceed
keyTest: (event) =>
event.code === CODES.TWO &&
event.shiftKey &&
!event.altKey &&
!event[KEYS.CTRL_OR_CMD],
});
export const actionZoomToFitSelection = register({
name: "zoomToFitSelection",
trackEvent: { category: "canvas" },
perform: (elements, appState, _, app) => {
const selectedElements = app.scene.getSelectedElements(appState);
return zoomToFit({
targetElements: selectedElements.length ? selectedElements : elements,
appState: {
...appState,
userToFollow: null,
},
fitToViewport: true,
});
},
// NOTE this action should use shift-2 per figma, alas
keyTest: (event) =>
event.code === CODES.THREE &&
event.shiftKey &&
!event.altKey &&
!event[KEYS.CTRL_OR_CMD],
});
export const actionZoomToFit = register({
name: "zoomToFit",
viewMode: true,
trackEvent: { category: "canvas" },
perform: (elements, appState) =>
zoomToFit({
targetElements: elements,
appState: {
...appState,
userToFollow: null,
},
fitToViewport: false,
}),
keyTest: (event) =>
event.code === CODES.ONE &&
event.shiftKey &&
!event.altKey &&
!event[KEYS.CTRL_OR_CMD],
});
export const actionToggleTheme = register({
name: "toggleTheme",
viewMode: true,
trackEvent: { category: "canvas" },
perform: (_, appState, value) => {
return {
appState: {
...appState,
theme:
value || (appState.theme === THEME.LIGHT ? THEME.DARK : THEME.LIGHT),
},
commitToHistory: false,
};
},
keyTest: (event) => event.altKey && event.shiftKey && event.code === CODES.D,
predicate: (elements, appState, props, app) => {
return !!app.props.UIOptions.canvasActions.toggleTheme;
},
});
export const actionToggleEraserTool = register({
name: "toggleEraserTool",
trackEvent: { category: "toolbar" },
perform: (elements, appState) => {
let activeTool: AppState["activeTool"];
if (isEraserActive(appState)) {
activeTool = updateActiveTool(appState, {
...(appState.activeTool.lastActiveTool || {
type: "selection",
}),
lastActiveToolBeforeEraser: null,
});
} else {
activeTool = updateActiveTool(appState, {
type: "eraser",
lastActiveToolBeforeEraser: appState.activeTool,
});
}
return {
appState: {
...appState,
selectedElementIds: {},
selectedGroupIds: {},
activeEmbeddable: null,
activeTool,
},
commitToHistory: true,
};
},
keyTest: (event) => event.key === KEYS.E,
});
export const actionToggleHandTool = register({
name: "toggleHandTool",
trackEvent: { category: "toolbar" },
perform: (elements, appState, _, app) => {
let activeTool: AppState["activeTool"];
if (isHandToolActive(appState)) {
activeTool = updateActiveTool(appState, {
...(appState.activeTool.lastActiveTool || {
type: "selection",
}),
lastActiveToolBeforeEraser: null,
});
} else {
activeTool = updateActiveTool(appState, {
type: "hand",
lastActiveToolBeforeEraser: appState.activeTool,
});
setCursor(app.interactiveCanvas, CURSOR_TYPE.GRAB);
}
return {
appState: {
...appState,
selectedElementIds: {},
selectedGroupIds: {},
activeEmbeddable: null,
activeTool,
},
commitToHistory: true,
};
},
keyTest: (event) =>
!event.altKey && !event[KEYS.CTRL_OR_CMD] && event.key === KEYS.H,
});