mirror of
https://github.com/excalidraw/excalidraw
synced 2025-07-25 13:58:22 +08:00
alternative ux: drag with lasso right away
This commit is contained in:
@ -6596,8 +6596,94 @@ class App extends React.Component<AppProps, AppState> {
|
||||
pointerDownState.origin.y,
|
||||
event.shiftKey,
|
||||
);
|
||||
// Block drag until next pointerdown if a lasso selection is made
|
||||
pointerDownState.drag.blockDragAfterLasso = true;
|
||||
}
|
||||
|
||||
// For lasso tool, if we hit an element, select it immediately like normal selection
|
||||
if (pointerDownState.hit.element && !hitSelectedElement) {
|
||||
this.setState((prevState) => {
|
||||
let nextSelectedElementIds: { [id: string]: true } = {
|
||||
...prevState.selectedElementIds,
|
||||
[pointerDownState.hit.element!.id]: true,
|
||||
};
|
||||
|
||||
const previouslySelectedElements: ExcalidrawElement[] = [];
|
||||
|
||||
Object.keys(prevState.selectedElementIds).forEach((id) => {
|
||||
const element = this.scene.getElement(id);
|
||||
element && previouslySelectedElements.push(element);
|
||||
});
|
||||
|
||||
const hitElement = pointerDownState.hit.element!;
|
||||
|
||||
// if hitElement is frame-like, deselect all of its elements
|
||||
// if they are selected
|
||||
if (isFrameLikeElement(hitElement)) {
|
||||
getFrameChildren(previouslySelectedElements, hitElement.id).forEach(
|
||||
(element) => {
|
||||
delete nextSelectedElementIds[element.id];
|
||||
},
|
||||
);
|
||||
} else if (hitElement.frameId) {
|
||||
// if hitElement is in a frame and its frame has been selected
|
||||
// disable selection for the given element
|
||||
if (nextSelectedElementIds[hitElement.frameId]) {
|
||||
delete nextSelectedElementIds[hitElement.id];
|
||||
}
|
||||
} else {
|
||||
// hitElement is neither a frame nor an element in a frame
|
||||
// but since hitElement could be in a group with some frames
|
||||
// this means selecting hitElement will have the frames selected as well
|
||||
// because we want to keep the invariant:
|
||||
// - frames and their elements are not selected at the same time
|
||||
// we deselect elements in those frames that were previously selected
|
||||
|
||||
const groupIds = hitElement.groupIds;
|
||||
const framesInGroups = new Set(
|
||||
groupIds
|
||||
.flatMap((gid) =>
|
||||
getElementsInGroup(this.scene.getNonDeletedElements(), gid),
|
||||
)
|
||||
.filter((element) => isFrameLikeElement(element))
|
||||
.map((frame) => frame.id),
|
||||
);
|
||||
|
||||
if (framesInGroups.size > 0) {
|
||||
previouslySelectedElements.forEach((element) => {
|
||||
if (element.frameId && framesInGroups.has(element.frameId)) {
|
||||
// deselect element and groups containing the element
|
||||
delete nextSelectedElementIds[element.id];
|
||||
element.groupIds
|
||||
.flatMap((gid) =>
|
||||
getElementsInGroup(
|
||||
this.scene.getNonDeletedElements(),
|
||||
gid,
|
||||
),
|
||||
)
|
||||
.forEach((element) => {
|
||||
delete nextSelectedElementIds[element.id];
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...selectGroupsForSelectedElements(
|
||||
{
|
||||
editingGroupId: prevState.editingGroupId,
|
||||
selectedElementIds: nextSelectedElementIds,
|
||||
},
|
||||
this.scene.getNonDeletedElements(),
|
||||
prevState,
|
||||
this,
|
||||
),
|
||||
showHyperlinkPopup:
|
||||
hitElement.link || isEmbeddableElement(hitElement)
|
||||
? "info"
|
||||
: false,
|
||||
};
|
||||
});
|
||||
pointerDownState.hit.wasAddedToSelection = true;
|
||||
}
|
||||
} else if (this.state.activeTool.type === "text") {
|
||||
this.handleTextOnPointerDown(event, pointerDownState);
|
||||
@ -6978,7 +7064,6 @@ class App extends React.Component<AppProps, AppState> {
|
||||
hasOccurred: false,
|
||||
offset: null,
|
||||
origin: { ...origin },
|
||||
blockDragAfterLasso: false,
|
||||
},
|
||||
eventListeners: {
|
||||
onMove: null,
|
||||
@ -8289,12 +8374,14 @@ class App extends React.Component<AppProps, AppState> {
|
||||
pointerDownState.hit.element?.id;
|
||||
if (
|
||||
(hasHitASelectedElement ||
|
||||
pointerDownState.hit.hasHitCommonBoundingBoxOfSelectedElements) &&
|
||||
pointerDownState.hit.hasHitCommonBoundingBoxOfSelectedElements ||
|
||||
(this.state.activeTool.type === "lasso" &&
|
||||
pointerDownState.hit.element)) &&
|
||||
!isSelectingPointsInLineEditor &&
|
||||
(this.state.activeTool.type !== "lasso" ||
|
||||
pointerDownState.hit.hasHitCommonBoundingBoxOfSelectedElements ||
|
||||
hasHitASelectedElement) &&
|
||||
!pointerDownState.drag.blockDragAfterLasso
|
||||
hasHitASelectedElement ||
|
||||
pointerDownState.hit.element)
|
||||
) {
|
||||
const selectedElements = this.scene.getSelectedElements(this.state);
|
||||
|
||||
@ -8319,6 +8406,11 @@ class App extends React.Component<AppProps, AppState> {
|
||||
// if elements should be deselected on pointerup
|
||||
pointerDownState.drag.hasOccurred = true;
|
||||
|
||||
// Clear lasso trail when starting to drag with lasso tool
|
||||
if (this.state.activeTool.type === "lasso") {
|
||||
this.lassoTrail.endPath();
|
||||
}
|
||||
|
||||
// prevent dragging even if we're no longer holding cmd/ctrl otherwise
|
||||
// it would have weird results (stuff jumping all over the screen)
|
||||
// Checking for editingTextElement to avoid jump while editing on mobile #6503
|
||||
@ -8915,7 +9007,6 @@ class App extends React.Component<AppProps, AppState> {
|
||||
): (event: PointerEvent) => void {
|
||||
return withBatchedUpdates((childEvent: PointerEvent) => {
|
||||
this.removePointer(childEvent);
|
||||
pointerDownState.drag.blockDragAfterLasso = false;
|
||||
if (pointerDownState.eventListeners.onMove) {
|
||||
pointerDownState.eventListeners.onMove.flush();
|
||||
}
|
||||
|
@ -782,8 +782,6 @@ export type PointerDownState = Readonly<{
|
||||
// by default same as PointerDownState.origin. On alt-duplication, reset
|
||||
// to current pointer position at time of duplication.
|
||||
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
|
||||
eventListeners: {
|
||||
|
Reference in New Issue
Block a user