feat: cycle through selected elements on cmd/ctrl-click
This commit is contained in:
parent
c819b653bf
commit
0e3a5b2042
@ -1862,6 +1862,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
/** if true, returns the first selected element (with highest z-index)
|
||||
of all hit elements */
|
||||
preferSelected?: boolean;
|
||||
cycleElementsUnderCursor?: boolean;
|
||||
},
|
||||
): NonDeleted<ExcalidrawElement> | null {
|
||||
const allHitElements = this.getElementsAtPosition(x, y);
|
||||
@ -1872,6 +1873,13 @@ class App extends React.Component<AppProps, AppState> {
|
||||
return allHitElements[index];
|
||||
}
|
||||
}
|
||||
} else if (opts?.cycleElementsUnderCursor) {
|
||||
const selectedIdx = allHitElements.findIndex(
|
||||
(element) => this.state.selectedElementIds[element.id],
|
||||
);
|
||||
return selectedIdx > 0
|
||||
? allHitElements[selectedIdx - 1]
|
||||
: allHitElements[allHitElements.length - 1];
|
||||
}
|
||||
const elementWithHighestZIndex =
|
||||
allHitElements[allHitElements.length - 1];
|
||||
@ -2746,10 +2754,13 @@ class App extends React.Component<AppProps, AppState> {
|
||||
|
||||
// hitElement may already be set above, so check first
|
||||
pointerDownState.hit.element =
|
||||
pointerDownState.hit.element ??
|
||||
(!event[KEYS.CTRL_OR_CMD] ? pointerDownState.hit.element : null) ??
|
||||
this.getElementAtPosition(
|
||||
pointerDownState.origin.x,
|
||||
pointerDownState.origin.y,
|
||||
{
|
||||
cycleElementsUnderCursor: event[KEYS.CTRL_OR_CMD],
|
||||
},
|
||||
);
|
||||
|
||||
// For overlapped elements one position may hit
|
||||
|
@ -144,6 +144,52 @@ describe("inner box-selection", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("selecting with cmd/ctrl modifier", () => {
|
||||
beforeEach(async () => {
|
||||
await render(<ExcalidrawApp />);
|
||||
});
|
||||
it("cycling through elements under cursor", async () => {
|
||||
const rect1 = API.createElement({
|
||||
type: "rectangle",
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 200,
|
||||
height: 200,
|
||||
backgroundColor: "red",
|
||||
fillStyle: "solid",
|
||||
});
|
||||
const rect2 = API.createElement({
|
||||
type: "rectangle",
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 200,
|
||||
height: 200,
|
||||
backgroundColor: "red",
|
||||
fillStyle: "solid",
|
||||
});
|
||||
const rect3 = API.createElement({
|
||||
type: "rectangle",
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 200,
|
||||
height: 200,
|
||||
backgroundColor: "red",
|
||||
fillStyle: "solid",
|
||||
});
|
||||
h.elements = [rect1, rect2, rect3];
|
||||
Keyboard.withModifierKeys({ ctrl: true }, () => {
|
||||
mouse.clickAt(100, 100);
|
||||
assertSelectedElements(rect3);
|
||||
mouse.clickAt(100, 100);
|
||||
assertSelectedElements(rect2);
|
||||
mouse.clickAt(100, 100);
|
||||
assertSelectedElements(rect1);
|
||||
mouse.clickAt(100, 100);
|
||||
assertSelectedElements(rect3);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("selection element", () => {
|
||||
it("create selection element on pointer down", async () => {
|
||||
const { getByToolName, container } = await render(<ExcalidrawApp />);
|
||||
|
Loading…
x
Reference in New Issue
Block a user