Turn off inside binding mode after leaving a shape

This commit is contained in:
Mark Tolmacs
2025-07-15 19:07:51 +02:00
parent c955b2716a
commit 1739fa92b1
6 changed files with 81 additions and 70 deletions

View File

@ -247,7 +247,7 @@ const bindingStrategyForEndpointDragging = (
// If the global bind mode is in free binding mode, just bind
// where the pointer is and keep the other end intact
if (globalBindMode === "fixed") {
if (globalBindMode === "inside") {
current = hovered
? { element: hovered, mode: hit ? "inside" : "outside" }
: { mode: undefined };

View File

@ -1992,7 +1992,7 @@ const pointDraggingUpdates = (
isBindingEnabled(appState) &&
isArrowElement(element) &&
hoveredElement &&
appState.bindMode === "focus" &&
appState.bindMode === "orbit" &&
!otherPointInsideElement
) {
newGlobalPointPosition = getOutlineAvoidingPoint(

View File

@ -1720,7 +1720,7 @@ export const actionChangeArrowType = register({
bindBindingElement(
newElement,
startElement,
appState.bindMode === "fixed" ? "inside" : "orbit",
appState.bindMode === "inside" ? "inside" : "orbit",
"start",
app.scene,
);
@ -1734,7 +1734,7 @@ export const actionChangeArrowType = register({
bindBindingElement(
newElement,
endElement,
appState.bindMode === "fixed" ? "inside" : "orbit",
appState.bindMode === "inside" ? "inside" : "orbit",
"end",
app.scene,
);

View File

@ -124,7 +124,7 @@ export const getDefaultAppState = (): Omit<
searchMatches: null,
lockedMultiSelections: {},
activeLockedId: null,
bindMode: "focus",
bindMode: "orbit",
};
};

View File

@ -4326,7 +4326,7 @@ class App extends React.Component<AppProps, AppState> {
}
// Handle Alt key for bind mode
if (event.key === KEYS.ALT && this.state.bindMode === "focus") {
if (event.key === KEYS.ALT && this.state.bindMode === "orbit") {
// Cancel any pending bind mode timer
if (this.bindModeHandler) {
clearTimeout(this.bindModeHandler);
@ -4647,7 +4647,7 @@ class App extends React.Component<AppProps, AppState> {
) {
// Handle Alt key release for bind mode
this.setState({
bindMode: "focus",
bindMode: "orbit",
});
// Restart the timer if we're creating/editing a linear element and hovering over an element
@ -4670,9 +4670,9 @@ class App extends React.Component<AppProps, AppState> {
if (hoveredElement && !this.bindModeHandler) {
this.bindModeHandler = setTimeout(() => {
if (hoveredElement) {
// this.setState({
// bindMode: "fixed",
// });
this.setState({
bindMode: "inside",
});
} else {
this.bindModeHandler = null;
}
@ -5999,10 +5999,7 @@ class App extends React.Component<AppProps, AppState> {
if (arrowWithoutUncommittedPoint || this.state.newElement) {
// Timed bind mode handler for arrow elements
if (
isArrowElement(this.state.newElement) &&
this.state.bindMode === "focus"
) {
if (isArrowElement(this.state.newElement)) {
const hoveredElement = getHoveredElementForBinding(
pointFrom<GlobalPoint>(scenePointer.x, scenePointer.y),
this.scene.getNonDeletedElements(),
@ -6010,48 +6007,56 @@ class App extends React.Component<AppProps, AppState> {
this.state.zoom,
);
if (this.bindModeHandler && !hoveredElement) {
clearTimeout(this.bindModeHandler);
this.bindModeHandler = null;
} else if (!this.bindModeHandler && hoveredElement) {
this.bindModeHandler = setTimeout(() => {
if (hoveredElement) {
flushSync(() => {
// this.setState({
// bindMode: "fixed",
// });
});
if (this.state.bindMode === "orbit") {
if (this.bindModeHandler && !hoveredElement) {
clearTimeout(this.bindModeHandler);
this.bindModeHandler = null;
} else if (!this.bindModeHandler && hoveredElement) {
this.bindModeHandler = setTimeout(() => {
if (hoveredElement) {
flushSync(() => {
this.setState({
bindMode: "inside",
});
});
if (isArrowElement(this.state.newElement)) {
const lastSceneCoords = viewportCoordsToSceneCoords(
{
clientX: this.lastPointerMoveEvent?.clientX ?? 0,
clientY: this.lastPointerMoveEvent?.clientY ?? 0,
},
this.state,
);
this.scene.mutateElement(
this.state.newElement,
{
points: [
...this.state.newElement.points.slice(0, -1),
LinearElementEditor.pointFromAbsoluteCoords(
this.state.newElement,
pointFrom<GlobalPoint>(
lastSceneCoords.x,
lastSceneCoords.y,
if (isArrowElement(this.state.newElement)) {
const lastSceneCoords = viewportCoordsToSceneCoords(
{
clientX: this.lastPointerMoveEvent?.clientX ?? 0,
clientY: this.lastPointerMoveEvent?.clientY ?? 0,
},
this.state,
);
this.scene.mutateElement(
this.state.newElement,
{
points: [
...this.state.newElement.points.slice(0, -1),
LinearElementEditor.pointFromAbsoluteCoords(
this.state.newElement,
pointFrom<GlobalPoint>(
lastSceneCoords.x,
lastSceneCoords.y,
),
this.scene.getNonDeletedElementsMap(),
),
this.scene.getNonDeletedElementsMap(),
),
],
},
{ informMutation: false, isDragging: false },
);
],
},
{ informMutation: false, isDragging: false },
);
}
} else {
this.bindModeHandler = null;
}
} else {
this.bindModeHandler = null;
}
}, BIND_MODE_TIMEOUT);
}, BIND_MODE_TIMEOUT);
}
} else if (!hoveredElement) {
flushSync(() => {
this.setState({
bindMode: "orbit",
});
});
}
}
@ -6135,7 +6140,7 @@ class App extends React.Component<AppProps, AppState> {
if (
isArrowElement(multiElement) &&
this.state.bindMode === "focus" &&
this.state.bindMode === "orbit" &&
isBindingEnabled(this.state)
) {
const hoveredElement = getHoveredElementForBinding(
@ -6923,13 +6928,13 @@ class App extends React.Component<AppProps, AppState> {
this.lastPointerUpEvent = event;
// Cancel any pending timeout for bind mode change
if (this.state.bindMode === "fixed" || this.state.bindMode === "skip") {
if (this.state.bindMode === "inside" || this.state.bindMode === "skip") {
if (this.bindModeHandler) {
clearTimeout(this.bindModeHandler);
}
this.bindModeHandler = null;
this.setState({
bindMode: "focus",
bindMode: "orbit",
});
}
@ -8579,15 +8584,15 @@ class App extends React.Component<AppProps, AppState> {
return;
}
// Timed bind mode handler for arrow elements
if (this.state.bindMode === "focus") {
const hoveredElement = getHoveredElementForBinding(
pointFrom<GlobalPoint>(pointerCoords.x, pointerCoords.y),
this.scene.getNonDeletedElements(),
elementsMap,
this.state.zoom,
);
const hoveredElement = getHoveredElementForBinding(
pointFrom<GlobalPoint>(pointerCoords.x, pointerCoords.y),
this.scene.getNonDeletedElements(),
elementsMap,
this.state.zoom,
);
// Timed bind mode handler for arrow elements
if (this.state.bindMode === "orbit") {
if (this.bindModeHandler && !hoveredElement) {
clearTimeout(this.bindModeHandler);
this.bindModeHandler = null;
@ -8595,9 +8600,9 @@ class App extends React.Component<AppProps, AppState> {
this.bindModeHandler = setTimeout(() => {
if (hoveredElement) {
flushSync(() => {
// this.setState({
// bindMode: "fixed",
// });
this.setState({
bindMode: "inside",
});
});
const newState = LinearElementEditor.handlePointDragging(
event,
@ -8620,6 +8625,12 @@ class App extends React.Component<AppProps, AppState> {
}
}, BIND_MODE_TIMEOUT);
}
} else if (!hoveredElement) {
flushSync(() => {
this.setState({
bindMode: "orbit",
});
});
}
const newState = LinearElementEditor.handlePointDragging(
@ -9104,7 +9115,7 @@ class App extends React.Component<AppProps, AppState> {
if (!arrowEndpointsAboutToBindToSameElement) {
const [targetPointX, targetPointY] =
this.state.bindMode === "focus" && isBindingEnabled(this.state)
this.state.bindMode === "orbit" && isBindingEnabled(this.state)
? getOutlineAvoidingPoint(
newElement,
hoveredElement,
@ -9462,7 +9473,7 @@ class App extends React.Component<AppProps, AppState> {
this.setState({
selectedElementsAreBeingDragged: false,
bindMode: "focus",
bindMode: "orbit",
});
if (

View File

@ -444,7 +444,7 @@ export interface AppState {
// as elements are unlocked, we remove the groupId from the elements
// and also remove groupId from this map
lockedMultiSelections: { [groupId: string]: true };
bindMode: "focus" | "fixed" | "skip";
bindMode: "orbit" | "inside" | "skip";
}
export type SearchMatch = {