feat: drag, resize, and rotate after selecting in lasso

This commit is contained in:
Ryan Di
2025-07-11 14:17:28 +10:00
parent 2d127f8c22
commit 5337480583
2 changed files with 50 additions and 10 deletions

View File

@@ -6013,6 +6013,7 @@ class App extends React.Component<AppProps, AppState> {
if ( if (
hasDeselectedButton || hasDeselectedButton ||
(this.state.activeTool.type !== "selection" && (this.state.activeTool.type !== "selection" &&
this.state.activeTool.type !== "lasso" &&
this.state.activeTool.type !== "text" && this.state.activeTool.type !== "text" &&
this.state.activeTool.type !== "eraser") this.state.activeTool.type !== "eraser")
) { ) {
@@ -6183,8 +6184,13 @@ class App extends React.Component<AppProps, AppState> {
// Ebow arrows can only be moved when unconnected // Ebow arrows can only be moved when unconnected
!isElbowArrow(hitElement) || !isElbowArrow(hitElement) ||
!(hitElement.startBinding || hitElement.endBinding) !(hitElement.startBinding || hitElement.endBinding)
) {
if (
this.state.activeTool.type !== "lasso" ||
selectedElements.length > 0
) { ) {
setCursor(this.interactiveCanvas, CURSOR_TYPE.MOVE); setCursor(this.interactiveCanvas, CURSOR_TYPE.MOVE);
}
if (this.state.activeEmbeddable?.state === "hover") { if (this.state.activeEmbeddable?.state === "hover") {
this.setState({ activeEmbeddable: null }); this.setState({ activeEmbeddable: null });
} }
@@ -6292,19 +6298,29 @@ class App extends React.Component<AppProps, AppState> {
// Ebow arrows can only be moved when unconnected // Ebow arrows can only be moved when unconnected
!isElbowArrow(element) || !isElbowArrow(element) ||
!(element.startBinding || element.endBinding) !(element.startBinding || element.endBinding)
) {
if (
this.state.activeTool.type !== "lasso" ||
Object.keys(this.state.selectedElementIds).length > 0
) { ) {
setCursor(this.interactiveCanvas, CURSOR_TYPE.MOVE); setCursor(this.interactiveCanvas, CURSOR_TYPE.MOVE);
} }
} }
}
} else if (this.hitElement(scenePointerX, scenePointerY, element)) { } else if (this.hitElement(scenePointerX, scenePointerY, element)) {
if ( if (
// Ebow arrows can only be moved when unconnected // Ebow arrows can only be moved when unconnected
!isElbowArrow(element) || !isElbowArrow(element) ||
!(element.startBinding || element.endBinding) !(element.startBinding || element.endBinding)
) {
if (
this.state.activeTool.type !== "lasso" ||
Object.keys(this.state.selectedElementIds).length > 0
) { ) {
setCursor(this.interactiveCanvas, CURSOR_TYPE.MOVE); setCursor(this.interactiveCanvas, CURSOR_TYPE.MOVE);
} }
} }
}
if ( if (
this.state.selectedLinearElement.hoverPointIndex !== hoverPointIndex this.state.selectedLinearElement.hoverPointIndex !== hoverPointIndex
@@ -6564,11 +6580,25 @@ class App extends React.Component<AppProps, AppState> {
} }
if (this.state.activeTool.type === "lasso") { if (this.state.activeTool.type === "lasso") {
const hitSelectedElement =
pointerDownState.hit.element &&
this.isASelectedElement(pointerDownState.hit.element);
// Start a new lasso ONLY if we're not interacting with an existing
// selection (move/resize/rotate).
if (
!pointerDownState.hit.hasHitCommonBoundingBoxOfSelectedElements &&
!pointerDownState.resize.handleType &&
!hitSelectedElement
) {
this.lassoTrail.startPath( this.lassoTrail.startPath(
pointerDownState.origin.x, pointerDownState.origin.x,
pointerDownState.origin.y, pointerDownState.origin.y,
event.shiftKey, event.shiftKey,
); );
// Block drag until next pointerdown if a lasso selection is made
pointerDownState.drag.blockDragAfterLasso = true;
}
} else if (this.state.activeTool.type === "text") { } else if (this.state.activeTool.type === "text") {
this.handleTextOnPointerDown(event, pointerDownState); this.handleTextOnPointerDown(event, pointerDownState);
} else if ( } else if (
@@ -6948,6 +6978,7 @@ class App extends React.Component<AppProps, AppState> {
hasOccurred: false, hasOccurred: false,
offset: null, offset: null,
origin: { ...origin }, origin: { ...origin },
blockDragAfterLasso: false,
}, },
eventListeners: { eventListeners: {
onMove: null, onMove: null,
@@ -7023,7 +7054,10 @@ class App extends React.Component<AppProps, AppState> {
event: React.PointerEvent<HTMLElement>, event: React.PointerEvent<HTMLElement>,
pointerDownState: PointerDownState, pointerDownState: PointerDownState,
): boolean => { ): boolean => {
if (this.state.activeTool.type === "selection") { if (
this.state.activeTool.type === "selection" ||
this.state.activeTool.type === "lasso"
) {
const elements = this.scene.getNonDeletedElements(); const elements = this.scene.getNonDeletedElements();
const elementsMap = this.scene.getNonDeletedElementsMap(); const elementsMap = this.scene.getNonDeletedElementsMap();
const selectedElements = this.scene.getSelectedElements(this.state); const selectedElements = this.scene.getSelectedElements(this.state);
@@ -8257,7 +8291,10 @@ class App extends React.Component<AppProps, AppState> {
(hasHitASelectedElement || (hasHitASelectedElement ||
pointerDownState.hit.hasHitCommonBoundingBoxOfSelectedElements) && pointerDownState.hit.hasHitCommonBoundingBoxOfSelectedElements) &&
!isSelectingPointsInLineEditor && !isSelectingPointsInLineEditor &&
this.state.activeTool.type !== "lasso" (this.state.activeTool.type !== "lasso" ||
pointerDownState.hit.hasHitCommonBoundingBoxOfSelectedElements ||
hasHitASelectedElement) &&
!pointerDownState.drag.blockDragAfterLasso
) { ) {
const selectedElements = this.scene.getSelectedElements(this.state); const selectedElements = this.scene.getSelectedElements(this.state);
@@ -8878,6 +8915,7 @@ class App extends React.Component<AppProps, AppState> {
): (event: PointerEvent) => void { ): (event: PointerEvent) => void {
return withBatchedUpdates((childEvent: PointerEvent) => { return withBatchedUpdates((childEvent: PointerEvent) => {
this.removePointer(childEvent); this.removePointer(childEvent);
pointerDownState.drag.blockDragAfterLasso = false;
if (pointerDownState.eventListeners.onMove) { if (pointerDownState.eventListeners.onMove) {
pointerDownState.eventListeners.onMove.flush(); pointerDownState.eventListeners.onMove.flush();
} }

View File

@@ -782,6 +782,8 @@ export type PointerDownState = Readonly<{
// by default same as PointerDownState.origin. On alt-duplication, reset // by default same as PointerDownState.origin. On alt-duplication, reset
// to current pointer position at time of duplication. // to current pointer position at time of duplication.
origin: { x: number; y: number }; origin: { x: number; y: number };
// used to block drag after lasso selection until next pointerdown
blockDragAfterLasso: boolean;
}; };
// We need to have these in the state so that we can unsubscribe them // We need to have these in the state so that we can unsubscribe them
eventListeners: { eventListeners: {