Resize handler detection should not be active when moving multip… (#767)

* Fix bug.

* Implement `getSelectedElements`.

* Explicit condition.

* Respect variable naming.

* Keep state consistent.

* Use `isSomeElementSelected` abstraction.

* Missing ones.
This commit is contained in:
Enzo Ferey 2020-02-16 22:54:50 +01:00 committed by GitHub
parent ad72946131
commit 6ebd41734f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 53 additions and 37 deletions

View File

@ -1,5 +1,5 @@
import { Action } from "./types";
import { deleteSelectedElements } from "../scene";
import { deleteSelectedElements, isSomeElementSelected } from "../scene";
import { KEYS } from "../keys";
export const actionDeleteSelected: Action = {
@ -12,6 +12,6 @@ export const actionDeleteSelected: Action = {
},
contextItemLabel: "labels.delete",
contextMenuOrder: 3,
commitToHistory: (_, elements) => elements.some(el => el.isSelected),
commitToHistory: (_, elements) => isSomeElementSelected(elements),
keyTest: event => event.key === KEYS.BACKSPACE || event.key === KEYS.DELETE,
};

View File

@ -1,7 +1,10 @@
import React from "react";
import { Action } from "./types";
import { ExcalidrawElement, ExcalidrawTextElement } from "../element/types";
import { getCommonAttributeOfSelectedElements } from "../scene";
import {
getCommonAttributeOfSelectedElements,
isSomeElementSelected,
} from "../scene";
import { ButtonSelect } from "../components/ButtonSelect";
import { isTextElement, redrawTextBoundingBox } from "../element";
import { ColorPicker } from "../components/ColorPicker";
@ -28,7 +31,7 @@ const getFormValue = function<T>(
): T | null {
return (
(editingElement && getAttribute(editingElement)) ??
(elements.some(element => element.isSelected)
(isSomeElementSelected(elements)
? getCommonAttributeOfSelectedElements(elements, getAttribute)
: defaultValue) ??
null

View File

@ -1,4 +1,5 @@
import { ExcalidrawElement } from "./element/types";
import { getSelectedElements } from "./scene";
let CLIPBOARD = "";
let PREFER_APP_CLIPBOARD = false;
@ -19,9 +20,7 @@ export async function copyToAppClipboard(
elements: readonly ExcalidrawElement[],
) {
CLIPBOARD = JSON.stringify(
elements
.filter(element => element.isSelected)
.map(({ shape, ...el }) => el),
getSelectedElements(elements).map(({ shape, ...el }) => el),
);
try {
// when copying to in-app clipboard, clear system clipboard so that if

View File

@ -16,6 +16,7 @@ import { t } from "../i18n";
import { KEYS } from "../keys";
import { probablySupportsClipboardBlob } from "../clipboard";
import { getSelectedElements, isSomeElementSelected } from "../scene";
const scales = [1, 2, 3];
const defaultScale = scales.includes(devicePixelRatio) ? devicePixelRatio : 1;
@ -46,7 +47,7 @@ function ExportModal({
onExportToBackend: ExportCB;
onCloseRequest: () => void;
}) {
const someElementIsSelected = elements.some(element => element.isSelected);
const someElementIsSelected = isSomeElementSelected(elements);
const [scale, setScale] = useState(defaultScale);
const [exportSelected, setExportSelected] = useState(someElementIsSelected);
const previewRef = useRef<HTMLDivElement>(null);
@ -56,7 +57,7 @@ function ExportModal({
const onlySelectedInput = useRef<HTMLInputElement>(null);
const exportedElements = exportSelected
? elements.filter(element => element.isSelected)
? getSelectedElements(elements)
: elements;
useEffect(() => {

View File

@ -1,6 +1,7 @@
import React from "react";
import { t } from "../i18n";
import { ExcalidrawElement } from "../element/types";
import { getSelectedElements } from "../scene";
import "./HintViewer.css";
@ -20,7 +21,7 @@ const getHints = ({ elementType, multiMode, isResizing, elements }: Hint) => {
}
if (isResizing) {
const selectedElements = elements.filter(el => el.isSelected);
const selectedElements = getSelectedElements(elements);
if (
selectedElements.length === 1 &&
(selectedElements[0].type === "arrow" ||

View File

@ -36,6 +36,8 @@ import {
loadFromBlob,
getZoomOrigin,
getNormalizedZoom,
getSelectedElements,
isSomeElementSelected,
} from "./scene";
import { renderScene } from "./renderer";
@ -275,7 +277,7 @@ const LayerUI = React.memo(
const { elementType, editingElement } = appState;
const targetElements = editingElement
? [editingElement]
: elements.filter(el => el.isSelected);
: getSelectedElements(elements);
if (!targetElements.length && elementType === "selection") {
return null;
}
@ -1046,11 +1048,15 @@ export class App extends React.Component<any, AppState> {
{ x, y },
this.state.zoom,
);
const selectedElements = getSelectedElements(elements);
if (selectedElements.length === 1 && resizeElement) {
this.setState({
resizingElement: resizeElement ? resizeElement.element : null,
resizingElement: resizeElement
? resizeElement.element
: null,
});
if (resizeElement) {
resizeHandle = resizeElement.resizeHandle;
document.documentElement.style.cursor = getCursorForResizingElement(
resizeElement,
@ -1087,9 +1093,7 @@ export class App extends React.Component<any, AppState> {
...element,
isSelected: false,
})),
...elements
.filter(element => element.isSelected)
.map(element => {
...getSelectedElements(elements).map(element => {
const newElement = duplicateElement(element);
newElement.isSelected = true;
return newElement;
@ -1328,7 +1332,7 @@ export class App extends React.Component<any, AppState> {
if (isResizingElements && this.state.resizingElement) {
this.setState({ isResizing: true });
const el = this.state.resizingElement;
const selectedElements = elements.filter(el => el.isSelected);
const selectedElements = getSelectedElements(elements);
if (selectedElements.length === 1) {
const { x, y } = viewportCoordsToSceneCoords(
e,
@ -1555,8 +1559,8 @@ export class App extends React.Component<any, AppState> {
// Marking that click was used for dragging to check
// if elements should be deselected on mouseup
draggingOccurred = true;
const selectedElements = elements.filter(el => el.isSelected);
if (selectedElements.length) {
const selectedElements = getSelectedElements(elements);
if (selectedElements.length > 0) {
const { x, y } = viewportCoordsToSceneCoords(
e,
this.state,
@ -1638,7 +1642,7 @@ export class App extends React.Component<any, AppState> {
draggingElement.shape = null;
if (this.state.elementType === "selection") {
if (!e.shiftKey && elements.some(el => el.isSelected)) {
if (!e.shiftKey && isSomeElementSelected(elements)) {
elements = clearSelection(elements);
}
const elementsWithinSelection = getElementsWithinSelection(
@ -1772,7 +1776,7 @@ export class App extends React.Component<any, AppState> {
if (
elementType !== "selection" ||
elements.some(el => el.isSelected)
isSomeElementSelected(elements)
) {
history.resumeRecording();
}
@ -1941,9 +1945,8 @@ export class App extends React.Component<any, AppState> {
return;
}
const selectedElements = elements.filter(e => e.isSelected)
.length;
if (selectedElements === 1) {
const selectedElements = getSelectedElements(elements);
if (selectedElements.length === 1) {
const resizeElement = getElementWithResizeHandler(
elements,
{ x, y },

View File

@ -12,6 +12,7 @@ import {
SCROLLBAR_WIDTH,
} from "../scene/scrollbars";
import { getZoomTranslation } from "../scene/zoom";
import { getSelectedElements } from "../scene/selection";
import { renderElement, renderElementToSvg } from "./renderElement";
@ -135,7 +136,7 @@ export function renderScene(
// Pain selected elements
if (renderSelection) {
const selectedElements = elements.filter(element => element.isSelected);
const selectedElements = getSelectedElements(elements);
const dashledLinePadding = 4 / sceneState.zoom;
context.save();

View File

@ -3,9 +3,10 @@ export {
clearSelection,
getSelectedIndices,
deleteSelectedElements,
someElementIsSelected,
isSomeElementSelected,
getElementsWithinSelection,
getCommonAttributeOfSelectedElements,
getSelectedElements,
} from "./selection";
export {
exportCanvas,

View File

@ -55,8 +55,11 @@ export function getSelectedIndices(elements: readonly ExcalidrawElement[]) {
return selectedIndices;
}
export const someElementIsSelected = (elements: readonly ExcalidrawElement[]) =>
elements.some(element => element.isSelected);
export function isSomeElementSelected(
elements: readonly ExcalidrawElement[],
): boolean {
return elements.some(element => element.isSelected);
}
/**
* Returns common attribute (picked by `getAttribute` callback) of selected
@ -68,10 +71,14 @@ export function getCommonAttributeOfSelectedElements<T>(
): T | null {
const attributes = Array.from(
new Set(
elements
.filter(element => element.isSelected)
.map(element => getAttribute(element)),
getSelectedElements(elements).map(element => getAttribute(element)),
),
);
return attributes.length === 1 ? attributes[0] : null;
}
export function getSelectedElements(
elements: readonly ExcalidrawElement[],
): readonly ExcalidrawElement[] {
return elements.filter(element => element.isSelected);
}