Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4d0cb0262f | |||
| f49c931b7e | |||
| 193386003f | |||
| 9a3795ec2c | |||
| 5834e9f11f | |||
| 50749b8119 | |||
| 3b0a6af46f | |||
| 3b99b7aba2 | |||
| 84f087633e |
@@ -27,6 +27,7 @@ import type {
|
|||||||
ElementsMap,
|
ElementsMap,
|
||||||
ExcalidrawArrowElement,
|
ExcalidrawArrowElement,
|
||||||
ExcalidrawBindableElement,
|
ExcalidrawBindableElement,
|
||||||
|
FixedPointBinding,
|
||||||
NonDeletedSceneElementsMap,
|
NonDeletedSceneElementsMap,
|
||||||
PointsPositionUpdates,
|
PointsPositionUpdates,
|
||||||
} from "../types";
|
} from "../types";
|
||||||
@@ -110,10 +111,17 @@ const focusPointUpdate = (
|
|||||||
) => {
|
) => {
|
||||||
const pointUpdates = new Map();
|
const pointUpdates = new Map();
|
||||||
|
|
||||||
|
const originalAdjacentBinding =
|
||||||
|
appState.selectedLinearElement?.initialState
|
||||||
|
.arrowOtherEndpointInitialBinding;
|
||||||
const bindingField = isStartBinding ? "startBinding" : "endBinding";
|
const bindingField = isStartBinding ? "startBinding" : "endBinding";
|
||||||
const adjacentBindingField = isStartBinding ? "endBinding" : "startBinding";
|
const adjacentBindingField = isStartBinding ? "endBinding" : "startBinding";
|
||||||
let currentBinding = arrow[bindingField];
|
let currentBinding = arrow[bindingField];
|
||||||
let adjacentBinding = arrow[adjacentBindingField];
|
let adjacentBinding =
|
||||||
|
originalAdjacentBinding?.mode === "orbit" &&
|
||||||
|
arrow[adjacentBindingField]?.mode === "inside"
|
||||||
|
? originalAdjacentBinding
|
||||||
|
: arrow[adjacentBindingField];
|
||||||
|
|
||||||
// Update the dragged focus point related end
|
// Update the dragged focus point related end
|
||||||
if (currentBinding && bindableElement) {
|
if (currentBinding && bindableElement) {
|
||||||
@@ -163,7 +171,7 @@ const focusPointUpdate = (
|
|||||||
// Same shape bound on both ends
|
// Same shape bound on both ends
|
||||||
const boundToSameElementAfterUpdate =
|
const boundToSameElementAfterUpdate =
|
||||||
bindableElement && adjacentBinding.elementId === bindableElement.id;
|
bindableElement && adjacentBinding.elementId === bindableElement.id;
|
||||||
if (switchToInsideBinding || boundToSameElementAfterUpdate) {
|
if (boundToSameElementAfterUpdate) {
|
||||||
adjacentBinding = {
|
adjacentBinding = {
|
||||||
...adjacentBinding,
|
...adjacentBinding,
|
||||||
mode: "inside",
|
mode: "inside",
|
||||||
@@ -339,6 +347,7 @@ export const handleFocusPointPointerDown = (
|
|||||||
): {
|
): {
|
||||||
hitFocusPoint: "start" | "end" | null;
|
hitFocusPoint: "start" | "end" | null;
|
||||||
pointerOffset: { x: number; y: number };
|
pointerOffset: { x: number; y: number };
|
||||||
|
arrowOtherEndpointInitialBinding: FixedPointBinding | null;
|
||||||
} => {
|
} => {
|
||||||
const pointerPos = pointFrom(
|
const pointerPos = pointFrom(
|
||||||
pointerDownState.origin.x,
|
pointerDownState.origin.x,
|
||||||
@@ -376,6 +385,7 @@ export const handleFocusPointPointerDown = (
|
|||||||
x: pointerPos[0] - focusPoint[0],
|
x: pointerPos[0] - focusPoint[0],
|
||||||
y: pointerPos[1] - focusPoint[1],
|
y: pointerPos[1] - focusPoint[1],
|
||||||
},
|
},
|
||||||
|
arrowOtherEndpointInitialBinding: arrow.endBinding,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -411,6 +421,7 @@ export const handleFocusPointPointerDown = (
|
|||||||
x: pointerPos[0] - focusPoint[0],
|
x: pointerPos[0] - focusPoint[0],
|
||||||
y: pointerPos[1] - focusPoint[1],
|
y: pointerPos[1] - focusPoint[1],
|
||||||
},
|
},
|
||||||
|
arrowOtherEndpointInitialBinding: arrow.startBinding,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -419,13 +430,14 @@ export const handleFocusPointPointerDown = (
|
|||||||
return {
|
return {
|
||||||
hitFocusPoint: null,
|
hitFocusPoint: null,
|
||||||
pointerOffset: { x: 0, y: 0 },
|
pointerOffset: { x: 0, y: 0 },
|
||||||
|
arrowOtherEndpointInitialBinding: null,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const handleFocusPointPointerUp = (
|
export const handleFocusPointPointerUp = (
|
||||||
linearElementEditor: LinearElementEditor,
|
linearElementEditor: LinearElementEditor,
|
||||||
scene: Scene,
|
scene: Scene,
|
||||||
) => {
|
): { arrowOtherEndpointInitialBinding: FixedPointBinding | null } => {
|
||||||
invariant(
|
invariant(
|
||||||
linearElementEditor.draggedFocusPointBinding,
|
linearElementEditor.draggedFocusPointBinding,
|
||||||
"Must have a dragged focus point at pointer release",
|
"Must have a dragged focus point at pointer release",
|
||||||
@@ -483,6 +495,10 @@ export const handleFocusPointPointerUp = (
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
arrowOtherEndpointInitialBinding: null,
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const handleFocusPointHover = (
|
export const handleFocusPointHover = (
|
||||||
|
|||||||
@@ -694,6 +694,7 @@ const getBindingStrategyForDraggingBindingElementEndpoints_simple = (
|
|||||||
localPoint,
|
localPoint,
|
||||||
elementsMap,
|
elementsMap,
|
||||||
);
|
);
|
||||||
|
|
||||||
const hit = getHoveredElementForBinding(
|
const hit = getHoveredElementForBinding(
|
||||||
globalPoint,
|
globalPoint,
|
||||||
elements,
|
elements,
|
||||||
@@ -748,6 +749,15 @@ const getBindingStrategyForDraggingBindingElementEndpoints_simple = (
|
|||||||
: // NOTE: Can only affect the start point because new arrows always drag the end point
|
: // NOTE: Can only affect the start point because new arrows always drag the end point
|
||||||
opts?.newArrow
|
opts?.newArrow
|
||||||
? appState.selectedLinearElement!.initialState.origin!
|
? appState.selectedLinearElement!.initialState.origin!
|
||||||
|
: otherBindableElement &&
|
||||||
|
appState.selectedLinearElement?.initialState
|
||||||
|
?.arrowOtherEndpointInitialBinding?.fixedPoint
|
||||||
|
? getGlobalFixedPointForBindableElement(
|
||||||
|
appState.selectedLinearElement?.initialState
|
||||||
|
.arrowOtherEndpointInitialBinding.fixedPoint,
|
||||||
|
otherBindableElement,
|
||||||
|
elementsMap,
|
||||||
|
)
|
||||||
: LinearElementEditor.getPointAtIndexGlobalCoordinates(
|
: LinearElementEditor.getPointAtIndexGlobalCoordinates(
|
||||||
arrow,
|
arrow,
|
||||||
0,
|
0,
|
||||||
@@ -759,6 +769,15 @@ const getBindingStrategyForDraggingBindingElementEndpoints_simple = (
|
|||||||
element: hit,
|
element: hit,
|
||||||
focusPoint: endDragged
|
focusPoint: endDragged
|
||||||
? globalPoint
|
? globalPoint
|
||||||
|
: otherBindableElement &&
|
||||||
|
appState.selectedLinearElement?.initialState
|
||||||
|
?.arrowOtherEndpointInitialBinding?.fixedPoint
|
||||||
|
? getGlobalFixedPointForBindableElement(
|
||||||
|
appState.selectedLinearElement?.initialState
|
||||||
|
.arrowOtherEndpointInitialBinding.fixedPoint,
|
||||||
|
otherBindableElement,
|
||||||
|
elementsMap,
|
||||||
|
)
|
||||||
: LinearElementEditor.getPointAtIndexGlobalCoordinates(
|
: LinearElementEditor.getPointAtIndexGlobalCoordinates(
|
||||||
arrow,
|
arrow,
|
||||||
-1,
|
-1,
|
||||||
@@ -831,36 +850,57 @@ const getBindingStrategyForDraggingBindingElementEndpoints_simple = (
|
|||||||
threshold: maxBindingDistance_simple(appState.zoom),
|
threshold: maxBindingDistance_simple(appState.zoom),
|
||||||
overrideShouldTestInside: true,
|
overrideShouldTestInside: true,
|
||||||
});
|
});
|
||||||
|
const otherPointWasInsideAtStart =
|
||||||
|
appState.selectedLinearElement?.initialState
|
||||||
|
.arrowOtherEndpointInitialBinding?.mode === "inside";
|
||||||
const otherNeverOverride = opts?.newArrow
|
const otherNeverOverride = opts?.newArrow
|
||||||
? appState.selectedLinearElement?.initialState.arrowStartIsInside
|
? appState.selectedLinearElement?.initialState.arrowStartIsInside
|
||||||
: otherBinding?.mode === "inside";
|
: otherBinding?.mode === "inside" && otherPointWasInsideAtStart;
|
||||||
const other: BindingStrategy = !otherNeverOverride
|
|
||||||
? otherBindableElement &&
|
let other: BindingStrategy = { mode: undefined };
|
||||||
|
if (!otherNeverOverride) {
|
||||||
|
if (
|
||||||
|
otherBinding?.mode === "inside" &&
|
||||||
|
!otherPointWasInsideAtStart &&
|
||||||
|
otherBindableElement
|
||||||
|
) {
|
||||||
|
other = {
|
||||||
|
mode: "orbit",
|
||||||
|
element: otherBindableElement,
|
||||||
|
focusPoint: getGlobalFixedPointForBindableElement(
|
||||||
|
otherBinding.fixedPoint,
|
||||||
|
otherBindableElement,
|
||||||
|
elementsMap,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
} else if (
|
||||||
|
otherBindableElement &&
|
||||||
!otherFocusPointIsInElement &&
|
!otherFocusPointIsInElement &&
|
||||||
!pointIsCloseToOtherElement &&
|
!pointIsCloseToOtherElement &&
|
||||||
appState.selectedLinearElement?.initialState.altFocusPoint
|
appState.selectedLinearElement?.initialState.altFocusPoint
|
||||||
? {
|
) {
|
||||||
mode: "orbit",
|
other = {
|
||||||
element: otherBindableElement,
|
mode: "orbit",
|
||||||
focusPoint: appState.selectedLinearElement.initialState.altFocusPoint,
|
element: otherBindableElement,
|
||||||
}
|
focusPoint: appState.selectedLinearElement.initialState.altFocusPoint,
|
||||||
: opts?.angleLocked && otherBindableElement
|
};
|
||||||
? {
|
} else if (opts?.angleLocked && otherBindableElement) {
|
||||||
mode: "orbit",
|
other = {
|
||||||
element: otherBindableElement,
|
mode: "orbit",
|
||||||
focusPoint:
|
element: otherBindableElement,
|
||||||
projectFixedPointOntoDiagonal(
|
focusPoint:
|
||||||
arrow,
|
projectFixedPointOntoDiagonal(
|
||||||
otherEndpoint,
|
arrow,
|
||||||
otherBindableElement,
|
otherEndpoint,
|
||||||
startDragged ? "end" : "start",
|
otherBindableElement,
|
||||||
elementsMap,
|
startDragged ? "end" : "start",
|
||||||
appState.zoom,
|
elementsMap,
|
||||||
appState.isMidpointSnappingEnabled,
|
appState.zoom,
|
||||||
) || otherEndpoint,
|
appState.isMidpointSnappingEnabled,
|
||||||
}
|
) || otherEndpoint,
|
||||||
: { mode: undefined }
|
};
|
||||||
: { mode: undefined };
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
start: startDragged ? current : other,
|
start: startDragged ? current : other,
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ export class LinearElementEditor {
|
|||||||
};
|
};
|
||||||
arrowStartIsInside: boolean;
|
arrowStartIsInside: boolean;
|
||||||
altFocusPoint: Readonly<GlobalPoint> | null;
|
altFocusPoint: Readonly<GlobalPoint> | null;
|
||||||
|
arrowOtherEndpointInitialBinding: FixedPointBinding | null;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
/** whether you're dragging a point */
|
/** whether you're dragging a point */
|
||||||
@@ -193,6 +194,7 @@ export class LinearElementEditor {
|
|||||||
added: false,
|
added: false,
|
||||||
},
|
},
|
||||||
arrowStartIsInside: false,
|
arrowStartIsInside: false,
|
||||||
|
arrowOtherEndpointInitialBinding: null,
|
||||||
altFocusPoint: null,
|
altFocusPoint: null,
|
||||||
};
|
};
|
||||||
this.hoverPointIndex = -1;
|
this.hoverPointIndex = -1;
|
||||||
@@ -764,6 +766,7 @@ export class LinearElementEditor {
|
|||||||
...editingLinearElement.initialState,
|
...editingLinearElement.initialState,
|
||||||
origin: null,
|
origin: null,
|
||||||
arrowStartIsInside: false,
|
arrowStartIsInside: false,
|
||||||
|
arrowOtherEndpointInitialBinding: null,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -1085,6 +1088,7 @@ export class LinearElementEditor {
|
|||||||
!!app.state.newElement &&
|
!!app.state.newElement &&
|
||||||
(app.state.bindMode === "inside" || app.state.bindMode === "skip"),
|
(app.state.bindMode === "inside" || app.state.bindMode === "skip"),
|
||||||
altFocusPoint: null,
|
altFocusPoint: null,
|
||||||
|
arrowOtherEndpointInitialBinding: element.startBinding,
|
||||||
},
|
},
|
||||||
selectedPointsIndices: [element.points.length - 1],
|
selectedPointsIndices: [element.points.length - 1],
|
||||||
lastUncommittedPoint: null,
|
lastUncommittedPoint: null,
|
||||||
@@ -1147,6 +1151,9 @@ export class LinearElementEditor {
|
|||||||
!!app.state.newElement &&
|
!!app.state.newElement &&
|
||||||
(app.state.bindMode === "inside" || app.state.bindMode === "skip"),
|
(app.state.bindMode === "inside" || app.state.bindMode === "skip"),
|
||||||
altFocusPoint: null,
|
altFocusPoint: null,
|
||||||
|
arrowOtherEndpointInitialBinding: nextSelectedPointsIndices?.includes(0)
|
||||||
|
? element.endBinding
|
||||||
|
: element.startBinding,
|
||||||
},
|
},
|
||||||
selectedPointsIndices: nextSelectedPointsIndices,
|
selectedPointsIndices: nextSelectedPointsIndices,
|
||||||
pointerOffset: targetPoint
|
pointerOffset: targetPoint
|
||||||
@@ -2409,22 +2416,21 @@ const pointDraggingUpdates = (
|
|||||||
)! as ExcalidrawBindableElement)
|
)! as ExcalidrawBindableElement)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
const endLocalPoint = startIsDraggingOverEndElement
|
const endLocalPoint =
|
||||||
? nextArrow.points[nextArrow.points.length - 1]
|
endIsDraggingOverStartElement &&
|
||||||
: endIsDraggingOverStartElement &&
|
app.state.bindMode !== "inside" &&
|
||||||
app.state.bindMode !== "inside" &&
|
getFeatureFlag("COMPLEX_BINDINGS")
|
||||||
getFeatureFlag("COMPLEX_BINDINGS")
|
? nextArrow.points[0]
|
||||||
? nextArrow.points[0]
|
: endBindable
|
||||||
: endBindable
|
? updateBoundPoint(
|
||||||
? updateBoundPoint(
|
nextArrow,
|
||||||
nextArrow,
|
"endBinding",
|
||||||
"endBinding",
|
nextArrow.endBinding,
|
||||||
nextArrow.endBinding,
|
endBindable,
|
||||||
endBindable,
|
elementsMap,
|
||||||
elementsMap,
|
endIsDragged,
|
||||||
endIsDragged,
|
) || nextArrow.points[nextArrow.points.length - 1]
|
||||||
) || nextArrow.points[nextArrow.points.length - 1]
|
: nextArrow.points[nextArrow.points.length - 1];
|
||||||
: nextArrow.points[nextArrow.points.length - 1];
|
|
||||||
|
|
||||||
// We need to keep the simulated next arrow up-to-date, because
|
// We need to keep the simulated next arrow up-to-date, because
|
||||||
// updateBoundPoint looks at the opposite point
|
// updateBoundPoint looks at the opposite point
|
||||||
@@ -2458,13 +2464,11 @@ const pointDraggingUpdates = (
|
|||||||
: nextArrow.points[0];
|
: nextArrow.points[0];
|
||||||
|
|
||||||
const endChanged =
|
const endChanged =
|
||||||
!startIsDraggingOverEndElement &&
|
|
||||||
!(
|
!(
|
||||||
endIsDraggingOverStartElement &&
|
endIsDraggingOverStartElement &&
|
||||||
app.state.bindMode !== "inside" &&
|
app.state.bindMode !== "inside" &&
|
||||||
getFeatureFlag("COMPLEX_BINDINGS")
|
getFeatureFlag("COMPLEX_BINDINGS")
|
||||||
) &&
|
) && !!endBindable;
|
||||||
!!endBindable;
|
|
||||||
const startChanged =
|
const startChanged =
|
||||||
pointDistance(startLocalPoint, nextArrow.points[0]) !== 0;
|
pointDistance(startLocalPoint, nextArrow.points[0]) !== 0;
|
||||||
|
|
||||||
|
|||||||
@@ -8618,13 +8618,16 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
) as any;
|
) as any;
|
||||||
|
|
||||||
if (arrow && isBindingElement(arrow)) {
|
if (arrow && isBindingElement(arrow)) {
|
||||||
const { hitFocusPoint, pointerOffset } =
|
const {
|
||||||
handleFocusPointPointerDown(
|
hitFocusPoint,
|
||||||
arrow,
|
pointerOffset,
|
||||||
pointerDownState,
|
arrowOtherEndpointInitialBinding,
|
||||||
elementsMap,
|
} = handleFocusPointPointerDown(
|
||||||
this.state,
|
arrow,
|
||||||
);
|
pointerDownState,
|
||||||
|
elementsMap,
|
||||||
|
this.state,
|
||||||
|
);
|
||||||
|
|
||||||
// If focus point is hit, update state and prevent element selection
|
// If focus point is hit, update state and prevent element selection
|
||||||
if (hitFocusPoint) {
|
if (hitFocusPoint) {
|
||||||
@@ -8634,6 +8637,10 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
hoveredFocusPointBinding: hitFocusPoint,
|
hoveredFocusPointBinding: hitFocusPoint,
|
||||||
draggedFocusPointBinding: hitFocusPoint,
|
draggedFocusPointBinding: hitFocusPoint,
|
||||||
pointerOffset,
|
pointerOffset,
|
||||||
|
initialState: {
|
||||||
|
...linearElementEditor.initialState,
|
||||||
|
arrowOtherEndpointInitialBinding,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
@@ -10756,14 +10763,19 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.state.selectedLinearElement.draggedFocusPointBinding) {
|
if (this.state.selectedLinearElement.draggedFocusPointBinding) {
|
||||||
handleFocusPointPointerUp(
|
const { arrowOtherEndpointInitialBinding } =
|
||||||
this.state.selectedLinearElement,
|
handleFocusPointPointerUp(
|
||||||
this.scene,
|
this.state.selectedLinearElement,
|
||||||
);
|
this.scene,
|
||||||
|
);
|
||||||
this.setState({
|
this.setState({
|
||||||
selectedLinearElement: {
|
selectedLinearElement: {
|
||||||
...this.state.selectedLinearElement,
|
...this.state.selectedLinearElement,
|
||||||
draggedFocusPointBinding: null,
|
draggedFocusPointBinding: null,
|
||||||
|
initialState: {
|
||||||
|
...this.state.selectedLinearElement.initialState,
|
||||||
|
arrowOtherEndpointInitialBinding,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else if (
|
} else if (
|
||||||
|
|||||||
@@ -8665,6 +8665,7 @@ exports[`regression tests > key 5 selects arrow tool > [end of test] appState 1`
|
|||||||
"hoveredFocusPointBinding": null,
|
"hoveredFocusPointBinding": null,
|
||||||
"initialState": {
|
"initialState": {
|
||||||
"altFocusPoint": null,
|
"altFocusPoint": null,
|
||||||
|
"arrowOtherEndpointInitialBinding": null,
|
||||||
"arrowStartIsInside": false,
|
"arrowStartIsInside": false,
|
||||||
"lastClickedPoint": -1,
|
"lastClickedPoint": -1,
|
||||||
"origin": null,
|
"origin": null,
|
||||||
@@ -8898,6 +8899,7 @@ exports[`regression tests > key 6 selects line tool > [end of test] appState 1`]
|
|||||||
"hoveredFocusPointBinding": null,
|
"hoveredFocusPointBinding": null,
|
||||||
"initialState": {
|
"initialState": {
|
||||||
"altFocusPoint": null,
|
"altFocusPoint": null,
|
||||||
|
"arrowOtherEndpointInitialBinding": null,
|
||||||
"arrowStartIsInside": false,
|
"arrowStartIsInside": false,
|
||||||
"lastClickedPoint": -1,
|
"lastClickedPoint": -1,
|
||||||
"origin": null,
|
"origin": null,
|
||||||
@@ -9321,6 +9323,7 @@ exports[`regression tests > key a selects arrow tool > [end of test] appState 1`
|
|||||||
"hoveredFocusPointBinding": null,
|
"hoveredFocusPointBinding": null,
|
||||||
"initialState": {
|
"initialState": {
|
||||||
"altFocusPoint": null,
|
"altFocusPoint": null,
|
||||||
|
"arrowOtherEndpointInitialBinding": null,
|
||||||
"arrowStartIsInside": false,
|
"arrowStartIsInside": false,
|
||||||
"lastClickedPoint": -1,
|
"lastClickedPoint": -1,
|
||||||
"origin": null,
|
"origin": null,
|
||||||
@@ -9734,6 +9737,7 @@ exports[`regression tests > key l selects line tool > [end of test] appState 1`]
|
|||||||
"hoveredFocusPointBinding": null,
|
"hoveredFocusPointBinding": null,
|
||||||
"initialState": {
|
"initialState": {
|
||||||
"altFocusPoint": null,
|
"altFocusPoint": null,
|
||||||
|
"arrowOtherEndpointInitialBinding": null,
|
||||||
"arrowStartIsInside": false,
|
"arrowStartIsInside": false,
|
||||||
"lastClickedPoint": -1,
|
"lastClickedPoint": -1,
|
||||||
"origin": null,
|
"origin": null,
|
||||||
|
|||||||
Reference in New Issue
Block a user