diff --git a/packages/element/src/binding.ts b/packages/element/src/binding.ts index 6bc3deb995..cda1e87e02 100644 --- a/packages/element/src/binding.ts +++ b/packages/element/src/binding.ts @@ -103,32 +103,25 @@ export type BindingStrategy = focusPoint?: undefined; }; -export const FIXED_BINDING_DISTANCE = 5; +/** excludes element strokeWidth */ +export const BASE_BINDING_GAP = 10; +/** scaled based on zoom when < 1 */ +const BASE_BINDING_DISTANCE = Math.max(BASE_BINDING_GAP, 15); export const BINDING_HIGHLIGHT_THICKNESS = 10; -export const getFixedBindingDistance = ( +export const getFixedBindingGap = ( element: ExcalidrawBindableElement, -): number => FIXED_BINDING_DISTANCE + element.strokeWidth / 2; +): number => BASE_BINDING_GAP + element.strokeWidth / 2; -export const maxBindingGap_simple = ( - element: ExcalidrawElement, - elementWidth: number, - elementHeight: number, - zoom?: AppState["zoom"], -): number => { +export const maxBindingDistance_simple = (zoom?: AppState["zoom"]): number => { const zoomValue = zoom?.value && zoom.value < 1 ? zoom.value : 1; - - // Aligns diamonds with rectangles - const shapeRatio = element.type === "diamond" ? 1 / Math.sqrt(2) : 1; - const smallerDimension = shapeRatio * Math.min(elementWidth, elementHeight); - - return Math.max( - 16, - // bigger bindable boundary for bigger elements - Math.min(0.25 * smallerDimension, 32), - // keep in sync with the zoomed highlight - BINDING_HIGHLIGHT_THICKNESS / zoomValue + FIXED_BINDING_DISTANCE, + return clamp( + // reducing zoom impact so that the diff between binding distance and + // binding gap is kept to minimum when possible + BASE_BINDING_DISTANCE / (zoomValue * 1.5), + BASE_BINDING_DISTANCE, + BASE_BINDING_DISTANCE * 2, ); }; @@ -247,8 +240,7 @@ const bindingStrategyForElbowArrowEndpointDragging = ( globalPoint, elements, elementsMap, - (element) => - maxBindingGap_simple(element, element.width, element.height, zoom), + (element) => maxBindingDistance_simple(zoom), ); const current = hit @@ -667,7 +659,7 @@ const getBindingStrategyForDraggingBindingElementEndpoints_simple = ( globalPoint, elements, elementsMap, - (e) => maxBindingGap_simple(e, e.width, e.height, appState.zoom), + (e) => maxBindingDistance_simple(appState.zoom), ); const pointInElement = hit && isPointInElement(globalPoint, hit, elementsMap); const otherBindableElement = otherBinding @@ -1197,12 +1189,7 @@ const getDistanceForBinding = ( zoom?: AppState["zoom"], ) => { const distance = distanceToElement(bindableElement, elementsMap, point); - const bindDistance = maxBindingGap_simple( - bindableElement, - bindableElement.width, - bindableElement.height, - zoom, - ); + const bindDistance = maxBindingDistance_simple(zoom); return distance > bindDistance ? null : distance; }; @@ -1272,7 +1259,7 @@ export const bindPointToSnapToElementOutline = ( bindableElement, elementsMap, intersector, - getFixedBindingDistance(bindableElement), + getFixedBindingGap(bindableElement), ).sort(pointDistanceSq)[0]; if (!intersection) { @@ -1294,7 +1281,7 @@ export const bindPointToSnapToElementOutline = ( bindableElement, elementsMap, anotherIntersector, - FIXED_BINDING_DISTANCE, + BASE_BINDING_GAP, ).sort(pointDistanceSq)[0]; } } else { @@ -1302,7 +1289,7 @@ export const bindPointToSnapToElementOutline = ( vectorNormalize(vectorFromPoint(edgePoint, adjacentPoint)), pointDistance(edgePoint, adjacentPoint) + Math.max(bindableElement.width, bindableElement.height) + - getFixedBindingDistance(bindableElement) * 2, + getFixedBindingGap(bindableElement) * 2, ); const intersector = customIntersector ?? @@ -1317,7 +1304,7 @@ export const bindPointToSnapToElementOutline = ( bindableElement, elementsMap, intersector, - getFixedBindingDistance(bindableElement), + getFixedBindingGap(bindableElement), ).sort( (g, h) => pointDistanceSq(g, adjacentPoint) - @@ -1346,15 +1333,15 @@ export const avoidRectangularCorner = ( if (nonRotatedPoint[0] < element.x && nonRotatedPoint[1] < element.y) { // Top left - if (nonRotatedPoint[1] - element.y > -getFixedBindingDistance(element)) { + if (nonRotatedPoint[1] - element.y > -getFixedBindingGap(element)) { return pointRotateRads( - pointFrom(element.x - getFixedBindingDistance(element), element.y), + pointFrom(element.x - getFixedBindingGap(element), element.y), center, element.angle, ); } return pointRotateRads( - pointFrom(element.x, element.y - getFixedBindingDistance(element)), + pointFrom(element.x, element.y - getFixedBindingGap(element)), center, element.angle, ); @@ -1363,11 +1350,11 @@ export const avoidRectangularCorner = ( nonRotatedPoint[1] > element.y + element.height ) { // Bottom left - if (nonRotatedPoint[0] - element.x > -getFixedBindingDistance(element)) { + if (nonRotatedPoint[0] - element.x > -getFixedBindingGap(element)) { return pointRotateRads( pointFrom( element.x, - element.y + element.height + getFixedBindingDistance(element), + element.y + element.height + getFixedBindingGap(element), ), center, element.angle, @@ -1375,7 +1362,7 @@ export const avoidRectangularCorner = ( } return pointRotateRads( pointFrom( - element.x - getFixedBindingDistance(element), + element.x - getFixedBindingGap(element), element.y + element.height, ), center, @@ -1388,12 +1375,12 @@ export const avoidRectangularCorner = ( // Bottom right if ( nonRotatedPoint[0] - element.x < - element.width + getFixedBindingDistance(element) + element.width + getFixedBindingGap(element) ) { return pointRotateRads( pointFrom( element.x + element.width, - element.y + element.height + getFixedBindingDistance(element), + element.y + element.height + getFixedBindingGap(element), ), center, element.angle, @@ -1401,7 +1388,7 @@ export const avoidRectangularCorner = ( } return pointRotateRads( pointFrom( - element.x + element.width + getFixedBindingDistance(element), + element.x + element.width + getFixedBindingGap(element), element.y + element.height, ), center, @@ -1414,12 +1401,12 @@ export const avoidRectangularCorner = ( // Top right if ( nonRotatedPoint[0] - element.x < - element.width + getFixedBindingDistance(element) + element.width + getFixedBindingGap(element) ) { return pointRotateRads( pointFrom( element.x + element.width, - element.y - getFixedBindingDistance(element), + element.y - getFixedBindingGap(element), ), center, element.angle, @@ -1427,7 +1414,7 @@ export const avoidRectangularCorner = ( } return pointRotateRads( pointFrom( - element.x + element.width + getFixedBindingDistance(element), + element.x + element.width + getFixedBindingGap(element), element.y, ), center, @@ -1454,7 +1441,7 @@ const snapToMid = ( const horizontalThreshold = clamp(tolerance * width, 5, 80); // Too close to the center makes it hard to resolve direction precisely - if (pointDistance(center, nonRotated) < getFixedBindingDistance(element)) { + if (pointDistance(center, nonRotated) < getFixedBindingGap(element)) { return p; } @@ -1465,7 +1452,7 @@ const snapToMid = ( ) { // LEFT return pointRotateRads( - pointFrom(x - getFixedBindingDistance(element), center[1]), + pointFrom(x - getFixedBindingGap(element), center[1]), center, angle, ); @@ -1476,7 +1463,7 @@ const snapToMid = ( ) { // TOP return pointRotateRads( - pointFrom(center[0], y - getFixedBindingDistance(element)), + pointFrom(center[0], y - getFixedBindingGap(element)), center, angle, ); @@ -1487,7 +1474,7 @@ const snapToMid = ( ) { // RIGHT return pointRotateRads( - pointFrom(x + width + getFixedBindingDistance(element), center[1]), + pointFrom(x + width + getFixedBindingGap(element), center[1]), center, angle, ); @@ -1498,12 +1485,12 @@ const snapToMid = ( ) { // DOWN return pointRotateRads( - pointFrom(center[0], y + height + getFixedBindingDistance(element)), + pointFrom(center[0], y + height + getFixedBindingGap(element)), center, angle, ); } else if (element.type === "diamond") { - const distance = getFixedBindingDistance(element); + const distance = getFixedBindingGap(element); const topLeft = pointFrom( x + width / 4 - distance, y + height / 4 - distance, diff --git a/packages/element/src/elbowArrow.ts b/packages/element/src/elbowArrow.ts index e8b9cbffe0..ad14c65198 100644 --- a/packages/element/src/elbowArrow.ts +++ b/packages/element/src/elbowArrow.ts @@ -26,11 +26,11 @@ import type { AppState } from "@excalidraw/excalidraw/types"; import { bindPointToSnapToElementOutline, - FIXED_BINDING_DISTANCE, + BASE_BINDING_GAP, getHeadingForElbowArrowSnap, getGlobalFixedPointForBindableElement, - getFixedBindingDistance, - maxBindingGap_simple, + getFixedBindingGap, + maxBindingDistance_simple, } from "./binding"; import { distanceToElement } from "./distance"; import { @@ -1304,8 +1304,8 @@ const getElbowArrowData = ( offsetFromHeading( startHeading, arrow.startArrowhead - ? getFixedBindingDistance(hoveredStartElement) * 6 - : getFixedBindingDistance(hoveredStartElement) * 2, + ? getFixedBindingGap(hoveredStartElement) * 6 + : getFixedBindingGap(hoveredStartElement) * 2, 1, ), ) @@ -1317,8 +1317,8 @@ const getElbowArrowData = ( offsetFromHeading( endHeading, arrow.endArrowhead - ? getFixedBindingDistance(hoveredEndElement) * 6 - : getFixedBindingDistance(hoveredEndElement) * 2, + ? getFixedBindingGap(hoveredEndElement) * 6 + : getFixedBindingGap(hoveredEndElement) * 2, 1, ), ) @@ -1365,8 +1365,8 @@ const getElbowArrowData = ( ? 0 : BASE_PADDING - (arrow.startArrowhead - ? FIXED_BINDING_DISTANCE * 6 - : FIXED_BINDING_DISTANCE * 2), + ? BASE_BINDING_GAP * 6 + : BASE_BINDING_GAP * 2), BASE_PADDING, ), boundsOverlap @@ -1381,8 +1381,8 @@ const getElbowArrowData = ( ? 0 : BASE_PADDING - (arrow.endArrowhead - ? FIXED_BINDING_DISTANCE * 6 - : FIXED_BINDING_DISTANCE * 2), + ? BASE_BINDING_GAP * 6 + : BASE_BINDING_GAP * 2), BASE_PADDING, ), boundsOverlap, @@ -2274,8 +2274,7 @@ const getHoveredElement = ( origPoint, elements, elementsMap, - (element) => - maxBindingGap_simple(element, element.width, element.height, zoom), + (element) => maxBindingDistance_simple(zoom), ); }; diff --git a/packages/element/src/utils.ts b/packages/element/src/utils.ts index 42062b5269..673dfefd16 100644 --- a/packages/element/src/utils.ts +++ b/packages/element/src/utils.ts @@ -1,4 +1,5 @@ import { + debugDrawLine, DEFAULT_ADAPTIVE_RADIUS, DEFAULT_PROPORTIONAL_RADIUS, invariant, @@ -598,5 +599,9 @@ export const projectFixedPointOntoDiagonal = ( p = p1 || p2 || null; } + debugDrawLine(diagonalOne, { color: "purple", permanent: false }); + debugDrawLine(diagonalTwo, { color: "purple", permanent: false }); + debugDrawLine(intersector, { color: "orange", permanent: false }); + return p && isPointInElement(p, element, elementsMap) ? p : null; }; diff --git a/packages/element/tests/__snapshots__/linearElementEditor.test.tsx.snap b/packages/element/tests/__snapshots__/linearElementEditor.test.tsx.snap index 67639e5bde..35e940d32e 100644 --- a/packages/element/tests/__snapshots__/linearElementEditor.test.tsx.snap +++ b/packages/element/tests/__snapshots__/linearElementEditor.test.tsx.snap @@ -44,14 +44,3 @@ exports[`Test Linear Elements > Test bound text element > should resize and posi "Online whiteboard collaboration made easy" `; - -exports[`Test Linear Elements > Test bound text element > should wrap the bound text when arrow bound container moves 1`] = ` -"Online whiteboard -collaboration made easy" -`; - -exports[`Test Linear Elements > Test bound text element > should wrap the bound text when arrow bound container moves 2`] = ` -"Online whiteboard -collaboration made -easy" -`; diff --git a/packages/excalidraw/components/App.tsx b/packages/excalidraw/components/App.tsx index d807ff5279..8952a5bad2 100644 --- a/packages/excalidraw/components/App.tsx +++ b/packages/excalidraw/components/App.tsx @@ -246,7 +246,7 @@ import { getElementBounds, doBoundsIntersect, isPointInElement, - maxBindingGap_simple, + maxBindingDistance_simple, } from "@excalidraw/element"; import type { GlobalPoint, LocalPoint, Radians } from "@excalidraw/math"; @@ -6550,7 +6550,7 @@ class App extends React.Component { pointFrom(scenePointerX, scenePointerY), this.scene.getNonDeletedElements(), this.scene.getNonDeletedElementsMap(), - (el) => maxBindingGap_simple(el, el.width, el.height, this.state.zoom), + (el) => maxBindingDistance_simple(this.state.zoom), ); if ( hit && diff --git a/packages/excalidraw/renderer/interactiveScene.ts b/packages/excalidraw/renderer/interactiveScene.ts index 0f18b5f2ca..5ee94a68ad 100644 --- a/packages/excalidraw/renderer/interactiveScene.ts +++ b/packages/excalidraw/renderer/interactiveScene.ts @@ -268,7 +268,7 @@ const renderBindingHighlightForBindableElement_simple = ( context.translate(element.x, element.y); context.lineWidth = - clamp(2.5, element.strokeWidth * 1.75, 4) / + clamp(1.75, element.strokeWidth, 4) / Math.max(0.25, appState.zoom.value); context.strokeStyle = appState.theme === THEME.DARK diff --git a/packages/excalidraw/tests/__snapshots__/history.test.tsx.snap b/packages/excalidraw/tests/__snapshots__/history.test.tsx.snap index 75a755751d..c002cbd459 100644 --- a/packages/excalidraw/tests/__snapshots__/history.test.tsx.snap +++ b/packages/excalidraw/tests/__snapshots__/history.test.tsx.snap @@ -123,7 +123,12 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl { "angle": 0, "backgroundColor": "transparent", - "boundElements": [], + "boundElements": [ + { + "id": "id4", + "type": "arrow", + }, + ], "customData": undefined, "fillStyle": "solid", "frameId": null, @@ -142,7 +147,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "strokeWidth": 2, "type": "rectangle", "updated": 1, - "version": 13, + "version": 7, "width": 100, "x": -100, "y": -50, @@ -153,7 +158,12 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl { "angle": 0, "backgroundColor": "transparent", - "boundElements": [], + "boundElements": [ + { + "id": "id4", + "type": "arrow", + }, + ], "customData": undefined, "fillStyle": "solid", "frameId": null, @@ -172,7 +182,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "strokeWidth": 2, "type": "rectangle", "updated": 1, - "version": 9, + "version": 6, "width": 100, "x": 100, "y": -50, @@ -188,17 +198,17 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "elbowed": false, "endArrowhead": "arrow", "endBinding": { - "elementId": "id15", + "elementId": "id1", "fixedPoint": [ - "0.50000", - 1, + "0.41019", + "0.58981", ], "mode": "orbit", }, "fillStyle": "solid", "frameId": null, "groupIds": [], - "height": "112.79549", + "height": "2.03061", "id": "id4", "index": "a2", "isDeleted": false, @@ -212,8 +222,8 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl 0, ], [ - "94.00000", - "112.79549", + "78.00000", + "-2.03061", ], ], "roughness": 1, @@ -221,16 +231,23 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "type": 2, }, "startArrowhead": null, - "startBinding": null, + "startBinding": { + "elementId": "id0", + "fixedPoint": [ + "0.63636", + "0.63636", + ], + "mode": "orbit", + }, "strokeColor": "#1e1e1e", "strokeStyle": "solid", "strokeWidth": 2, "type": "arrow", "updated": 1, - "version": 34, - "width": "94.00000", - "x": 0, - "y": 0, + "version": 19, + "width": "78.00000", + "x": 11, + "y": "12.36576", } `; @@ -238,12 +255,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl { "angle": 0, "backgroundColor": "transparent", - "boundElements": [ - { - "id": "id4", - "type": "arrow", - }, - ], + "boundElements": [], "customData": undefined, "fillStyle": "solid", "frameId": null, @@ -262,7 +274,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "strokeWidth": 2, "type": "rectangle", "updated": 1, - "version": 10, + "version": 4, "width": 50, "x": 100, "y": 100, @@ -271,214 +283,9 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl exports[`history > multiplayer undo/redo > conflicts in arrows and their bindable elements > should rebind bindings when both are updated through the history and the arrow got bound to a different element in the meantime > [end of test] number of elements 1`] = `4`; -exports[`history > multiplayer undo/redo > conflicts in arrows and their bindable elements > should rebind bindings when both are updated through the history and the arrow got bound to a different element in the meantime > [end of test] number of renders 1`] = `22`; +exports[`history > multiplayer undo/redo > conflicts in arrows and their bindable elements > should rebind bindings when both are updated through the history and the arrow got bound to a different element in the meantime > [end of test] number of renders 1`] = `16`; -exports[`history > multiplayer undo/redo > conflicts in arrows and their bindable elements > should rebind bindings when both are updated through the history and the arrow got bound to a different element in the meantime > [end of test] redo stack 1`] = ` -[ - { - "appState": AppStateDelta { - "delta": Delta { - "deleted": {}, - "inserted": {}, - }, - }, - "elements": { - "added": {}, - "removed": {}, - "updated": { - "id0": { - "deleted": { - "version": 12, - }, - "inserted": { - "version": 11, - }, - }, - "id1": { - "deleted": { - "boundElements": [], - "version": 9, - }, - "inserted": { - "boundElements": [ - { - "id": "id4", - "type": "arrow", - }, - ], - "version": 8, - }, - }, - "id15": { - "deleted": { - "boundElements": [ - { - "id": "id4", - "type": "arrow", - }, - ], - "version": 9, - }, - "inserted": { - "boundElements": [], - "version": 8, - }, - }, - "id4": { - "deleted": { - "endBinding": { - "elementId": "id15", - "fixedPoint": [ - "0.50000", - 1, - ], - "mode": "orbit", - }, - "height": "74.35962", - "points": [ - [ - 0, - 0, - ], - [ - 88, - "74.35962", - ], - ], - "startBinding": { - "elementId": "id0", - "fixedPoint": [ - "0.63636", - "0.63636", - ], - "mode": "orbit", - }, - "version": 33, - "width": 88, - "y": "49.43345", - }, - "inserted": { - "endBinding": { - "elementId": "id1", - "fixedPoint": [ - "0.41067", - "0.58933", - ], - "mode": "orbit", - }, - "height": "2.33211", - "points": [ - [ - 0, - 0, - ], - [ - 88, - "-2.33211", - ], - ], - "startBinding": { - "elementId": "id0", - "fixedPoint": [ - "0.63636", - "0.63636", - ], - "mode": "orbit", - }, - "version": 30, - "width": 88, - "y": "12.51248", - }, - }, - }, - }, - "id": "id22", - }, - { - "appState": AppStateDelta { - "delta": Delta { - "deleted": {}, - "inserted": {}, - }, - }, - "elements": { - "added": {}, - "removed": {}, - "updated": { - "id0": { - "deleted": { - "boundElements": [], - "version": 13, - }, - "inserted": { - "boundElements": [ - { - "id": "id4", - "type": "arrow", - }, - ], - "version": 12, - }, - }, - "id15": { - "deleted": { - "version": 10, - }, - "inserted": { - "version": 9, - }, - }, - "id4": { - "deleted": { - "height": "112.79549", - "points": [ - [ - 0, - 0, - ], - [ - "94.00000", - "112.79549", - ], - ], - "startBinding": null, - "version": 34, - "width": "94.00000", - "x": 0, - "y": 0, - }, - "inserted": { - "height": "74.35962", - "points": [ - [ - 0, - 0, - ], - [ - 88, - "74.35962", - ], - ], - "startBinding": { - "elementId": "id0", - "fixedPoint": [ - "0.63636", - "0.63636", - ], - "mode": "orbit", - }, - "version": 33, - "width": 88, - "x": 6, - "y": "49.43345", - }, - }, - }, - }, - "id": "id23", - }, -] -`; +exports[`history > multiplayer undo/redo > conflicts in arrows and their bindable elements > should rebind bindings when both are updated through the history and the arrow got bound to a different element in the meantime > [end of test] redo stack 1`] = `[]`; exports[`history > multiplayer undo/redo > conflicts in arrows and their bindable elements > should rebind bindings when both are updated through the history and the arrow got bound to a different element in the meantime > [end of test] undo stack 1`] = ` [ @@ -633,6 +440,208 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl }, "id": "id6", }, + { + "appState": AppStateDelta { + "delta": Delta { + "deleted": {}, + "inserted": {}, + }, + }, + "elements": { + "added": {}, + "removed": {}, + "updated": { + "id0": { + "deleted": { + "boundElements": [ + { + "id": "id4", + "type": "arrow", + }, + ], + "version": 6, + }, + "inserted": { + "boundElements": [], + "version": 5, + }, + }, + "id15": { + "deleted": { + "version": 3, + }, + "inserted": { + "version": 2, + }, + }, + "id4": { + "deleted": { + "height": "57.11798", + "points": [ + [ + 0, + 0, + ], + [ + 78, + "57.11798", + ], + ], + "startBinding": { + "elementId": "id0", + "fixedPoint": [ + "0.63636", + "0.63636", + ], + "mode": "orbit", + }, + "version": 16, + "width": 78, + "x": 11, + "y": "48.31989", + }, + "inserted": { + "height": 0, + "points": [ + [ + 0, + 0, + ], + [ + 100, + 0, + ], + ], + "startBinding": null, + "version": 13, + "width": 100, + "x": 0, + "y": 0, + }, + }, + }, + }, + "id": "id16", + }, + { + "appState": AppStateDelta { + "delta": Delta { + "deleted": {}, + "inserted": {}, + }, + }, + "elements": { + "added": {}, + "removed": {}, + "updated": { + "id0": { + "deleted": { + "version": 7, + }, + "inserted": { + "version": 6, + }, + }, + "id1": { + "deleted": { + "boundElements": [ + { + "id": "id4", + "type": "arrow", + }, + ], + "version": 6, + }, + "inserted": { + "boundElements": [], + "version": 5, + }, + }, + "id15": { + "deleted": { + "boundElements": [], + "version": 4, + }, + "inserted": { + "boundElements": [ + { + "id": "id4", + "type": "arrow", + }, + ], + "version": 3, + }, + }, + "id4": { + "deleted": { + "endBinding": { + "elementId": "id1", + "fixedPoint": [ + "0.41019", + "0.58981", + ], + "mode": "orbit", + }, + "height": "2.03061", + "points": [ + [ + 0, + 0, + ], + [ + "78.00000", + "-2.03061", + ], + ], + "startBinding": { + "elementId": "id0", + "fixedPoint": [ + "0.63636", + "0.63636", + ], + "mode": "orbit", + }, + "version": 19, + "width": "78.00000", + "y": "12.36576", + }, + "inserted": { + "endBinding": { + "elementId": "id15", + "fixedPoint": [ + "0.50000", + 1, + ], + "mode": "orbit", + }, + "height": "57.11798", + "points": [ + [ + 0, + 0, + ], + [ + 78, + "57.11798", + ], + ], + "startBinding": { + "elementId": "id0", + "fixedPoint": [ + "0.63636", + "0.63636", + ], + "mode": "orbit", + }, + "version": 16, + "width": 78, + "y": "48.31989", + }, + }, + }, + }, + "id": "id17", + }, ] `; @@ -759,7 +768,12 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl { "angle": 0, "backgroundColor": "transparent", - "boundElements": [], + "boundElements": [ + { + "id": "id4", + "type": "arrow", + }, + ], "customData": undefined, "fillStyle": "solid", "frameId": null, @@ -778,7 +792,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "strokeWidth": 2, "type": "rectangle", "updated": 1, - "version": 14, + "version": 8, "width": 100, "x": 150, "y": -50, @@ -789,7 +803,12 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl { "angle": 0, "backgroundColor": "transparent", - "boundElements": [], + "boundElements": [ + { + "id": "id4", + "type": "arrow", + }, + ], "customData": undefined, "fillStyle": "solid", "frameId": null, @@ -808,7 +827,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "strokeWidth": 2, "type": "rectangle", "updated": 1, - "version": 9, + "version": 6, "width": 100, "x": 150, "y": -50, @@ -823,11 +842,18 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "customData": undefined, "elbowed": false, "endArrowhead": "arrow", - "endBinding": null, + "endBinding": { + "elementId": "id1", + "fixedPoint": [ + "0.41092", + "0.58908", + ], + "mode": "orbit", + }, "fillStyle": "solid", "frameId": null, "groupIds": [], - "height": 0, + "height": "10.92646", "id": "id4", "index": "a2", "isDeleted": false, @@ -841,8 +867,8 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl 0, ], [ - 100, - 0, + "52.09230", + "10.92646", ], ], "roughness": 1, @@ -850,199 +876,31 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "type": 2, }, "startArrowhead": null, - "startBinding": null, + "startBinding": { + "elementId": "id0", + "fixedPoint": [ + "0.63636", + "0.63636", + ], + "mode": "orbit", + }, "strokeColor": "#1e1e1e", "strokeStyle": "solid", "strokeWidth": 2, "type": "arrow", "updated": 1, - "version": 26, - "width": 100, - "x": 150, - "y": 0, + "version": 20, + "width": "52.09230", + "x": "139.00000", + "y": "-2.01876", } `; exports[`history > multiplayer undo/redo > conflicts in arrows and their bindable elements > should rebind bindings when both are updated through the history and there are no conflicting updates in the meantime > [end of test] number of elements 1`] = `3`; -exports[`history > multiplayer undo/redo > conflicts in arrows and their bindable elements > should rebind bindings when both are updated through the history and there are no conflicting updates in the meantime > [end of test] number of renders 1`] = `24`; +exports[`history > multiplayer undo/redo > conflicts in arrows and their bindable elements > should rebind bindings when both are updated through the history and there are no conflicting updates in the meantime > [end of test] number of renders 1`] = `18`; -exports[`history > multiplayer undo/redo > conflicts in arrows and their bindable elements > should rebind bindings when both are updated through the history and there are no conflicting updates in the meantime > [end of test] redo stack 1`] = ` -[ - { - "appState": AppStateDelta { - "delta": Delta { - "deleted": {}, - "inserted": {}, - }, - }, - "elements": { - "added": {}, - "removed": {}, - "updated": { - "id0": { - "deleted": { - "version": 13, - }, - "inserted": { - "version": 12, - }, - }, - "id1": { - "deleted": { - "boundElements": [], - "version": 9, - }, - "inserted": { - "boundElements": [ - { - "id": "id4", - "type": "arrow", - }, - ], - "version": 8, - }, - }, - "id4": { - "deleted": { - "endBinding": null, - "height": "5.28000", - "points": [ - [ - 0, - 0, - ], - [ - -44, - "-5.28000", - ], - ], - "startBinding": { - "elementId": "id0", - "fixedPoint": [ - "0.63636", - "0.63636", - ], - "mode": "orbit", - }, - "version": 25, - "width": 44, - "y": "5.28000", - }, - "inserted": { - "endBinding": { - "elementId": "id1", - "fixedPoint": [ - "0.41095", - "0.58905", - ], - "mode": "orbit", - }, - "height": "9.88589", - "points": [ - [ - 0, - 0, - ], - [ - "47.09529", - "9.88589", - ], - ], - "startBinding": { - "elementId": "id0", - "fixedPoint": [ - "0.63636", - "0.63636", - ], - "mode": "orbit", - }, - "version": 24, - "width": "47.09529", - "y": "-0.98118", - }, - }, - }, - }, - "id": "id21", - }, - { - "appState": AppStateDelta { - "delta": Delta { - "deleted": {}, - "inserted": {}, - }, - }, - "elements": { - "added": {}, - "removed": {}, - "updated": { - "id0": { - "deleted": { - "boundElements": [], - "version": 14, - }, - "inserted": { - "boundElements": [ - { - "id": "id4", - "type": "arrow", - }, - ], - "version": 13, - }, - }, - "id4": { - "deleted": { - "height": 0, - "points": [ - [ - 0, - 0, - ], - [ - 100, - 0, - ], - ], - "startBinding": null, - "version": 26, - "width": 100, - "x": 150, - "y": 0, - }, - "inserted": { - "height": "5.28000", - "points": [ - [ - 0, - 0, - ], - [ - -44, - "-5.28000", - ], - ], - "startBinding": { - "elementId": "id0", - "fixedPoint": [ - "0.63636", - "0.63636", - ], - "mode": "orbit", - }, - "version": 25, - "width": 44, - "x": 144, - "y": "5.28000", - }, - }, - }, - }, - "id": "id22", - }, -] -`; +exports[`history > multiplayer undo/redo > conflicts in arrows and their bindable elements > should rebind bindings when both are updated through the history and there are no conflicting updates in the meantime > [end of test] redo stack 1`] = `[]`; exports[`history > multiplayer undo/redo > conflicts in arrows and their bindable elements > should rebind bindings when both are updated through the history and there are no conflicting updates in the meantime > [end of test] undo stack 1`] = ` [ @@ -1197,6 +1055,178 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl }, "id": "id6", }, + { + "appState": AppStateDelta { + "delta": Delta { + "deleted": {}, + "inserted": {}, + }, + }, + "elements": { + "added": {}, + "removed": {}, + "updated": { + "id0": { + "deleted": { + "boundElements": [ + { + "id": "id4", + "type": "arrow", + }, + ], + "version": 7, + }, + "inserted": { + "boundElements": [], + "version": 6, + }, + }, + "id4": { + "deleted": { + "height": "4.68000", + "points": [ + [ + 0, + 0, + ], + [ + -39, + "-4.68000", + ], + ], + "startBinding": { + "elementId": "id0", + "fixedPoint": [ + "0.63636", + "0.63636", + ], + "mode": "orbit", + }, + "version": 17, + "width": 39, + "x": 139, + "y": "4.68000", + }, + "inserted": { + "height": 0, + "points": [ + [ + 0, + 0, + ], + [ + 100, + 0, + ], + ], + "startBinding": null, + "version": 15, + "width": 100, + "x": 150, + "y": 0, + }, + }, + }, + }, + "id": "id15", + }, + { + "appState": AppStateDelta { + "delta": Delta { + "deleted": {}, + "inserted": {}, + }, + }, + "elements": { + "added": {}, + "removed": {}, + "updated": { + "id0": { + "deleted": { + "version": 8, + }, + "inserted": { + "version": 7, + }, + }, + "id1": { + "deleted": { + "boundElements": [ + { + "id": "id4", + "type": "arrow", + }, + ], + "version": 6, + }, + "inserted": { + "boundElements": [], + "version": 5, + }, + }, + "id4": { + "deleted": { + "endBinding": { + "elementId": "id1", + "fixedPoint": [ + "0.41092", + "0.58908", + ], + "mode": "orbit", + }, + "height": "10.92646", + "points": [ + [ + 0, + 0, + ], + [ + "52.09230", + "10.92646", + ], + ], + "startBinding": { + "elementId": "id0", + "fixedPoint": [ + "0.63636", + "0.63636", + ], + "mode": "orbit", + }, + "version": 20, + "width": "52.09230", + "y": "-2.01876", + }, + "inserted": { + "endBinding": null, + "height": "4.68000", + "points": [ + [ + 0, + 0, + ], + [ + -39, + "-4.68000", + ], + ], + "startBinding": { + "elementId": "id0", + "fixedPoint": [ + "0.63636", + "0.63636", + ], + "mode": "orbit", + }, + "version": 17, + "width": 39, + "y": "4.68000", + }, + }, + }, + }, + "id": "id16", + }, ] `; @@ -1337,7 +1367,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "fillStyle": "solid", "frameId": null, "groupIds": [], - "height": "29.36414", + "height": "26.16768", "id": "id4", "index": "Zz", "isDeleted": false, @@ -1351,8 +1381,8 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl 0, ], [ - 88, - "29.36414", + "78.00000", + "26.16768", ], ], "roughness": 1, @@ -1372,9 +1402,9 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "type": "arrow", "updated": 1, "version": 10, - "width": 88, - "x": 6, - "y": "2.00946", + "width": "78.00000", + "x": 11, + "y": "3.67566", } `; @@ -1700,7 +1730,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "fillStyle": "solid", "frameId": null, "groupIds": [], - "height": "14.91372", + "height": "10.76674", "id": "id5", "index": "a0", "isDeleted": false, @@ -1714,8 +1744,8 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl 0, ], [ - "88.00000", - "-14.91372", + "78.00000", + "-10.76674", ], ], "roughness": 1, @@ -1735,9 +1765,9 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "type": "arrow", "updated": 1, "version": 11, - "width": "88.00000", - "x": 6, - "y": "37.05219", + "width": "78.00000", + "x": 11, + "y": "35.29320", } `; @@ -1848,7 +1878,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "fillStyle": "solid", "frameId": null, "groupIds": [], - "height": "14.91372", + "height": "10.76674", "index": "a0", "isDeleted": false, "link": null, @@ -1860,8 +1890,8 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl 0, ], [ - "88.00000", - "-14.91372", + "78.00000", + "-10.76674", ], ], "roughness": 1, @@ -1880,9 +1910,9 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "strokeWidth": 2, "type": "arrow", "version": 11, - "width": "88.00000", - "x": 6, - "y": "37.05219", + "width": "78.00000", + "x": 11, + "y": "35.29320", }, "inserted": { "isDeleted": true, @@ -2400,7 +2430,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "fillStyle": "solid", "frameId": null, "groupIds": [], - "height": "362.12154", + "height": "353.93931", "id": "id4", "index": "a2", "isDeleted": false, @@ -2414,8 +2444,8 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl 0, ], [ - "488.00000", - "-362.12154", + "478.03878", + "-353.93931", ], ], "roughness": 1, @@ -2437,9 +2467,9 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "type": "arrow", "updated": 1, "version": 12, - "width": "488.00000", - "x": 6, - "y": "-41.53751", + "width": "478.03878", + "x": 11, + "y": "-45.14692", } `; @@ -2568,7 +2598,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "fillStyle": "solid", "frameId": null, "groupIds": [], - "height": "362.12154", + "height": "353.93931", "index": "a2", "isDeleted": false, "link": null, @@ -2580,8 +2610,8 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl 0, ], [ - "488.00000", - "-362.12154", + "478.03878", + "-353.93931", ], ], "roughness": 1, @@ -2602,9 +2632,9 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "strokeWidth": 2, "type": "arrow", "version": 12, - "width": "488.00000", - "x": 6, - "y": "-41.53751", + "width": "478.03878", + "x": 11, + "y": "-45.14692", }, "inserted": { "isDeleted": true, @@ -16385,7 +16415,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "fillStyle": "solid", "frameId": null, "groupIds": [], - "height": 0, + "height": "0.00000", "id": "id13", "index": "a3", "isDeleted": false, @@ -16399,8 +16429,8 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding 0, ], [ - 88, - 0, + "78.00000", + "-0.00000", ], ], "roughness": 1, @@ -16422,8 +16452,8 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "type": "arrow", "updated": 1, "version": 11, - "width": 88, - "x": 6, + "width": "78.00000", + "x": 11, "y": "0.01000", } `; @@ -16815,7 +16845,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding 0, ], [ - 88, + "78.00000", 0, ], ], @@ -16837,8 +16867,8 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "strokeWidth": 2, "type": "arrow", "version": 8, - "width": 88, - "x": 6, + "width": "78.00000", + "x": 11, "y": "0.01000", }, "inserted": { @@ -17136,7 +17166,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "fillStyle": "solid", "frameId": null, "groupIds": [], - "height": 0, + "height": "0.00000", "id": "id13", "index": "a3", "isDeleted": false, @@ -17150,8 +17180,8 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding 0, ], [ - 88, - 0, + "78.00000", + "-0.00000", ], ], "roughness": 1, @@ -17173,8 +17203,8 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "type": "arrow", "updated": 1, "version": 11, - "width": 88, - "x": 6, + "width": "78.00000", + "x": 11, "y": "0.01000", } `; @@ -17444,7 +17474,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "fillStyle": "solid", "frameId": null, "groupIds": [], - "height": 0, + "height": "0.00000", "index": "a3", "isDeleted": false, "link": null, @@ -17456,8 +17486,8 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding 0, ], [ - 88, - 0, + "78.00000", + "-0.00000", ], ], "roughness": 1, @@ -17478,8 +17508,8 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "strokeWidth": 2, "type": "arrow", "version": 11, - "width": 88, - "x": 6, + "width": "78.00000", + "x": 11, "y": "0.01000", }, "inserted": { @@ -17785,7 +17815,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "fillStyle": "solid", "frameId": null, "groupIds": [], - "height": 0, + "height": "0.00000", "id": "id13", "index": "a3", "isDeleted": false, @@ -17799,8 +17829,8 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding 0, ], [ - 88, - 0, + "78.00000", + "-0.00000", ], ], "roughness": 1, @@ -17822,8 +17852,8 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "type": "arrow", "updated": 1, "version": 11, - "width": 88, - "x": 6, + "width": "78.00000", + "x": 11, "y": "0.01000", } `; @@ -18093,7 +18123,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "fillStyle": "solid", "frameId": null, "groupIds": [], - "height": 0, + "height": "0.00000", "index": "a3", "isDeleted": false, "link": null, @@ -18105,8 +18135,8 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding 0, ], [ - 88, - 0, + "78.00000", + "-0.00000", ], ], "roughness": 1, @@ -18127,8 +18157,8 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "strokeWidth": 2, "type": "arrow", "version": 11, - "width": 88, - "x": 6, + "width": "78.00000", + "x": 11, "y": "0.01000", }, "inserted": { @@ -18432,7 +18462,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "fillStyle": "solid", "frameId": null, "groupIds": [], - "height": 0, + "height": "0.00000", "id": "id13", "index": "a3", "isDeleted": false, @@ -18446,8 +18476,8 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding 0, ], [ - 88, - 0, + "78.00000", + "-0.00000", ], ], "roughness": 1, @@ -18469,8 +18499,8 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "type": "arrow", "updated": 1, "version": 11, - "width": 88, - "x": 6, + "width": "78.00000", + "x": 11, "y": "0.01000", } `; @@ -18838,7 +18868,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding 0, ], [ - 88, + "78.00000", 0, ], ], @@ -18860,8 +18890,8 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "strokeWidth": 2, "type": "arrow", "version": 8, - "width": 88, - "x": 6, + "width": "78.00000", + "x": 11, "y": "0.01000", }, "inserted": { @@ -19187,7 +19217,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "fillStyle": "solid", "frameId": null, "groupIds": [], - "height": 0, + "height": "0.00000", "id": "id13", "index": "a3", "isDeleted": false, @@ -19201,8 +19231,8 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding 0, ], [ - 88, - 0, + "78.00000", + "-0.00000", ], ], "roughness": 1, @@ -19224,8 +19254,8 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "type": "arrow", "updated": 1, "version": 12, - "width": 88, - "x": 6, + "width": "78.00000", + "x": 11, "y": "0.01000", } `; @@ -19589,7 +19619,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding 0, ], [ - 88, + "78.00000", 0, ], ], @@ -19611,8 +19641,8 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "strokeWidth": 2, "type": "arrow", "version": 8, - "width": 88, - "x": 6, + "width": "78.00000", + "x": 11, "y": "0.01000", }, "inserted": { diff --git a/packages/excalidraw/tests/__snapshots__/move.test.tsx.snap b/packages/excalidraw/tests/__snapshots__/move.test.tsx.snap index 16f49c8210..556a41c35b 100644 --- a/packages/excalidraw/tests/__snapshots__/move.test.tsx.snap +++ b/packages/excalidraw/tests/__snapshots__/move.test.tsx.snap @@ -95,141 +95,3 @@ exports[`move element > rectangle 5`] = ` "y": 40, } `; - -exports[`move element > rectangles with binding arrow 5`] = ` -{ - "angle": 0, - "backgroundColor": "transparent", - "boundElements": [ - { - "id": "id6", - "type": "arrow", - }, - ], - "customData": undefined, - "fillStyle": "solid", - "frameId": null, - "groupIds": [], - "height": 100, - "id": "id0", - "index": "a0", - "isDeleted": false, - "link": null, - "locked": false, - "opacity": 100, - "roughness": 1, - "roundness": null, - "seed": 1278240551, - "strokeColor": "#1e1e1e", - "strokeStyle": "solid", - "strokeWidth": 2, - "type": "rectangle", - "updated": 1, - "version": 4, - "versionNonce": 760410951, - "width": 100, - "x": 0, - "y": 0, -} -`; - -exports[`move element > rectangles with binding arrow 6`] = ` -{ - "angle": 0, - "backgroundColor": "transparent", - "boundElements": [ - { - "id": "id6", - "type": "arrow", - }, - ], - "customData": undefined, - "fillStyle": "solid", - "frameId": null, - "groupIds": [], - "height": 300, - "id": "id3", - "index": "a1", - "isDeleted": false, - "link": null, - "locked": false, - "opacity": 100, - "roughness": 1, - "roundness": null, - "seed": 1116226695, - "strokeColor": "#1e1e1e", - "strokeStyle": "solid", - "strokeWidth": 2, - "type": "rectangle", - "updated": 1, - "version": 7, - "versionNonce": 651223591, - "width": 300, - "x": 201, - "y": 2, -} -`; - -exports[`move element > rectangles with binding arrow 7`] = ` -{ - "angle": 0, - "backgroundColor": "transparent", - "boundElements": null, - "customData": undefined, - "elbowed": false, - "endArrowhead": "arrow", - "endBinding": { - "elementId": "id3", - "fixedPoint": [ - "-0.02000", - "0.44666", - ], - "mode": "orbit", - }, - "fillStyle": "solid", - "frameId": null, - "groupIds": [], - "height": "89.98900", - "id": "id6", - "index": "a2", - "isDeleted": false, - "link": null, - "locked": false, - "moveMidPointsWithElement": false, - "opacity": 100, - "points": [ - [ - 0, - 0, - ], - [ - "89.00000", - "89.98900", - ], - ], - "roughness": 1, - "roundness": { - "type": 2, - }, - "seed": 23633383, - "startArrowhead": null, - "startBinding": { - "elementId": "id0", - "fixedPoint": [ - "1.06000", - "0.46011", - ], - "mode": "orbit", - }, - "strokeColor": "#1e1e1e", - "strokeStyle": "solid", - "strokeWidth": 2, - "type": "arrow", - "updated": 1, - "version": 14, - "versionNonce": 348321737, - "width": "89.00000", - "x": 106, - "y": "46.01050", -} -`;