From fb4bb29aa5d9f03c60afc9984b01f25bcd117970 Mon Sep 17 00:00:00 2001 From: David Luzar <5153846+dwelle@users.noreply.github.com> Date: Thu, 15 Aug 2024 18:48:25 +0200 Subject: [PATCH] fix: object snapping not working (#8381) --- packages/excalidraw/components/App.tsx | 21 +++--- packages/excalidraw/components/HintViewer.tsx | 3 +- .../excalidraw/components/Stats/index.tsx | 4 +- packages/excalidraw/snapping.ts | 71 +++++++++++-------- 4 files changed, 53 insertions(+), 46 deletions(-) diff --git a/packages/excalidraw/components/App.tsx b/packages/excalidraw/components/App.tsx index 4cbac0888..b0293b781 100644 --- a/packages/excalidraw/components/App.tsx +++ b/packages/excalidraw/components/App.tsx @@ -384,6 +384,7 @@ import { getVisibleGaps, getReferenceSnapPoints, SnapCache, + isGridModeEnabled, } from "../snapping"; import { actionWrapTextInContainer } from "../actions/actionBoundText"; import BraveMeasureTextError from "./BraveMeasureTextError"; @@ -818,9 +819,7 @@ class App extends React.Component { */ public getEffectiveGridSize = () => { return ( - this.props.gridModeEnabled ?? this.state.gridModeEnabled - ? this.state.gridSize - : null + isGridModeEnabled(this) ? this.state.gridSize : null ) as NullableGridSize; }; @@ -1696,9 +1695,7 @@ class App extends React.Component { renderConfig={{ imageCache: this.imageCache, isExporting: false, - renderGrid: - this.props.gridModeEnabled ?? - this.state.gridModeEnabled, + renderGrid: isGridModeEnabled(this), canvasBackgroundColor: this.state.viewBackgroundColor, embedsValidationStatus: this.embedsValidationStatus, @@ -5452,7 +5449,7 @@ class App extends React.Component { ) { const { originOffset, snapLines } = getSnapLinesAtPointer( this.scene.getNonDeletedElements(), - this.state, + this, { x: scenePointerX, y: scenePointerY, @@ -7499,7 +7496,7 @@ class App extends React.Component { if ( isSnappingEnabled({ event, - appState: this.state, + app: this, selectedElements, }) && (recomputeAnyways || !SnapCache.getReferenceSnapPoints()) @@ -7523,7 +7520,7 @@ class App extends React.Component { if ( isSnappingEnabled({ event, - appState: this.state, + app: this, selectedElements, }) && (recomputeAnyways || !SnapCache.getVisibleGaps()) @@ -7811,7 +7808,7 @@ class App extends React.Component { const { snapOffset, snapLines } = snapDraggedElements( originalElements, dragOffset, - this.state, + this, event, this.scene.getNonDeletedElementsMap(), ); @@ -9812,7 +9809,7 @@ class App extends React.Component { const { snapOffset, snapLines } = snapNewElement( newElement, - this.state, + this, event, { x: @@ -9949,7 +9946,7 @@ class App extends React.Component { const { snapOffset, snapLines } = snapResizingElements( selectedElements, getSelectedElements(originalElements, this.state), - this.state, + this, event, dragOffset, transformHandleType, diff --git a/packages/excalidraw/components/HintViewer.tsx b/packages/excalidraw/components/HintViewer.tsx index 34bed1ed0..e6411749f 100644 --- a/packages/excalidraw/components/HintViewer.tsx +++ b/packages/excalidraw/components/HintViewer.tsx @@ -12,6 +12,7 @@ import { isEraserActive } from "../appState"; import "./HintViewer.scss"; import { isNodeInFlowchart } from "../element/flowchart"; +import { isGridModeEnabled } from "../snapping"; interface HintViewerProps { appState: UIAppState; @@ -100,7 +101,7 @@ const getHints = ({ return t("hints.deepBoxSelect"); } - if (appState.gridSize && appState.selectedElementsAreBeingDragged) { + if (isGridModeEnabled(app) && appState.selectedElementsAreBeingDragged) { return t("hints.disableSnapping"); } diff --git a/packages/excalidraw/components/Stats/index.tsx b/packages/excalidraw/components/Stats/index.tsx index 642862e2e..3857adbe4 100644 --- a/packages/excalidraw/components/Stats/index.tsx +++ b/packages/excalidraw/components/Stats/index.tsx @@ -28,6 +28,7 @@ import CanvasGrid from "./CanvasGrid"; import clsx from "clsx"; import "./Stats.scss"; +import { isGridModeEnabled } from "../../snapping"; interface StatsProps { app: AppClassProperties; @@ -44,8 +45,7 @@ export const Stats = (props: StatsProps) => { selectedElementIds: appState.selectedElementIds, includeBoundTextElement: false, }); - const gridModeEnabled = - props.app.props.gridModeEnabled ?? appState.gridModeEnabled; + const gridModeEnabled = isGridModeEnabled(props.app); return ( + app.props.gridModeEnabled ?? app.state.gridModeEnabled; + export const isSnappingEnabled = ({ event, - appState, + app, selectedElements, }: { - appState: AppState; + app: AppClassProperties; event: KeyboardModifiersObject; selectedElements: NonDeletedExcalidrawElement[]; }) => { if (event) { return ( - (appState.objectsSnapModeEnabled && !event[KEYS.CTRL_OR_CMD]) || - (!appState.objectsSnapModeEnabled && + (app.state.objectsSnapModeEnabled && !event[KEYS.CTRL_OR_CMD]) || + (!app.state.objectsSnapModeEnabled && event[KEYS.CTRL_OR_CMD] && - appState.gridSize === null) + !isGridModeEnabled(app)) ); } @@ -161,7 +169,7 @@ export const isSnappingEnabled = ({ if (selectedElements.length === 1 && selectedElements[0].type === "arrow") { return false; } - return appState.objectsSnapModeEnabled; + return app.state.objectsSnapModeEnabled; }; export const areRoughlyEqual = (a: number, b: number, precision = 0.01) => { @@ -406,13 +414,13 @@ export const getVisibleGaps = ( const getGapSnaps = ( selectedElements: ExcalidrawElement[], dragOffset: Vector2D, - appState: AppState, + app: AppClassProperties, event: KeyboardModifiersObject, nearestSnapsX: Snaps, nearestSnapsY: Snaps, minOffset: Vector2D, ) => { - if (!isSnappingEnabled({ appState, event, selectedElements })) { + if (!isSnappingEnabled({ app, event, selectedElements })) { return []; } @@ -596,14 +604,14 @@ export const getReferenceSnapPoints = ( const getPointSnaps = ( selectedElements: ExcalidrawElement[], selectionSnapPoints: Point[], - appState: AppState, + app: AppClassProperties, event: KeyboardModifiersObject, nearestSnapsX: Snaps, nearestSnapsY: Snaps, minOffset: Vector2D, ) => { if ( - !isSnappingEnabled({ appState, event, selectedElements }) || + !isSnappingEnabled({ app, event, selectedElements }) || (selectedElements.length === 0 && selectionSnapPoints.length === 0) ) { return []; @@ -652,13 +660,14 @@ const getPointSnaps = ( export const snapDraggedElements = ( elements: ExcalidrawElement[], dragOffset: Vector2D, - appState: AppState, + app: AppClassProperties, event: KeyboardModifiersObject, elementsMap: ElementsMap, ) => { + const appState = app.state; const selectedElements = getSelectedElements(elements, appState); if ( - !isSnappingEnabled({ appState, event, selectedElements }) || + !isSnappingEnabled({ app, event, selectedElements }) || selectedElements.length === 0 ) { return { @@ -687,7 +696,7 @@ export const snapDraggedElements = ( getPointSnaps( selectedElements, selectionPoints, - appState, + app, event, nearestSnapsX, nearestSnapsY, @@ -697,7 +706,7 @@ export const snapDraggedElements = ( getGapSnaps( selectedElements, dragOffset, - appState, + app, event, nearestSnapsX, nearestSnapsY, @@ -732,7 +741,7 @@ export const snapDraggedElements = ( getElementsCorners(selectedElements, elementsMap, { dragOffset: newDragOffset, }), - appState, + app, event, nearestSnapsX, nearestSnapsY, @@ -742,7 +751,7 @@ export const snapDraggedElements = ( getGapSnaps( selectedElements, newDragOffset, - appState, + app, event, nearestSnapsX, nearestSnapsY, @@ -1075,13 +1084,13 @@ export const snapResizingElements = ( selectedElements: ExcalidrawElement[], // while using the original elements to appy dragOffset to calculate snaps selectedOriginalElements: ExcalidrawElement[], - appState: AppState, + app: AppClassProperties, event: KeyboardModifiersObject, dragOffset: Vector2D, transformHandle: MaybeTransformHandleType, ) => { if ( - !isSnappingEnabled({ event, selectedElements, appState }) || + !isSnappingEnabled({ event, selectedElements, app }) || selectedElements.length === 0 || (selectedElements.length === 1 && !areRoughlyEqual(selectedElements[0].angle, 0)) @@ -1147,7 +1156,7 @@ export const snapResizingElements = ( } } - const snapDistance = getSnapDistance(appState.zoom.value); + const snapDistance = getSnapDistance(app.state.zoom.value); const minOffset = { x: snapDistance, @@ -1160,7 +1169,7 @@ export const snapResizingElements = ( getPointSnaps( selectedOriginalElements, selectionSnapPoints, - appState, + app, event, nearestSnapsX, nearestSnapsY, @@ -1193,7 +1202,7 @@ export const snapResizingElements = ( getPointSnaps( selectedElements, corners, - appState, + app, event, nearestSnapsX, nearestSnapsY, @@ -1210,13 +1219,13 @@ export const snapResizingElements = ( export const snapNewElement = ( newElement: ExcalidrawElement, - appState: AppState, + app: AppClassProperties, event: KeyboardModifiersObject, origin: Vector2D, dragOffset: Vector2D, elementsMap: ElementsMap, ) => { - if (!isSnappingEnabled({ event, selectedElements: [newElement], appState })) { + if (!isSnappingEnabled({ event, selectedElements: [newElement], app })) { return { snapOffset: { x: 0, y: 0 }, snapLines: [], @@ -1227,7 +1236,7 @@ export const snapNewElement = ( [origin.x + dragOffset.x, origin.y + dragOffset.y], ]; - const snapDistance = getSnapDistance(appState.zoom.value); + const snapDistance = getSnapDistance(app.state.zoom.value); const minOffset = { x: snapDistance, @@ -1240,7 +1249,7 @@ export const snapNewElement = ( getPointSnaps( [newElement], selectionSnapPoints, - appState, + app, event, nearestSnapsX, nearestSnapsY, @@ -1265,7 +1274,7 @@ export const snapNewElement = ( getPointSnaps( [newElement], corners, - appState, + app, event, nearestSnapsX, nearestSnapsY, @@ -1282,12 +1291,12 @@ export const snapNewElement = ( export const getSnapLinesAtPointer = ( elements: readonly ExcalidrawElement[], - appState: AppState, + app: AppClassProperties, pointer: Vector2D, event: KeyboardModifiersObject, elementsMap: ElementsMap, ) => { - if (!isSnappingEnabled({ event, selectedElements: [], appState })) { + if (!isSnappingEnabled({ event, selectedElements: [], app })) { return { originOffset: { x: 0, y: 0 }, snapLines: [], @@ -1297,11 +1306,11 @@ export const getSnapLinesAtPointer = ( const referenceElements = getVisibleAndNonSelectedElements( elements, [], - appState, + app.state, elementsMap, ); - const snapDistance = getSnapDistance(appState.zoom.value); + const snapDistance = getSnapDistance(app.state.zoom.value); const minOffset = { x: snapDistance,