소스 검색

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.
Enzo Ferey 5 년 전
부모
커밋
6ebd41734f

+ 2 - 2
src/actions/actionDeleteSelected.tsx

@@ -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,
 };

+ 5 - 2
src/actions/actionProperties.tsx

@@ -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

+ 2 - 3
src/clipboard.ts

@@ -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

+ 3 - 2
src/components/ExportDialog.tsx

@@ -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(() => {

+ 2 - 1
src/components/HintViewer.tsx

@@ -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" ||

+ 23 - 20
src/index.tsx

@@ -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 },

+ 2 - 1
src/renderer/renderScene.ts

@@ -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();

+ 2 - 1
src/scene/index.ts

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

+ 12 - 5
src/scene/selection.ts

@@ -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);
+}