Right-click on mind-map node sometimes shows wrong context menu #154

Open
opened 2026-05-06 09:34:36 +00:00 by AhmedHanafy725 · 3 comments
Member

Right-clicking a mind-map node should consistently show the per-node menu (Add Comment, Delete Node, etc.). Currently, sometimes it shows the mindmap-group context menu instead, depending on where the click lands or the order of event handlers.

Expected

Right-click on any mind-map node (root, branch, leaf) — including on the text label, the rounded rect, anywhere inside the node — always opens the per-node menu wired in mindmap.js (showNodeContextMenu).

Actual

Sometimes the click is captured by the parent group / canvas before the node-level handler runs, and the user sees a different menu (the canvas-level or group-level right-click menu).

Repro

  1. Create a mindmap on a board
  2. Right-click on different parts of a node (label vs rect, near edges vs center)
  3. Observe that the menu shown is sometimes the per-node one, sometimes another menu

Likely root cause: event bubbling. The node rect has a contextmenu handler but the label Konva.Text does not, so right-click on the label propagates to the parent group / stage, where another handler may run.

Right-clicking a mind-map node should consistently show the per-node menu (Add Comment, Delete Node, etc.). Currently, sometimes it shows the mindmap-group context menu instead, depending on where the click lands or the order of event handlers. ## Expected Right-click on any mind-map node (root, branch, leaf) — including on the text label, the rounded rect, anywhere inside the node — always opens the per-node menu wired in `mindmap.js` (`showNodeContextMenu`). ## Actual Sometimes the click is captured by the parent group / canvas before the node-level handler runs, and the user sees a different menu (the canvas-level or group-level right-click menu). ## Repro 1. Create a mindmap on a board 2. Right-click on different parts of a node (label vs rect, near edges vs center) 3. Observe that the menu shown is sometimes the per-node one, sometimes another menu Likely root cause: event bubbling. The node rect has a `contextmenu` handler but the label `Konva.Text` does not, so right-click on the label propagates to the parent group / stage, where another handler may run.
Author
Member

Implementation Spec

Root cause

In crates/hero_whiteboard_ui/static/web/js/whiteboard/mindmap.js only nodeRect has a contextmenu listener (around line 424). The other Konva sub-shapes inside the same Konva.Group — the label Konva.Text, the collapse indicator (+/), the add-child + button, and the comment icon (💬) — have no contextmenu listener.

The competing handler is the document-level listener in crates/hero_whiteboard_ui/static/web/js/whiteboard/contextmenu.js (~line 15):

document.addEventListener('contextmenu', function(e) { ... });

The node-level handler calls e.evt.preventDefault() + e.evt.stopPropagation(), which is what stops the document handler — but only when the right-click hits nodeRect directly. When the cursor is over the label, the toggle, the add-child button, or the comment icon, Konva's intersection returns that child, no Konva listener fires, the DOM event is never preventDefault-ed, and the document-level handler shows the generic per-object canvas menu. (e.cancelBubble = true controls Konva-internal bubbling, not the native DOM event — the load-bearing calls are on e.evt.)

Approach

Extract the existing handler into a small local function and attach it to every per-node sub-shape (nodeRect, label, indicator, addBtn, commentIcon).

Why not attach to the parent group: group is the single mindmap-object Konva.Group shared by all nodes plus the title, direction-flip arrow, background, and connector lines. A handler on group would fire for right-clicks on the title, flip arrow, blank background, or connectors too — wrong. Per-shape attachment matches the existing pattern (the dblclick handler is already duplicated on nodeRect and label).

Files to Modify

  • crates/hero_whiteboard_ui/static/web/js/whiteboard/mindmap.js — only this file.

Implementation Plan

Step 1 — wire contextmenu on every per-node shape

Files: mindmap.js

