From 35a8920db82a902beae4a59253ea41a65371c866 Mon Sep 17 00:00:00 2001 From: Tanish Manku Date: Wed, 26 Feb 2025 15:39:47 -0800 Subject: [PATCH] Show context menu at right click position (#432) --- .../realtime/src/KonvaApp/Nodes/BaseNode.ts | 11 ++++++-- .../NodesManagers/SelectionManager.ts | 28 +++++++++++++++---- .../src/signals/uiAccess/toolbarNode.ts | 1 + 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/frontend/realtime/src/KonvaApp/Nodes/BaseNode.ts b/frontend/realtime/src/KonvaApp/Nodes/BaseNode.ts index 1c749c4444..748f41ae26 100644 --- a/frontend/realtime/src/KonvaApp/Nodes/BaseNode.ts +++ b/frontend/realtime/src/KonvaApp/Nodes/BaseNode.ts @@ -4,6 +4,8 @@ import { SelectionManager } from "../NodesManagers"; import { highlightStrokeWidth, primaryOrange } from "./constants"; import { NodeData, Position } from "../types"; import { RealTimeDrawEngine } from "../RenderingPrimitives/RealTimeDrawEngine"; +import { MouseEventHandler } from "react"; +import { KonvaEventObject } from "konva/lib/Node"; export abstract class BaseNode { protected selectionManagerRef: SelectionManager; @@ -151,15 +153,18 @@ export abstract class BaseNode { } }; - const handleRightClick = () => { + const handleRightClick = (e: KonvaEventObject) => { this.selectionManagerRef.clearSelection(); - this.selectionManagerRef.selectNode(this, undefined, true); + this.selectionManagerRef.selectNode(this, undefined, { + show: true, + at: { x: e.evt.x, y: e.evt.y } + }); } this.kNode.on("contextmenu", (e) => { console.log("CONTEXT MENU", e); e.evt.preventDefault(); - handleRightClick(); + handleRightClick(e); }); this.kNode.on("mousedown", (e) => { diff --git a/frontend/realtime/src/KonvaApp/NodesManagers/SelectionManager.ts b/frontend/realtime/src/KonvaApp/NodesManagers/SelectionManager.ts index 3e35151d19..ce2b88d6d2 100644 --- a/frontend/realtime/src/KonvaApp/NodesManagers/SelectionManager.ts +++ b/frontend/realtime/src/KonvaApp/NodesManagers/SelectionManager.ts @@ -80,7 +80,12 @@ export class SelectionManager { public isDisabled() { return this.disable; } - public selectNode(node: MediaNode, doNotDraw?: boolean, showContextMenu = false): boolean { + public selectNode(node: MediaNode, doNotDraw?: boolean, contextMenu: { + show?: boolean, + at?: { x: number, y: number } + } = { + show: false, + }): boolean { if ( this.disabled || // no-op when disabled this.selectedNodes.has(node) || // if the node is already selected @@ -101,9 +106,9 @@ export class SelectionManager { this.updateNodeTransformer(); this.updateContextComponents(); - console.log("SelectionManager > selectNode > showContextMenu", showContextMenu); - if (showContextMenu) { - this.showContextComponents(); + console.log("SelectionManager > selectNode > showContextMenu", contextMenu.show); + if (contextMenu.show) { + this.showContextComponents(contextMenu.at); } if (!doNotDraw) { @@ -266,10 +271,15 @@ export class SelectionManager { uiAccess.loadingBar.hide(); } - public showContextComponents() { + public showContextComponents(at?: { x: number, y: number }) { const showOrUpdate = uiAccess.toolbarNode.isShowing() ? uiAccess.toolbarNode.update : uiAccess.toolbarNode.show; + + // If a position is received, set it + // Otherwise use the previous position + const position = at ? at : uiAccess.toolbarNode.signal.value.position; + const node = this.selectedNodes.values().next().value; if (node === undefined) { this.hideContextComponents(); @@ -281,6 +291,7 @@ export class SelectionManager { knodeIds: [node.kNode.id()], locked: node.isLocked(), buttonStates: getImageNodeButtonStates({ locked: node.isLocked() }), + position: position }); } else if (node instanceof ShapeNode) { @@ -289,7 +300,8 @@ export class SelectionManager { knodeIds: [node.kNode.id()], locked: node.isLocked(), buttonStates: getShapeNodeButtonStates({ locked: node.isLocked() }), - color: node.color + color: node.color, + position: position }); } else if (node instanceof TextNode) { @@ -298,12 +310,14 @@ export class SelectionManager { locked: node.isLocked(), buttonStates: getTextNodeButtonStates({ locked: node.isLocked() }), color: node.getNodeData({ x: 0, y: 0 })?.textNodeData?.color ?? "#000000", + position: position }); } else if (node instanceof VideoNode) { showOrUpdate({ locked: node.isLocked(), buttonStates: getVideoNodeButtonStates({ locked: node.isLocked() }), + position: position }); } @@ -319,6 +333,7 @@ export class SelectionManager { showOrUpdate({ locked: node.isLocked(), buttonStates: getMultiSelectButtonStates({ locked: node.isLocked() }), + position: position }); } } @@ -348,6 +363,7 @@ export class SelectionManager { // console.log("setting lock"); uiAccess.toolbarNode.setLocked(node.isLocked()); } + console.debug("Setting context position to:", coord) uiAccess.toolbarNode.setPosition({ x: coord.x, y: coord.y, diff --git a/frontend/realtime/src/signals/uiAccess/toolbarNode.ts b/frontend/realtime/src/signals/uiAccess/toolbarNode.ts index 9b9dcc88b1..b977385019 100644 --- a/frontend/realtime/src/signals/uiAccess/toolbarNode.ts +++ b/frontend/realtime/src/signals/uiAccess/toolbarNode.ts @@ -18,6 +18,7 @@ export interface ContextualToolbarProps extends ContextualUi { lockDisabled: boolean; buttonStates: ButtonStates; color: string; + position: { x: number, y: number }; } interface PartialContextualToolbarProps extends Partial> {