From 74786de5ec670ec84ba05f63b2a6040068d14f1e Mon Sep 17 00:00:00 2001 From: ColemanDunn <42652642+ColemanDunn@users.noreply.github.com> Date: Thu, 9 Jan 2025 17:41:18 -0700 Subject: [PATCH 1/4] Added right click/POINTER_BUTTON.SECONDARY logic. Mimicked context menu functionality and pointer icon of other popular apps that have this functionality. --- packages/excalidraw/components/App.tsx | 22 ++++++++++++++++++++-- packages/excalidraw/locales/en.json | 2 +- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/packages/excalidraw/components/App.tsx b/packages/excalidraw/components/App.tsx index 7a19eeee0..8d3249fa8 100644 --- a/packages/excalidraw/components/App.tsx +++ b/packages/excalidraw/components/App.tsx @@ -6712,11 +6712,13 @@ class App extends React.Component { public handleCanvasPanUsingWheelOrSpaceDrag = ( event: React.PointerEvent | MouseEvent, ): boolean => { + const isRightClickDragging = event.button === POINTER_BUTTON.SECONDARY; if ( !( gesture.pointers.size <= 1 && (event.button === POINTER_BUTTON.WHEEL || (event.button === POINTER_BUTTON.MAIN && isHoldingSpace) || + isRightClickDragging || isHandToolActive(this.state) || this.state.viewModeEnabled) ) @@ -6744,9 +6746,16 @@ class App extends React.Component { ? false : /Linux/.test(window.navigator.platform); - setCursor(this.interactiveCanvas, CURSOR_TYPE.GRABBING); + if (!isRightClickDragging) { + setCursor(this.interactiveCanvas, CURSOR_TYPE.GRABBING); + } let { clientX: lastX, clientY: lastY } = event; + let hasDragged = false; const onPointerMove = withBatchedUpdatesThrottled((event: PointerEvent) => { + if (isRightClickDragging && !hasDragged) { + setCursor(this.interactiveCanvas, CURSOR_TYPE.GRABBING); + } + hasDragged = true; const deltaX = lastX - event.clientX; const deltaY = lastY - event.clientY; lastX = event.clientX; @@ -6807,6 +6816,11 @@ class App extends React.Component { }); this.savePointer(event.clientX, event.clientY, "up"); window.removeEventListener(EVENT.POINTER_MOVE, onPointerMove); + if (!hasDragged && isRightClickDragging) { + this.handleCanvasContextMenu( + event as React.MouseEvent, + ); + } window.removeEventListener(EVENT.POINTER_UP, teardown); window.removeEventListener(EVENT.BLUR, teardown); onPointerMove.flush(); @@ -9835,7 +9849,7 @@ class App extends React.Component { }; /** updates image cache, refreshing updated elements and/or setting status - to error for images that fail during element creation */ + to error for images that fail during element creation */ private updateImageCache = async ( elements: readonly InitializedExcalidrawImageElement[], files = this.files, @@ -10211,6 +10225,10 @@ class App extends React.Component { return; } + if (isPanning) { + return; + } + const { x, y } = viewportCoordsToSceneCoords(event, this.state); const element = this.getElementAtPosition(x, y, { preferSelected: true, diff --git a/packages/excalidraw/locales/en.json b/packages/excalidraw/locales/en.json index bf99dd58d..f995ac3c8 100644 --- a/packages/excalidraw/locales/en.json +++ b/packages/excalidraw/locales/en.json @@ -319,7 +319,7 @@ }, "hints": { "dismissSearch": "Escape to dismiss search", - "canvasPanning": "To move canvas, hold mouse wheel or spacebar while dragging, or use the hand tool", + "canvasPanning": "To move canvas, hold mouse wheel or spacebar while dragging, drag with right click, or use the hand tool", "linearElement": "Click to start multiple points, drag for single line", "arrowTool": "Click to start multiple points, drag for single line. Press {{arrowShortcut}} again to change arrow type.", "freeDraw": "Click and drag, release when you're finished", From ae40c783e8c760ab532ff961062cfa4109348a35 Mon Sep 17 00:00:00 2001 From: ColemanDunn <42652642+ColemanDunn@users.noreply.github.com> Date: Thu, 9 Jan 2025 18:36:04 -0700 Subject: [PATCH 2/4] renamed handleCanvasPanUsingWheelOrSpaceDrag --- packages/excalidraw/components/App.tsx | 4 ++-- packages/excalidraw/element/textWysiwyg.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/excalidraw/components/App.tsx b/packages/excalidraw/components/App.tsx index 8d3249fa8..b8aa605fe 100644 --- a/packages/excalidraw/components/App.tsx +++ b/packages/excalidraw/components/App.tsx @@ -6386,7 +6386,7 @@ class App extends React.Component { // we must exit before we set `cursorButton` state and `savePointer` // else it will send pointer state & laser pointer events in collab when // panning - if (this.handleCanvasPanUsingWheelOrSpaceDrag(event)) { + if (this.handleCanvasPanUsingWheelOrSpaceDragOrRightClickHold(event)) { return; } @@ -6709,7 +6709,7 @@ class App extends React.Component { }; // Returns whether the event is a panning - public handleCanvasPanUsingWheelOrSpaceDrag = ( + public handleCanvasPanUsingWheelOrSpaceDragOrRightClickHold = ( event: React.PointerEvent | MouseEvent, ): boolean => { const isRightClickDragging = event.button === POINTER_BUTTON.SECONDARY; diff --git a/packages/excalidraw/element/textWysiwyg.tsx b/packages/excalidraw/element/textWysiwyg.tsx index efe27bc6e..418ee30c8 100644 --- a/packages/excalidraw/element/textWysiwyg.tsx +++ b/packages/excalidraw/element/textWysiwyg.tsx @@ -640,7 +640,7 @@ export const textWysiwyg = ({ // trying to pan by clicking inside text area itself -> handle here if (target instanceof HTMLTextAreaElement) { event.preventDefault(); - app.handleCanvasPanUsingWheelOrSpaceDrag(event); + app.handleCanvasPanUsingWheelOrSpaceDragOrRightClickHold(event); } temporarilyDisableSubmit(); return; From 44fada0cf64c6e92286a33558c37e8ee6c56e7a3 Mon Sep 17 00:00:00 2001 From: ColemanDunn <42652642+ColemanDunn@users.noreply.github.com> Date: Mon, 27 Jan 2025 18:05:43 -0700 Subject: [PATCH 3/4] renamed handleCanvasPanUsingWheelOrSpaceDragOrRightClickHold to handleCanvasPan and moved the `this.handleCanvasContextMenu(` to the end of `teardown` --- packages/excalidraw/components/App.tsx | 10 +++++----- packages/excalidraw/element/textWysiwyg.tsx | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/excalidraw/components/App.tsx b/packages/excalidraw/components/App.tsx index b8aa605fe..a6bac55f1 100644 --- a/packages/excalidraw/components/App.tsx +++ b/packages/excalidraw/components/App.tsx @@ -6386,7 +6386,7 @@ class App extends React.Component { // we must exit before we set `cursorButton` state and `savePointer` // else it will send pointer state & laser pointer events in collab when // panning - if (this.handleCanvasPanUsingWheelOrSpaceDragOrRightClickHold(event)) { + if (this.handleCanvasPan(event)) { return; } @@ -6709,7 +6709,7 @@ class App extends React.Component { }; // Returns whether the event is a panning - public handleCanvasPanUsingWheelOrSpaceDragOrRightClickHold = ( + public handleCanvasPan = ( event: React.PointerEvent | MouseEvent, ): boolean => { const isRightClickDragging = event.button === POINTER_BUTTON.SECONDARY; @@ -6816,14 +6816,14 @@ class App extends React.Component { }); this.savePointer(event.clientX, event.clientY, "up"); window.removeEventListener(EVENT.POINTER_MOVE, onPointerMove); + window.removeEventListener(EVENT.POINTER_UP, teardown); + window.removeEventListener(EVENT.BLUR, teardown); + onPointerMove.flush(); if (!hasDragged && isRightClickDragging) { this.handleCanvasContextMenu( event as React.MouseEvent, ); } - window.removeEventListener(EVENT.POINTER_UP, teardown); - window.removeEventListener(EVENT.BLUR, teardown); - onPointerMove.flush(); }), ); window.addEventListener(EVENT.BLUR, teardown); diff --git a/packages/excalidraw/element/textWysiwyg.tsx b/packages/excalidraw/element/textWysiwyg.tsx index 418ee30c8..7be3af3b7 100644 --- a/packages/excalidraw/element/textWysiwyg.tsx +++ b/packages/excalidraw/element/textWysiwyg.tsx @@ -640,7 +640,7 @@ export const textWysiwyg = ({ // trying to pan by clicking inside text area itself -> handle here if (target instanceof HTMLTextAreaElement) { event.preventDefault(); - app.handleCanvasPanUsingWheelOrSpaceDragOrRightClickHold(event); + app.handleCanvasPan(event); } temporarilyDisableSubmit(); return; From 19abf76834befba22f43d7cd026b7829196cd0b3 Mon Sep 17 00:00:00 2001 From: ColemanDunn <42652642+ColemanDunn@users.noreply.github.com> Date: Mon, 27 Jan 2025 18:33:01 -0700 Subject: [PATCH 4/4] renamed `isRightClickDragging` to `isRightClicked` --- packages/excalidraw/components/App.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/excalidraw/components/App.tsx b/packages/excalidraw/components/App.tsx index a6bac55f1..5045a526e 100644 --- a/packages/excalidraw/components/App.tsx +++ b/packages/excalidraw/components/App.tsx @@ -6712,13 +6712,13 @@ class App extends React.Component { public handleCanvasPan = ( event: React.PointerEvent | MouseEvent, ): boolean => { - const isRightClickDragging = event.button === POINTER_BUTTON.SECONDARY; + const isRightClicked = event.button === POINTER_BUTTON.SECONDARY; if ( !( gesture.pointers.size <= 1 && (event.button === POINTER_BUTTON.WHEEL || (event.button === POINTER_BUTTON.MAIN && isHoldingSpace) || - isRightClickDragging || + isRightClicked || isHandToolActive(this.state) || this.state.viewModeEnabled) ) @@ -6746,13 +6746,13 @@ class App extends React.Component { ? false : /Linux/.test(window.navigator.platform); - if (!isRightClickDragging) { + if (!isRightClicked) { setCursor(this.interactiveCanvas, CURSOR_TYPE.GRABBING); } let { clientX: lastX, clientY: lastY } = event; let hasDragged = false; const onPointerMove = withBatchedUpdatesThrottled((event: PointerEvent) => { - if (isRightClickDragging && !hasDragged) { + if (isRightClicked && !hasDragged) { setCursor(this.interactiveCanvas, CURSOR_TYPE.GRABBING); } hasDragged = true; @@ -6819,7 +6819,7 @@ class App extends React.Component { window.removeEventListener(EVENT.POINTER_UP, teardown); window.removeEventListener(EVENT.BLUR, teardown); onPointerMove.flush(); - if (!hasDragged && isRightClickDragging) { + if (!hasDragged && isRightClicked) { this.handleCanvasContextMenu( event as React.MouseEvent, );