In drawNodes:

  1. Define a local handler near the bottom of the per-node setup:
    function onNodeContextMenu(e) {
        e.evt.preventDefault();
        e.evt.stopPropagation();
        e.cancelBubble = true;
        showNodeContextMenu(group, node, nodeRect, e.evt);
    }
    
  2. Replace the existing inline nodeRect.on('contextmenu', ...) with nodeRect.on('contextmenu', onNodeContextMenu);.
  3. Add label.on('contextmenu', onNodeContextMenu);.
  4. Inside the if (node.children && node.children.length > 0) block, after the indicator is added: indicator.on('contextmenu', onNodeContextMenu);.
  5. After addBtn is added: addBtn.on('contextmenu', onNodeContextMenu);.
  6. Inside the if (node.comment) block, after commentIcon is added: commentIcon.on('contextmenu', onNodeContextMenu);.

Leave all other handlers (click, dblclick, dbltap, mouseenter, mouseleave) intact.

Acceptance Criteria

Right-clicking on each of these node surfaces must show the per-node menu (Add/Edit Comment, Add Child, Delete Node) — never the canvas/object menu:

  • Node body / background rect
  • Text label (any glyph, any padding pixel)
  • Collapse +/ indicator (when present)
  • Add-child + button
  • Comment 💬 icon (when present)

And the following must continue to show the canvas/object menu (intentionally not per-node):

  • Mindmap title text (mm-title)
  • Direction-flip arrow (mm-flip)
  • Connector lines between nodes
  • Empty area outside any mindmap

What NOT to break

  • Locked nodes: existing handler has no lock check; preserve.
  • Double-click to edit (dblclick/dbltap) on nodeRect and label — independent.
  • Left-click handlers on indicator, addBtn, commentIcon — Konva fires click only on left mouseup; adding a contextmenu listener does not steal left clicks.
  • Drag-start on the mindmap group — contextmenu does not initiate drags.
  • The per-node menu's outside-click dismissal — unchanged.
  • Document-level menu in contextmenu.js — unchanged.
## Implementation Spec ### Root cause In `crates/hero_whiteboard_ui/static/web/js/whiteboard/mindmap.js` only `nodeRect` has a `contextmenu` listener (around line 424). The other Konva sub-shapes inside the same Konva.Group — the label `Konva.Text`, the collapse indicator (`+`/`−`), the add-child `+` button, and the comment icon (💬) — have no `contextmenu` listener. The competing handler is the document-level listener in `crates/hero_whiteboard_ui/static/web/js/whiteboard/contextmenu.js` (~line 15): ```js document.addEventListener('contextmenu', function(e) { ... }); ``` The node-level handler calls `e.evt.preventDefault()` + `e.evt.stopPropagation()`, which is what stops the document handler — but only when the right-click hits `nodeRect` directly. When the cursor is over the label, the toggle, the add-child button, or the comment icon, Konva's intersection returns that child, no Konva listener fires, the DOM event is never `preventDefault`-ed, and the document-level handler shows the generic per-object canvas menu. (`e.cancelBubble = true` controls Konva-internal bubbling, not the native DOM event — the load-bearing calls are on `e.evt`.) ### Approach Extract the existing handler into a small local function and attach it to **every per-node sub-shape** (`nodeRect`, `label`, `indicator`, `addBtn`, `commentIcon`). Why not attach to the parent `group`: `group` is the single mindmap-object Konva.Group shared by **all** nodes plus the title, direction-flip arrow, background, and connector lines. A handler on `group` would fire for right-clicks on the title, flip arrow, blank background, or connectors too — wrong. Per-shape attachment matches the existing pattern (the `dblclick` handler is already duplicated on `nodeRect` and `label`). ### Files to Modify - `crates/hero_whiteboard_ui/static/web/js/whiteboard/mindmap.js` — only this file. ### Implementation Plan #### Step 1 — wire `contextmenu` on every per-node shape Files: `mindmap.js` In `drawNodes`: 1. Define a local handler near the bottom of the per-node setup: ```js function onNodeContextMenu(e) { e.evt.preventDefault(); e.evt.stopPropagation(); e.cancelBubble = true; showNodeContextMenu(group, node, nodeRect, e.evt); } ``` 2. Replace the existing inline `nodeRect.on('contextmenu', ...)` with `nodeRect.on('contextmenu', onNodeContextMenu);`. 3. Add `label.on('contextmenu', onNodeContextMenu);`. 4. Inside the `if (node.children && node.children.length > 0)` block, after the `indicator` is added: `indicator.on('contextmenu', onNodeContextMenu);`. 5. After `addBtn` is added: `addBtn.on('contextmenu', onNodeContextMenu);`. 6. Inside the `if (node.comment)` block, after `commentIcon` is added: `commentIcon.on('contextmenu', onNodeContextMenu);`. Leave all other handlers (`click`, `dblclick`, `dbltap`, `mouseenter`, `mouseleave`) intact. ### Acceptance Criteria Right-clicking on each of these node surfaces must show the per-node menu (Add/Edit Comment, Add Child, Delete Node) — never the canvas/object menu: - [ ] Node body / background rect - [ ] Text label (any glyph, any padding pixel) - [ ] Collapse `+`/`−` indicator (when present) - [ ] Add-child `+` button - [ ] Comment 💬 icon (when present) And the following must continue to show the **canvas/object** menu (intentionally not per-node): - [ ] Mindmap title text (`mm-title`) - [ ] Direction-flip arrow (`mm-flip`) - [ ] Connector lines between nodes - [ ] Empty area outside any mindmap ### What NOT to break - Locked nodes: existing handler has no lock check; preserve. - Double-click to edit (`dblclick`/`dbltap`) on `nodeRect` and `label` — independent. - Left-click handlers on `indicator`, `addBtn`, `commentIcon` — Konva fires `click` only on left mouseup; adding a `contextmenu` listener does not steal left clicks. - Drag-start on the mindmap group — `contextmenu` does not initiate drags. - The per-node menu's outside-click dismissal — unchanged. - Document-level menu in `contextmenu.js` — unchanged.
Author
Member

Validation

Check Result
Files changed 1 file (mindmap.js)
cargo check --workspace pass
cargo test --workspace --lib pass
## Validation | Check | Result | |---|---| | Files changed | 1 file (mindmap.js) | | `cargo check --workspace` | pass | | `cargo test --workspace --lib` | pass |
Author
Member

Implementation summary

Changes

  • crates/hero_whiteboard_ui/static/web/js/whiteboard/mindmap.js — extracted the existing per-node contextmenu handler into a local onNodeContextMenu function and attached it to every per-node sub-shape: nodeRect, label, indicator (when present), addBtn, and commentIcon (when present). Right-click hits on any of these now preventDefault + stopPropagation the native event so the document-level handler in contextmenu.js no longer fires the canvas/object menu.

Validation

  • cargo check --workspace: pass
  • cargo test --workspace --lib: pass
  • Diff scope: 1 file (mindmap.js, +12/-4)

Notes

Intentionally not changed: right-click on the mindmap title text (mm-title), the direction-flip arrow (mm-flip), connector lines, and the empty area inside the mindmap's bg rect — those continue to show the canvas/object menu (no per-node target there).

The handler is shared by all per-node shapes, so locked-node behavior, double-click-to-edit, single-click handlers on the toggle / add / comment icons, and drag-start on the parent group are all unaffected.

## Implementation summary ### Changes - `crates/hero_whiteboard_ui/static/web/js/whiteboard/mindmap.js` — extracted the existing per-node `contextmenu` handler into a local `onNodeContextMenu` function and attached it to every per-node sub-shape: `nodeRect`, `label`, `indicator` (when present), `addBtn`, and `commentIcon` (when present). Right-click hits on any of these now `preventDefault` + `stopPropagation` the native event so the document-level handler in `contextmenu.js` no longer fires the canvas/object menu. ### Validation - `cargo check --workspace`: pass - `cargo test --workspace --lib`: pass - Diff scope: 1 file (mindmap.js, +12/-4) ### Notes Intentionally not changed: right-click on the mindmap title text (`mm-title`), the direction-flip arrow (`mm-flip`), connector lines, and the empty area inside the mindmap's bg rect — those continue to show the canvas/object menu (no per-node target there). The handler is shared by all per-node shapes, so locked-node behavior, double-click-to-edit, single-click handlers on the toggle / add / comment icons, and drag-start on the parent group are all unaffected.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lhumina_code/hero_whiteboard#154
No description provided.