[{"data":1,"prerenderedAt":7},["ShallowReactive",2],{"example-markdown-content:en:node-line-canvas-context-menu":3},{"markdown":4,"isPaidExample":5,"isTruncated":5,"charLimit":6},"# Building a Shared Context Menu for Nodes, Lines, and Canvas\n\n## What This Example Builds\n\nThis example builds a full-height relation graph with one floating context menu that appears at the pointer location when the user right-clicks a node, a line, or empty canvas. The visible graph stays small and read-only: three green circular icon nodes, two labeled links, and a white rounded menu card that changes its header and actions according to the clicked target.\n\nUsers can open a target-specific menu, choose an action row, and dismiss the overlay by clicking elsewhere. Every action closes the menu and shows a toast in the top-right corner, so the demo is about contextual UI scaffolding rather than graph mutation.\n\nThe key idea is the shared menu-state pattern: one graph event handler, one overlay surface, and branching content for three target types.\n\n## How the Data Is Organized\n\nThe data is assembled inline inside `initializeGraph()` as one `RGJsonData` object with `rootId: '2'`, three node records, and two line records. Each node includes `id`, `text`, and `data.myicon`. The `data.myicon` value is not decorative metadata only; it drives the custom node slot so each node can render a different Lucide icon.\n\nThere is no preprocessing step before `setJsonData()`. The component constructs the final relation-graph payload directly in code, then loads it into the graph instance as-is. In real applications, the same shape could represent services, devices, ownership links, workflow steps, or dependency edges, while `data.myicon` could be replaced by type, status, or category fields that choose a custom renderer.\n\n## How relation-graph Is Used\n\n`index.tsx` wraps the demo in `RGProvider`, and `MyGraph.tsx` uses `RGHooks.useGraphInstance()` to access the active graph instance. A mount-time `useEffect()` calls `initializeGraph()`, which loads the inline dataset, recenters the graph, and fits it to the viewport.\n\nThe graph configuration is intentionally light. `defaultNodeColor` sets the green node fill, `defaultNodeShape` uses circular nodes, and `defaultJunctionPoint` attaches lines at node borders. The reviewed source does not define an explicit layout, so the graph relies on relation-graph's default arrangement for this payload.\n\nTwo relation-graph slots carry the example. `RGSlotOnNode` replaces the usual node body with a circular icon renderer, and `RGSlotOnView` renders the floating context menu inside the graph view. The menu position is taken directly from `eventPositionOnView`, so the overlay follows the right-click location without additional wrapper-relative coordinate math.\n\nThe main graph event is `onContextmenu`. It provides the clicked target type, the target object when one exists, and coordinates on both the canvas and the view. This example stores the `RGEventTargetType` and optional target object in React state, then branches menu labels and action rows from that state. Even menu items such as `Edit Node`, `Change Color`, `Add New Node`, and `Reset Zoom` do not call graph mutation APIs here; they only trigger feedback.\n\nToast feedback is implemented outside relation-graph through the local `SimpleGlobalMessage` helper. That helper lazily mounts a global message host into `document.body`, and the companion message styles pin the toast stack to the top-right of the screen.\n\n## Key Interactions\n\n- Right-clicking a node opens `Node Menu` with `View Details`, `Edit Node`, and `Delete`.\n- Right-clicking a line opens `Line Menu` with `View Details`, `Change Color`, and `Delete`.\n- Right-clicking empty canvas opens `Canvas Menu` with `View Details`, `Add New Node`, and `Reset Zoom`.\n- Clicking any menu row shows a success toast and closes the menu.\n- Clicking outside the menu dismisses the overlay, while clicking inside the card stops propagation so users can select a menu item first.\n\n## Key Code Fragments\n\nThis options block shows that the example relies on built-in graph options for most of its visual setup.\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 inline dataset proves that the graph payload is assembled directly in component code and carries per-node icon metadata.\n\n```tsx\nconst myJsonData: RGJsonData = {\n    rootId: '2',\n    nodes: [\n        { id: '1', text: 'Node-1', data: { myicon: 'star' } },\n        { id: '2', text: 'Node-2', data: { myicon: 'settings' } },\n        { id: '5', text: 'Node-5', data: { myicon: 'gift' } }\n    ],\n    lines: [\n        { id: 'l1', from: '1', to: '2', text: 'Link 1' },\n        { id: 'l2', from: '1', to: '5', text: 'Link 2' }\n    ]\n};\n```\n\nThis initialization sequence shows the full graph-instance workflow used by the demo.\n\n```tsx\nif (graphInstance) {\n    await graphInstance.setJsonData(myJsonData);\n    await graphInstance.moveToCenter();\n    await graphInstance.zoomToFit();\n}\n```\n\nThis handler shows how one relation-graph event becomes shared menu state and a view-relative overlay position.\n\n```tsx\nconst onContextmenu = (\n    e: RGUserEvent,\n    objectType: RGEventTargetType,\n    object: RGNode | RGLine | undefined,\n    eventPositionOnCanvas: RGCoordinate,\n    eventPositionOnView: RGCoordinate\n) => {\n    setMenuTarget({ type: objectType, data: object });\n    setMenuPosition({ x: eventPositionOnView.x, y: eventPositionOnView.y });\n    setIsShowMenu(true);\n};\n```\n\nThis node slot shows that icon rendering comes from node data rather than static markup.\n\n```tsx\n\u003CRGSlotOnNode>\n    {({ node }: RGNodeSlotProps) => (\n        \u003Cdiv className=\"c-my-rg-node\">\n            \u003CNodeIcon name={node.data?.myicon} />\n        \u003C/div>\n    )}\n\u003C/RGSlotOnNode>\n```\n\nThis view slot fragment shows one floating card adapting its header and actions from the current graph target.\n\n```tsx\n\u003CRGSlotOnView>\n    {isShowMenu && (\n        \u003Cdiv\n            className=\"pointer-events-auto context-menu-panel\"\n            style={{ left: menuPosition.x, top: menuPosition.y }}\n            onClick={(e) => e.stopPropagation()}\n        >\n            \u003Cdiv className=\"menu-header\">\n                {menuTarget?.type === 'node' && 'Node Menu'}\n                {menuTarget?.type === 'line' && 'Line Menu'}\n                {menuTarget?.type === 'canvas' && 'Canvas Menu'}\n            \u003C/div>\n            {/* ... menu items omitted ... */}\n        \u003C/div>\n    )}\n\u003C/RGSlotOnView>\n```\n\nThis message API fragment proves that menu actions enqueue toast feedback through a lazily mounted global host instead of mutating the graph.\n\n```tsx\nshowMessage: (messageObject: { duration?: number; message: string; type: string; }) => {\n    messageManager.mount();\n    setTimeout(() => {\n        messageManager.myMessageUIRef?.addMessage(\n            messageObject.type,\n            messageObject.message,\n            messageObject.duration\n        );\n    }, 0);\n},\n```\n\n## What Makes This Example Distinct\n\nThe comparison data places this example closest to `node-menu-2`, `node-line-tips-contentmenu`, `node-tips`, and `built-in-slots`, but its teaching focus is more specific than any of those neighbors. Against `node-menu-2`, the same icon-node and floating-card visual style is reused, but the control flow moves from node-slot DOM handlers to relation-graph's graph-level `onContextmenu`, which lets one event path cover nodes, lines, and empty canvas. Against `node-line-tips-contentmenu`, the scope narrows from a broader tooltip-and-inspection workspace to a compact scaffold for contextual actions.\n\nThe unusual feature combination is not simply \"a menu inside `RGSlotOnView`\". The comparison and rarity records emphasize a shared menu state keyed by `RGEventTargetType`, direct positioning from `eventPositionOnView`, and read-only action confirmation through a global toast layer. That makes the example especially useful when a team needs a reusable branching context-menu framework before wiring real business commands.\n\nIt is important not to overstate the uniqueness. Other examples also use `RGSlotOnView` and custom right-click overlays. What stands out here is the compact graph-level pattern that covers node, line, and canvas in one menu component while keeping the graph itself unchanged.\n\n## Where Else This Pattern Applies\n\nThis pattern transfers well to topology viewers, infrastructure maps, architecture diagrams, process inspectors, and relationship screens where the same contextual UI should work on nodes, links, and blank space. The placeholder actions in this demo can be replaced by real commands such as opening detail drawers, creating neighbor nodes, editing connection properties, or starting a canvas-level creation flow.\n\nIt also fits monitoring and approval tools that want lightweight confirmation before deeper implementation. The reusable part is the event contract and overlay structure: let relation-graph identify the target, place one view-layer menu near the pointer, and keep the actual action handlers application-specific.\n",false,500,1782615423268]