Skip to content

Commit

Permalink
Resize handler detection should not be active when moving multip… (ex…
Browse files Browse the repository at this point in the history
…calidraw#767)

* Fix bug.

* Implement `getSelectedElements`.

* Explicit condition.

* Respect variable naming.

* Keep state consistent.

* Use `isSomeElementSelected` abstraction.

* Missing ones.
  • Loading branch information
enzoferey authored Feb 16, 2020

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent ad72946 commit 6ebd417
Showing 9 changed files with 53 additions and 37 deletions.
4 changes: 2 additions & 2 deletions src/actions/actionDeleteSelected.tsx
Original file line number Diff line number Diff line change
@@ -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,
};
7 changes: 5 additions & 2 deletions src/actions/actionProperties.tsx
Original file line number Diff line number Diff line change
@@ -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
5 changes: 2 additions & 3 deletions src/clipboard.ts
Original file line number Diff line number Diff line change
@@ -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
5 changes: 3 additions & 2 deletions src/components/ExportDialog.tsx
Original file line number Diff line number Diff line change
@@ -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(() => {
3 changes: 2 additions & 1 deletion src/components/HintViewer.tsx
Original file line number Diff line number Diff line change
@@ -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" ||
43 changes: 23 additions & 20 deletions src/index.tsx
Original file line number Diff line number Diff line change
@@ -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,
);
this.setState({
resizingElement: resizeElement ? resizeElement.element : null,
});

if (resizeElement) {
const selectedElements = getSelectedElements(elements);
if (selectedElements.length === 1 && resizeElement) {
this.setState({
resizingElement: resizeElement
? resizeElement.element
: null,
});

resizeHandle = resizeElement.resizeHandle;
document.documentElement.style.cursor = getCursorForResizingElement(
resizeElement,
@@ -1087,13 +1093,11 @@ export class App extends React.Component<any, AppState> {
...element,
isSelected: false,
})),
...elements
.filter(element => element.isSelected)
.map(element => {
const newElement = duplicateElement(element);
newElement.isSelected = true;
return newElement;
}),
...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 },
3 changes: 2 additions & 1 deletion src/renderer/renderScene.ts
Original file line number Diff line number Diff line change
@@ -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();
3 changes: 2 additions & 1 deletion src/scene/index.ts
Original file line number Diff line number Diff line change
@@ -3,9 +3,10 @@ export {
clearSelection,
getSelectedIndices,
deleteSelectedElements,
someElementIsSelected,
isSomeElementSelected,
getElementsWithinSelection,
getCommonAttributeOfSelectedElements,
getSelectedElements,
} from "./selection";
export {
exportCanvas,
17 changes: 12 additions & 5 deletions src/scene/selection.ts
Original file line number Diff line number Diff line change
@@ -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);
}

0 comments on commit 6ebd417

Please sign in to comment.