mirror of
				https://github.com/excalidraw/excalidraw.git
				synced 2025-10-31 02:44:50 +01:00 
			
		
		
		
	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:
		| @@ -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, | ||||
| }; | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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(() => { | ||||
|   | ||||
| @@ -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" || | ||||
|   | ||||
| @@ -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 }, | ||||
|   | ||||
| @@ -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,9 +3,10 @@ export { | ||||
|   clearSelection, | ||||
|   getSelectedIndices, | ||||
|   deleteSelectedElements, | ||||
|   someElementIsSelected, | ||||
|   isSomeElementSelected, | ||||
|   getElementsWithinSelection, | ||||
|   getCommonAttributeOfSelectedElements, | ||||
|   getSelectedElements, | ||||
| } from "./selection"; | ||||
| export { | ||||
|   exportCanvas, | ||||
|   | ||||
| @@ -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); | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Enzo Ferey
					Enzo Ferey