From 8af9ea3cf3a10d8053cbad232be0fdf9ad3f46c9 Mon Sep 17 00:00:00 2001 From: David Luzar Date: Thu, 27 Jul 2023 12:50:08 +0200 Subject: [PATCH] fix: adding to selection via shift box-select (#6815) --- src/components/App.tsx | 69 +++++++++++++++++++++--------------- src/tests/selection.test.tsx | 68 +++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 29 deletions(-) diff --git a/src/components/App.tsx b/src/components/App.tsx index 33094a48..b3dd678b 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -6068,28 +6068,7 @@ class App extends React.Component { pointerDownState.boxSelection.hasOccurred = true; const elements = this.scene.getNonDeletedElements(); - if ( - !event.shiftKey && - // allows for box-selecting points (without shift) - !this.state.editingLinearElement && - isSomeElementSelected(elements, this.state) - ) { - if (pointerDownState.withCmdOrCtrl && pointerDownState.hit.element) { - this.setState((prevState) => - selectGroupsForSelectedElements( - { - ...prevState, - selectedElementIds: { - [pointerDownState.hit.element!.id]: true, - }, - }, - this.scene.getNonDeletedElements(), - prevState, - this, - ), - ); - } - } + // box-select line editor points if (this.state.editingLinearElement) { LinearElementEditor.handleBoxSelection( @@ -6099,18 +6078,46 @@ class App extends React.Component { ); // regular box-select } else { + let shouldReuseSelection = true; + + if (!event.shiftKey && isSomeElementSelected(elements, this.state)) { + if ( + pointerDownState.withCmdOrCtrl && + pointerDownState.hit.element + ) { + this.setState((prevState) => + selectGroupsForSelectedElements( + { + ...prevState, + selectedElementIds: { + [pointerDownState.hit.element!.id]: true, + }, + }, + this.scene.getNonDeletedElements(), + prevState, + this, + ), + ); + } else { + shouldReuseSelection = false; + } + } const elementsWithinSelection = getElementsWithinSelection( elements, draggingElement, ); + this.setState((prevState) => { - const nextSelectedElementIds = elementsWithinSelection.reduce( - (acc: Record, element) => { - acc[element.id] = true; - return acc; - }, - {}, - ); + const nextSelectedElementIds = { + ...(shouldReuseSelection && prevState.selectedElementIds), + ...elementsWithinSelection.reduce( + (acc: Record, element) => { + acc[element.id] = true; + return acc; + }, + {}, + ), + }; if (pointerDownState.hit.element) { // if using ctrl/cmd, select the hitElement only if we @@ -6125,6 +6132,10 @@ class App extends React.Component { return selectGroupsForSelectedElements( { ...prevState, + ...(!shouldReuseSelection && { + selectedGroupIds: {}, + editingGroupId: null, + }), selectedElementIds: nextSelectedElementIds, showHyperlinkPopup: elementsWithinSelection.length === 1 && diff --git a/src/tests/selection.test.tsx b/src/tests/selection.test.tsx index 7bfd6e9b..af5505f3 100644 --- a/src/tests/selection.test.tsx +++ b/src/tests/selection.test.tsx @@ -28,6 +28,74 @@ const { h } = window; const mouse = new Pointer("mouse"); +describe("box-selection", () => { + beforeEach(async () => { + await render(); + }); + + it("should allow adding to selection via box-select when holding shift", async () => { + const rect1 = API.createElement({ + type: "rectangle", + x: 0, + y: 0, + width: 50, + height: 50, + backgroundColor: "red", + fillStyle: "solid", + }); + const rect2 = API.createElement({ + type: "rectangle", + x: 100, + y: 0, + width: 50, + height: 50, + }); + + h.elements = [rect1, rect2]; + + mouse.downAt(175, -20); + mouse.moveTo(85, 70); + mouse.up(); + + assertSelectedElements([rect2.id]); + + Keyboard.withModifierKeys({ shift: true }, () => { + mouse.downAt(75, -20); + mouse.moveTo(-15, 70); + mouse.up(); + }); + + assertSelectedElements([rect2.id, rect1.id]); + }); + + it("should (de)select element when box-selecting over and out while not holding shift", async () => { + const rect1 = API.createElement({ + type: "rectangle", + x: 0, + y: 0, + width: 50, + height: 50, + backgroundColor: "red", + fillStyle: "solid", + }); + + h.elements = [rect1]; + + mouse.downAt(75, -20); + mouse.moveTo(-15, 70); + + assertSelectedElements([rect1.id]); + + mouse.moveTo(100, -100); + + assertSelectedElements([]); + + mouse.up(); + + assertSelectedElements([]); + }); +}); + describe("inner box-selection", () => { beforeEach(async () => { await render();