mirror of
				https://github.com/excalidraw/excalidraw.git
				synced 2025-11-03 20:34:40 +01:00 
			
		
		
		
	Compare commits
	
		
			3 Commits
		
	
	
		
			zsviczian-
			...
			zsviczian-
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					34daf09b4a | ||
| 
						 | 
					2bca4c258d | ||
| 
						 | 
					b0ca8f8126 | 
@@ -18,7 +18,6 @@ import {
 | 
			
		||||
  getDefaultAppState,
 | 
			
		||||
  isEraserActive,
 | 
			
		||||
  isHandToolActive,
 | 
			
		||||
  isLaserPointerActive,
 | 
			
		||||
} from "../appState";
 | 
			
		||||
import { DEFAULT_CANVAS_BACKGROUND_PICKS } from "../colors";
 | 
			
		||||
import { Bounds } from "../element/bounds";
 | 
			
		||||
@@ -440,44 +439,3 @@ export const actionToggleHandTool = register({
 | 
			
		||||
  },
 | 
			
		||||
  keyTest: (event) => event.key === KEYS.H,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export const actionToggleLaserPointer = register({
 | 
			
		||||
  name: "toggleLaserPointerTool",
 | 
			
		||||
  viewMode: true,
 | 
			
		||||
  trackEvent: { category: "menu" },
 | 
			
		||||
  perform(elements, appState, _, app) {
 | 
			
		||||
    let activeTool: AppState["activeTool"];
 | 
			
		||||
 | 
			
		||||
    if (isLaserPointerActive(appState)) {
 | 
			
		||||
      activeTool = updateActiveTool(appState, {
 | 
			
		||||
        ...(appState.activeTool.lastActiveTool || {
 | 
			
		||||
          type: appState.viewModeEnabled ? "hand" : "selection",
 | 
			
		||||
        }),
 | 
			
		||||
        lastActiveToolBeforeEraser: null,
 | 
			
		||||
      });
 | 
			
		||||
      setCursor(
 | 
			
		||||
        app.interactiveCanvas,
 | 
			
		||||
        appState.viewModeEnabled ? CURSOR_TYPE.GRAB : CURSOR_TYPE.POINTER,
 | 
			
		||||
      );
 | 
			
		||||
    } else {
 | 
			
		||||
      activeTool = updateActiveTool(appState, {
 | 
			
		||||
        type: "laser",
 | 
			
		||||
        lastActiveToolBeforeEraser: appState.activeTool,
 | 
			
		||||
      });
 | 
			
		||||
      setCursor(app.interactiveCanvas, CURSOR_TYPE.CROSSHAIR);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      appState: {
 | 
			
		||||
        ...appState,
 | 
			
		||||
        selectedElementIds: {},
 | 
			
		||||
        selectedGroupIds: {},
 | 
			
		||||
        activeEmbeddable: null,
 | 
			
		||||
        activeTool,
 | 
			
		||||
      },
 | 
			
		||||
      commitToHistory: true,
 | 
			
		||||
    };
 | 
			
		||||
  },
 | 
			
		||||
  checked: (appState) => appState.activeTool.type === "laser",
 | 
			
		||||
  contextItemLabel: "labels.laser",
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -36,7 +36,6 @@ export type ShortcutName =
 | 
			
		||||
      | "flipVertical"
 | 
			
		||||
      | "hyperlink"
 | 
			
		||||
      | "toggleElementLock"
 | 
			
		||||
      | "toggleLaserPointerTool"
 | 
			
		||||
    >
 | 
			
		||||
  | "saveScene"
 | 
			
		||||
  | "imageExport";
 | 
			
		||||
@@ -84,7 +83,6 @@ const shortcutMap: Record<ShortcutName, string[]> = {
 | 
			
		||||
  viewMode: [getShortcutKey("Alt+R")],
 | 
			
		||||
  hyperlink: [getShortcutKey("CtrlOrCmd+K")],
 | 
			
		||||
  toggleElementLock: [getShortcutKey("CtrlOrCmd+Shift+L")],
 | 
			
		||||
  toggleLaserPointerTool: [getShortcutKey("K")],
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const getShortcutFromShortcutName = (name: ShortcutName) => {
 | 
			
		||||
 
 | 
			
		||||
@@ -124,8 +124,7 @@ export type ActionName =
 | 
			
		||||
  | "setFrameAsActiveTool"
 | 
			
		||||
  | "setEmbeddableAsActiveTool"
 | 
			
		||||
  | "createContainerFromText"
 | 
			
		||||
  | "wrapTextInContainer"
 | 
			
		||||
  | "toggleLaserPointerTool";
 | 
			
		||||
  | "wrapTextInContainer";
 | 
			
		||||
 | 
			
		||||
export type PanelComponentProps = {
 | 
			
		||||
  elements: readonly ExcalidrawElement[];
 | 
			
		||||
 
 | 
			
		||||
@@ -266,11 +266,3 @@ export const isHandToolActive = ({
 | 
			
		||||
}) => {
 | 
			
		||||
  return activeTool.type === "hand";
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const isLaserPointerActive = ({
 | 
			
		||||
  activeTool,
 | 
			
		||||
}: {
 | 
			
		||||
  activeTool: AppState["activeTool"];
 | 
			
		||||
}) => {
 | 
			
		||||
  return activeTool.type === "laser";
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -46,7 +46,6 @@ import {
 | 
			
		||||
  getDefaultAppState,
 | 
			
		||||
  isEraserActive,
 | 
			
		||||
  isHandToolActive,
 | 
			
		||||
  isLaserPointerActive,
 | 
			
		||||
} from "../appState";
 | 
			
		||||
import { parseClipboard } from "../clipboard";
 | 
			
		||||
import {
 | 
			
		||||
@@ -344,11 +343,7 @@ import {
 | 
			
		||||
  actionRemoveAllElementsFromFrame,
 | 
			
		||||
  actionSelectAllElementsInFrame,
 | 
			
		||||
} from "../actions/actionFrame";
 | 
			
		||||
import {
 | 
			
		||||
  actionToggleHandTool,
 | 
			
		||||
  zoomToFit,
 | 
			
		||||
  actionToggleLaserPointer,
 | 
			
		||||
} from "../actions/actionCanvas";
 | 
			
		||||
import { actionToggleHandTool, zoomToFit } from "../actions/actionCanvas";
 | 
			
		||||
import { jotaiStore } from "../jotai";
 | 
			
		||||
import { activeConfirmDialogAtom } from "./ActiveConfirmDialog";
 | 
			
		||||
import {
 | 
			
		||||
@@ -2915,22 +2910,7 @@ class App extends React.Component<AppProps, AppState> {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (event.key === KEYS.K && !event.altKey && !event[KEYS.CTRL_OR_CMD]) {
 | 
			
		||||
        if (isLaserPointerActive(this.state)) {
 | 
			
		||||
          this.setActiveTool({
 | 
			
		||||
            type: "selection",
 | 
			
		||||
          });
 | 
			
		||||
        } else {
 | 
			
		||||
          this.setActiveTool({ type: "laser" });
 | 
			
		||||
        }
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (this.state.viewModeEnabled) {
 | 
			
		||||
        //revert to hand in case a key is pressed (K is handled above)
 | 
			
		||||
        if (event.key !== KEYS.K) {
 | 
			
		||||
          this.setActiveTool({ type: "selection" });
 | 
			
		||||
        }
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
@@ -3080,6 +3060,15 @@ class App extends React.Component<AppProps, AppState> {
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (event.key === KEYS.K && !event.altKey && !event[KEYS.CTRL_OR_CMD]) {
 | 
			
		||||
        if (this.state.activeTool.type === "laser") {
 | 
			
		||||
          this.setActiveTool({ type: "selection" });
 | 
			
		||||
        } else {
 | 
			
		||||
          this.setActiveTool({ type: "laser" });
 | 
			
		||||
        }
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (
 | 
			
		||||
        event[KEYS.CTRL_OR_CMD] &&
 | 
			
		||||
        (event.key === KEYS.BACKSPACE || event.key === KEYS.DELETE)
 | 
			
		||||
@@ -3623,18 +3612,6 @@ class App extends React.Component<AppProps, AppState> {
 | 
			
		||||
    if (this.state.multiElement) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (this.state.viewModeEnabled) {
 | 
			
		||||
      if (this.state.activeTool.type === "laser") {
 | 
			
		||||
        this.setActiveTool({ type: "selection" });
 | 
			
		||||
        setCursor(this.interactiveCanvas, CURSOR_TYPE.GRAB);
 | 
			
		||||
      } else {
 | 
			
		||||
        this.setActiveTool({ type: "laser" });
 | 
			
		||||
        setCursor(this.interactiveCanvas, CURSOR_TYPE.CROSSHAIR);
 | 
			
		||||
      }
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // we should only be able to double click when mode is selection
 | 
			
		||||
    if (this.state.activeTool.type !== "selection") {
 | 
			
		||||
      return;
 | 
			
		||||
@@ -4762,7 +4739,7 @@ class App extends React.Component<AppProps, AppState> {
 | 
			
		||||
        (event.button === POINTER_BUTTON.WHEEL ||
 | 
			
		||||
          (event.button === POINTER_BUTTON.MAIN && isHoldingSpace) ||
 | 
			
		||||
          isHandToolActive(this.state) ||
 | 
			
		||||
          (this.state.viewModeEnabled && !isLaserPointerActive(this.state)))
 | 
			
		||||
          this.state.viewModeEnabled)
 | 
			
		||||
      ) ||
 | 
			
		||||
      isTextElement(this.state.editingElement)
 | 
			
		||||
    ) {
 | 
			
		||||
@@ -8166,7 +8143,6 @@ class App extends React.Component<AppProps, AppState> {
 | 
			
		||||
          actionToggleZenMode,
 | 
			
		||||
          actionToggleViewMode,
 | 
			
		||||
          actionToggleStats,
 | 
			
		||||
          actionToggleLaserPointer,
 | 
			
		||||
        ];
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -155,7 +155,9 @@ const InteractiveCanvas = (props: InteractiveCanvasProps) => {
 | 
			
		||||
      onPointerCancel={props.onPointerCancel}
 | 
			
		||||
      onTouchMove={props.onTouchMove}
 | 
			
		||||
      onPointerDown={props.onPointerDown}
 | 
			
		||||
      onDoubleClick={props.onDoubleClick}
 | 
			
		||||
      onDoubleClick={
 | 
			
		||||
        props.appState.viewModeEnabled ? undefined : props.onDoubleClick
 | 
			
		||||
      }
 | 
			
		||||
    >
 | 
			
		||||
      {t("labels.drawingCanvas")}
 | 
			
		||||
    </canvas>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
import { updateBoundElements } from "./binding";
 | 
			
		||||
import { getCommonBounds } from "./bounds";
 | 
			
		||||
import { Bounds, getCommonBounds } from "./bounds";
 | 
			
		||||
import { mutateElement } from "./mutateElement";
 | 
			
		||||
import { getPerfectElementSize } from "./sizeHelpers";
 | 
			
		||||
import { NonDeletedExcalidrawElement } from "./types";
 | 
			
		||||
@@ -41,14 +41,16 @@ export const dragSelectedElements = (
 | 
			
		||||
    elementsInFrames.forEach((element) => elementsToUpdate.add(element));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const commonBounds = getCommonBounds(Array.from(elementsToUpdate));
 | 
			
		||||
  const adjustedOffset = calculateOffset(
 | 
			
		||||
    commonBounds,
 | 
			
		||||
    offset,
 | 
			
		||||
    snapOffset,
 | 
			
		||||
    gridSize,
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  elementsToUpdate.forEach((element) => {
 | 
			
		||||
    updateElementCoords(
 | 
			
		||||
      pointerDownState,
 | 
			
		||||
      element,
 | 
			
		||||
      offset,
 | 
			
		||||
      snapOffset,
 | 
			
		||||
      gridSize,
 | 
			
		||||
    );
 | 
			
		||||
    updateElementCoords(pointerDownState, element, adjustedOffset);
 | 
			
		||||
    // update coords of bound text only if we're dragging the container directly
 | 
			
		||||
    // (we don't drag the group that it's part of)
 | 
			
		||||
    if (
 | 
			
		||||
@@ -66,13 +68,7 @@ export const dragSelectedElements = (
 | 
			
		||||
        // updating its coords again
 | 
			
		||||
        (!textElement.frameId || !frames.includes(textElement.frameId))
 | 
			
		||||
      ) {
 | 
			
		||||
        updateElementCoords(
 | 
			
		||||
          pointerDownState,
 | 
			
		||||
          textElement,
 | 
			
		||||
          offset,
 | 
			
		||||
          snapOffset,
 | 
			
		||||
          gridSize,
 | 
			
		||||
        );
 | 
			
		||||
        updateElementCoords(pointerDownState, textElement, adjustedOffset);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    updateBoundElements(element, {
 | 
			
		||||
@@ -81,23 +77,20 @@ export const dragSelectedElements = (
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const updateElementCoords = (
 | 
			
		||||
  pointerDownState: PointerDownState,
 | 
			
		||||
  element: NonDeletedExcalidrawElement,
 | 
			
		||||
const calculateOffset = (
 | 
			
		||||
  commonBounds: Bounds,
 | 
			
		||||
  dragOffset: { x: number; y: number },
 | 
			
		||||
  snapOffset: { x: number; y: number },
 | 
			
		||||
  gridSize: AppState["gridSize"],
 | 
			
		||||
) => {
 | 
			
		||||
  const originalElement =
 | 
			
		||||
    pointerDownState.originalElements.get(element.id) ?? element;
 | 
			
		||||
 | 
			
		||||
  let nextX = originalElement.x + dragOffset.x + snapOffset.x;
 | 
			
		||||
  let nextY = originalElement.y + dragOffset.y + snapOffset.y;
 | 
			
		||||
): { x: number; y: number } => {
 | 
			
		||||
  const [x, y] = commonBounds;
 | 
			
		||||
  let nextX = x + dragOffset.x + snapOffset.x;
 | 
			
		||||
  let nextY = y + dragOffset.y + snapOffset.y;
 | 
			
		||||
 | 
			
		||||
  if (snapOffset.x === 0 || snapOffset.y === 0) {
 | 
			
		||||
    const [nextGridX, nextGridY] = getGridPoint(
 | 
			
		||||
      originalElement.x + dragOffset.x,
 | 
			
		||||
      originalElement.y + dragOffset.y,
 | 
			
		||||
      x + dragOffset.x,
 | 
			
		||||
      y + dragOffset.y,
 | 
			
		||||
      gridSize,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
@@ -109,6 +102,22 @@ const updateElementCoords = (
 | 
			
		||||
      nextY = nextGridY;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return {
 | 
			
		||||
    x: nextX - x,
 | 
			
		||||
    y: nextY - y,
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const updateElementCoords = (
 | 
			
		||||
  pointerDownState: PointerDownState,
 | 
			
		||||
  element: NonDeletedExcalidrawElement,
 | 
			
		||||
  dragOffset: { x: number; y: number },
 | 
			
		||||
) => {
 | 
			
		||||
  const originalElement =
 | 
			
		||||
    pointerDownState.originalElements.get(element.id) ?? element;
 | 
			
		||||
 | 
			
		||||
  const nextX = originalElement.x + dragOffset.x;
 | 
			
		||||
  const nextY = originalElement.y + dragOffset.y;
 | 
			
		||||
 | 
			
		||||
  mutateElement(element, {
 | 
			
		||||
    x: nextX,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,5 @@
 | 
			
		||||
{
 | 
			
		||||
  "labels": {
 | 
			
		||||
    "laser": "Toggle laser pointer",
 | 
			
		||||
    "paste": "Paste",
 | 
			
		||||
    "pasteAsPlaintext": "Paste as plaintext",
 | 
			
		||||
    "pasteCharts": "Paste charts",
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user