mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
feat: allow inner-drag-selecting with cmd/ctrl (#3603)
* feat: allow inner-drag-selecting with cmd/ctrl * don't use cursor when pressing cmd/ctrl * ensure we reset deselected groups * add tests * add docs * couple fixes around group selection
This commit is contained in:
parent
f4e10c93e1
commit
6d40039f08
6 changed files with 228 additions and 20 deletions
|
@ -259,6 +259,7 @@ export type PointerDownState = Readonly<{
|
|||
hasBeenDuplicated: boolean;
|
||||
hasHitCommonBoundingBoxOfSelectedElements: boolean;
|
||||
};
|
||||
withCmdOrCtrl: boolean;
|
||||
drag: {
|
||||
// Might change during the pointer interation
|
||||
hasOccurred: boolean;
|
||||
|
@ -2260,11 +2261,13 @@ class App extends React.Component<AppProps, AppState> {
|
|||
} else if (isOverScrollBar) {
|
||||
setCursor(this.canvas, CURSOR_TYPE.AUTO);
|
||||
} else if (
|
||||
hitElement ||
|
||||
this.isHittingCommonBoundingBoxOfSelectedElements(
|
||||
scenePointer,
|
||||
selectedElements,
|
||||
)
|
||||
// if using cmd/ctrl, we're not dragging
|
||||
!event[KEYS.CTRL_OR_CMD] &&
|
||||
(hitElement ||
|
||||
this.isHittingCommonBoundingBoxOfSelectedElements(
|
||||
scenePointer,
|
||||
selectedElements,
|
||||
))
|
||||
) {
|
||||
setCursor(this.canvas, CURSOR_TYPE.MOVE);
|
||||
} else {
|
||||
|
@ -2542,6 +2545,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||
|
||||
return {
|
||||
origin,
|
||||
withCmdOrCtrl: event[KEYS.CTRL_OR_CMD],
|
||||
originInGrid: tupleToCoors(
|
||||
getGridPoint(origin.x, origin.y, this.state.gridSize),
|
||||
),
|
||||
|
@ -2746,6 +2750,9 @@ class App extends React.Component<AppProps, AppState> {
|
|||
if (hitElement != null) {
|
||||
// on CMD/CTRL, drill down to hit element regardless of groups etc.
|
||||
if (event[KEYS.CTRL_OR_CMD]) {
|
||||
if (!this.state.selectedElementIds[hitElement.id]) {
|
||||
pointerDownState.hit.wasAddedToSelection = true;
|
||||
}
|
||||
this.setState((prevState) => ({
|
||||
...editGroupForSelectedElement(prevState, hitElement),
|
||||
previousSelectedElementIds: this.state.selectedElementIds,
|
||||
|
@ -3184,7 +3191,9 @@ class App extends React.Component<AppProps, AppState> {
|
|||
this.scene.getElements(),
|
||||
this.state,
|
||||
);
|
||||
if (selectedElements.length > 0) {
|
||||
// prevent dragging even if we're no longer holding cmd/ctrl otherwise
|
||||
// it would have weird results (stuff jumping all over the screen)
|
||||
if (selectedElements.length > 0 && !pointerDownState.withCmdOrCtrl) {
|
||||
const [dragX, dragY] = getGridPoint(
|
||||
pointerCoords.x - pointerDownState.drag.offset.x,
|
||||
pointerCoords.y - pointerDownState.drag.offset.y,
|
||||
|
@ -3326,11 +3335,25 @@ class App extends React.Component<AppProps, AppState> {
|
|||
if (this.state.elementType === "selection") {
|
||||
const elements = this.scene.getElements();
|
||||
if (!event.shiftKey && isSomeElementSelected(elements, this.state)) {
|
||||
this.setState({
|
||||
selectedElementIds: {},
|
||||
selectedGroupIds: {},
|
||||
editingGroupId: null,
|
||||
});
|
||||
if (pointerDownState.withCmdOrCtrl && pointerDownState.hit.element) {
|
||||
this.setState((prevState) =>
|
||||
selectGroupsForSelectedElements(
|
||||
{
|
||||
...prevState,
|
||||
selectedElementIds: {
|
||||
[pointerDownState.hit.element!.id]: true,
|
||||
},
|
||||
},
|
||||
this.scene.getElements(),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
this.setState({
|
||||
selectedElementIds: {},
|
||||
selectedGroupIds: {},
|
||||
editingGroupId: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
const elementsWithinSelection = getElementsWithinSelection(
|
||||
elements,
|
||||
|
@ -3346,6 +3369,14 @@ class App extends React.Component<AppProps, AppState> {
|
|||
map[element.id] = true;
|
||||
return map;
|
||||
}, {} as any),
|
||||
...(pointerDownState.hit.element
|
||||
? {
|
||||
// if using ctrl/cmd, select the hitElement only if we
|
||||
// haven't box-selected anything else
|
||||
[pointerDownState.hit.element
|
||||
.id]: !elementsWithinSelection.length,
|
||||
}
|
||||
: null),
|
||||
},
|
||||
},
|
||||
this.scene.getElements(),
|
||||
|
@ -3610,12 +3641,18 @@ class App extends React.Component<AppProps, AppState> {
|
|||
} else {
|
||||
// remove element from selection while
|
||||
// keeping prev elements selected
|
||||
this.setState((prevState) => ({
|
||||
selectedElementIds: {
|
||||
...prevState.selectedElementIds,
|
||||
[hitElement!.id]: false,
|
||||
},
|
||||
}));
|
||||
this.setState((prevState) =>
|
||||
selectGroupsForSelectedElements(
|
||||
{
|
||||
...prevState,
|
||||
selectedElementIds: {
|
||||
...prevState.selectedElementIds,
|
||||
[hitElement!.id]: false,
|
||||
},
|
||||
},
|
||||
this.scene.getElements(),
|
||||
),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// add element to selection while
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue