[{"data":1,"prerenderedAt":7},["ShallowReactive",2],{"example-markdown-content:en:node-context-menu-via-slots":3},{"markdown":4,"isPaidExample":5,"isTruncated":5,"charLimit":6},"# Node Context Menus with relation-graph Slots\n\n## What This Example Builds\nThis example builds a read-only relation graph whose nodes are rendered as green circular icon badges instead of the default node body. Clicking or right-clicking a node opens a floating action menu at the pointer position inside the graph scene, and choosing one of the fixed actions shows a success message before the menu closes.\n\nThe main lesson is the split of responsibilities between two slot layers: `RGSlotOnNode` provides the interactive trigger surface, and `RGSlotOnView` renders the overlay menu inside the graph viewport.\n\n## How the Data Is Organized\nThe graph data is declared inline inside `initializeGraph` as a single `RGJsonData` object. It uses `rootId: '2'`, a `nodes` array with `id`, `text`, and `data.myicon`, and a `lines` array where every record has explicit `id`, `from`, `to`, and `text` fields.\n\nThere is no preprocessing step before `setJsonData` beyond assembling this object in code. In a production graph, `data.myicon` could be replaced with role, status, type, or category metadata that drives both the node appearance and the available node actions.\n\n## How relation-graph Is Used\n`RGProvider` wraps the demo so `RGHooks.useGraphInstance()` can read the active graph instance. A `useEffect` waits for that instance and then calls `setJsonData()`, `moveToCenter()`, and `zoomToFit()` to load the sample network and fit it into view.\n\nThe local `graphOptions` object only adjusts default presentation: node color is green, node shape is circular, and junction points connect on the border. The reviewed files do not set an explicit layout option, so the final arrangement depends on relation-graph defaults outside this example.\n\n`RGSlotOnNode` replaces the default node body with custom DOM. That slot reads `node.data.myicon`, renders a Lucide icon, adds a caption block under the node, and binds both `onClick` and `onContextMenu` to the same menu-opening handler.\n\n`RGSlotOnView` renders the menu panel inside the graph view layer. React state tracks the active node, whether the panel is visible, and the menu coordinates relative to the wrapping element. The registered `onNodeClick` and `onLineClick` handlers are secondary here; in the reviewed source they only log to the console. The example does not implement graph editing or data mutation.\n\nStyling is split between SCSS and inline styles. SCSS defines the circular node shell and the white floating menu card, while inline styles place the label block below each node.\n\n## Key Interactions\nLeft-clicking a custom node opens the same menu as right-clicking it.\n\nRight-click does not show the browser's native context menu because the node handler calls `preventDefault()`.\n\nThe menu is positioned from wrapper-relative pointer coordinates, so it appears where the interaction happened without leaving the graph scene.\n\nClicking outside the panel closes it, while clicking inside the panel does not, because the menu stops propagation and the wrapper handles click-away dismissal.\n\nSelecting an action does not change graph data. It sends a success toast through `SimpleGlobalMessage` and then hides the menu.\n\n## Key Code Fragments\nThis options object shows that the example changes node appearance and junction behavior without introducing a custom layout configuration.\n\n```tsx\nconst graphOptions: RGOptions = {\n  defaultNodeColor: 'rgba(66,187,66,1)',\n  defaultNodeShape: RGNodeShape.circle,\n  defaultJunctionPoint: RGJunctionPoint.border\n};\n```\n\nThis handler proves that the menu position is computed from the pointer location relative to the wrapper element, not from graph model coordinates.\n\n```tsx\nconst showNodeMenus = (node: RGNode, $event: React.MouseEvent\u003CHTMLDivElement>) => {\n  setCurrentNode(node);\n  if (myPage.current) {\n    const _base_position = myPage.current.getBoundingClientRect();\n    setIsShowNodeMenuPanel(true);\n    setNodeMenuPanelPosition({\n      x: $event.clientX - _base_position.x,\n      y: $event.clientY - _base_position.y\n    });\n  }\n  $event.stopPropagation();\n  $event.preventDefault();\n};\n```\n\nThis node slot is the real trigger surface: both click and context-menu events are attached to custom node DOM instead of relying on a graph-wide context-menu hook.\n\n```tsx\n\u003CRGSlotOnNode>\n  {({ node }: RGNodeSlotProps) => (\n    \u003Cdiv\n      className=\"c-my-rg-node\"\n      onClick={(event) => showNodeMenus(node, event)}\n      onContextMenu={(event) => showNodeMenus(node, event)}\n    >\n      \u003CNodeIcon name={node.data?.myicon} />\n      \u003Cdiv>\n        {node.data?.myicon}\n      \u003C/div>\n    \u003C/div>\n  )}\n\u003C/RGSlotOnNode>\n```\n\nThis view slot shows that the menu is rendered inside the graph layer and only exists while the local visibility state is true.\n\n```tsx\n\u003CRGSlotOnView>\n  {isShowNodeMenuPanel && (\n    \u003Cdiv\n      className=\"pointer-events-auto context-menu-panel\"\n      style={{ left: nodeMenuPanelPosition.x, top: nodeMenuPanelPosition.y }}\n      onClick={(e) => e.stopPropagation()}\n    >\n      \u003Cdiv className=\"py-1 px-2 text-gray-400 text-xs border-b\">\n        Node Actions:\n      \u003C/div>\n      {/* menu items */}\n    \u003C/div>\n  )}\n\u003C/RGSlotOnView>\n```\n\nThis action handler confirms that menu selections only emit shared success feedback and close the panel.\n\n```tsx\nconst doAction = (actionName: string) => {\n  SimpleGlobalMessage.showMessage({\n    message: `Performed action ${actionName} on node: ${currentNode?.text}`,\n    type: 'success'\n  });\n  setIsShowNodeMenuPanel(false);\n};\n```\n\n## What Makes This Example Distinct\nComparison data places this example near `node-menu`, `node-tips`, `simple`, `node-content-lines`, and `node`, but its emphasis is narrower. Its standout combination is `RGSlotOnNode` plus `RGSlotOnView`, pointer-positioned menu state, browser context-menu suppression, outside-click dismissal, and toast-confirmed actions in an otherwise read-only viewer.\n\nCompared with `node-menu`, this is not a graph-wide context-menu system for nodes, lines, and canvas. Only the custom node DOM opens the menu, and the same trigger surface supports both left-click and right-click. Compared with `node-tips`, the floating panel is operational rather than informational because users can click actions and receive feedback instead of only seeing hover detail. Compared with `simple` and `node`, the view slot is used for transient contextual UI rather than persistent navigation or utility widgets.\n\nThat makes this example a stronger starting point when the requirement is per-node actions on a mostly fixed graph, not a broader editor or dashboard.\n\n## Where Else This Pattern Applies\nThis pattern can be transferred to organization charts where a node should open actions such as view profile, assign owner, or jump to a detail page.\n\nIt also fits service maps, asset topology viewers, and knowledge-graph explorers where the graph remains read-only but each node needs lightweight operational commands.\n\nThe same structure can be extended for workflow monitoring, case-management, or dependency-analysis tools by replacing the hardcoded action list with permission-aware commands and replacing `data.myicon` with domain metadata.\n",false,500,1782615423593]