[{"data":1,"prerenderedAt":7},["ShallowReactive",2],{"example-markdown-content:en:canvas-node-interaction-locks":3},{"markdown":4,"isPaidExample":5,"isTruncated":5,"charLimit":6},"# Locking Canvas and Node Interaction in a Centered Graph Viewer\n\n## What This Example Builds\n\nThis example builds a full-height relation-graph viewer that opens in a restricted interaction state. The graph itself is a small centered hierarchy with rectangular nodes and gray curved lines, while a floating white utility window sits above the canvas.\n\nUsers can re-enable zoom, canvas dragging, and node dragging through three top-level switches. The same floating window can also be dragged, minimized, expanded into a settings overlay, and used to export the current graph as an image.\n\nThe key point is the startup state. Instead of allowing free navigation immediately, the example declares interaction locks in the initial `RGOptions` and then relaxes them on the live graph instance.\n\n## How the Data Is Organized\n\nThe graph data is declared inline inside `initializeGraph` as one `RGJsonData` object. It uses the standard flat relation-graph shape: a `rootId`, a `nodes` array of `{ id, text }` records, and a `lines` array of `{ id, from, to }` records.\n\nThere is no preprocessing pipeline before `setJsonData()`. The only preparation built into the data itself is that every line already has an explicit `id`, and after loading the example immediately normalizes the viewport with `moveToCenter()` and `zoomToFit()`.\n\nIn a real application, the same structure could represent an organization tree, a dependency outline, an approval hierarchy, or any embedded viewer where the data should remain readable but initially non-draggable.\n\n## How relation-graph Is Used\n\n`index.tsx` wraps the page in `RGProvider`, and `MyGraph.tsx` uses `RGHooks.useGraphInstance()` to work with the active graph instance. The graph options are stored in React state and passed directly into `\u003CRelationGraph options={options} />`, which makes the component a compact example of state-driven option control.\n\nThe configured graph stays on one `center` layout. It sets rectangular nodes, curved lines, left-right-top-bottom junction routing, and a minimal gray visual style. More importantly, it starts with `wheelEventAction: 'none'`, `dragEventAction: 'none'`, and `disableDragNode: true`, so both the canvas and the nodes are intentionally locked at first render.\n\nThe main instance API flow is simple and explicit: `setJsonData()` loads the inline hierarchy, `moveToCenter()` and `zoomToFit()` normalize the first view, and `updateOptions()` applies partial option changes from the three example-specific switches. No node slots, line slots, editing handles, or data mutations are used here.\n\nThe floating control window comes from the shared `DraggableWindow` helper. That helper uses `RGHooks.useGraphStore()` and `RGHooks.useGraphInstance()` again inside `CanvasSettingsPanel`, where it reads the current option state, switches wheel and canvas drag behavior with `setOptions()`, and exports the graph image through `prepareForImageGeneration()` and `restoreAfterImageGeneration()`. The imported SCSS file is only an empty selector scaffold, so the visible result is driven almost entirely by graph options and shared utility UI.\n\n## Key Interactions\n\nThe primary interaction is runtime option switching. Three switches in the floating panel toggle wheel zoom, canvas dragging, and node dragging by merging partial `RGOptions` into local React state and sending the same changes to `graphInstance.updateOptions(...)`.\n\nThe second interaction layer comes from the shared utility window. Its title bar can be used to drag the window, the window can be minimized and restored, and the settings button opens an overlay with segmented controls for wheel mode and canvas drag mode.\n\nThe settings overlay adds one more practical action: image export. It asks relation-graph to prepare the canvas DOM for screenshot generation, renders that DOM to a blob, downloads the file, and then restores the graph state after capture.\n\nThere is no example-specific node click handler, selection workflow, or graph editing flow in the current source. The lesson is interaction restriction and release, not structural editing or selection state.\n\n## Key Code Fragments\n\nThis fragment shows that the graph starts with the center layout and all three interaction restrictions already declared in the initial options state.\n\n```tsx\nconst [options, setOptions] = useState\u003CRGOptions>({\n    defaultNodeBorderWidth: 2,\n    defaultLineColor: '#666',\n    defaultLineWidth: 2,\n    defaultNodeShape: RGNodeShape.rect,\n    defaultLineShape: RGLineShape.StandardCurve,\n    defaultJunctionPoint: RGJunctionPoint.ltrb,\n    wheelEventAction: 'none',\n    dragEventAction: 'none',\n    disableDragNode: true,\n    layout: {\n        layoutName: 'center'\n    }\n});\n```\n\nThis fragment shows the flat inline `RGJsonData` structure that the example loads into the viewer.\n\n```tsx\nconst myJsonData: RGJsonData = {\n    rootId: 'a',\n    nodes: [\n        { id: 'a', text: 'a' }, { id: 'b', text: 'b' }, { id: 'b1', text: 'b1' },\n        { id: 'b2', text: 'b2' }, { id: 'b3', text: 'b3' }, { id: 'b4', text: 'b4' },\n        // ...\n    ],\n    lines: [\n        { id: 'l1', from: 'a', to: 'b' }, { id: 'l2', from: 'b', to: 'b1' },\n        { id: 'l3', from: 'b', to: 'b2' }, { id: 'l4', from: 'b', to: 'b3' },\n        // ...\n    ]\n};\n```\n\nThis fragment shows that initialization is a one-time load plus viewport normalization, not a repeating rebuild cycle.\n\n```tsx\nconst initializeGraph = async () => {\n    // ...build myJsonData\n    await graphInstance.setJsonData(myJsonData);\n    graphInstance.moveToCenter();\n    graphInstance.zoomToFit();\n};\n\nuseEffect(() => {\n    initializeGraph();\n}, []);\n```\n\nThis fragment proves that the example updates live interaction behavior through partial option patches.\n\n```tsx\nconst handleUpdateOptions = (newOptions: Partial\u003CRGOptions>) => {\n    setOptions(prev => ({ ...prev, ...newOptions }));\n    graphInstance.updateOptions(newOptions);\n};\n```\n\nThis fragment shows the example-specific control surface for disabling zoom, canvas drag, and node drag.\n\n```tsx\n\u003CSimpleUISwitch\n    currentValue={options.wheelEventAction === 'none'}\n    onChange={(disabled) => {\n        handleUpdateOptions({ wheelEventAction: disabled ? 'none' : 'zoom' });\n    }}\n/>\n\u003CSimpleUISwitch\n    currentValue={options.dragEventAction === 'none'}\n    onChange={(disabled) => {\n        handleUpdateOptions({ dragEventAction: disabled ? 'none' : 'move' });\n    }}\n/>\n```\n\nThis fragment shows the shared settings overlay mutating the active graph instance directly and exposing screenshot export.\n\n```tsx\nconst graphInstance = RGHooks.useGraphInstance();\nconst { options } = RGHooks.useGraphStore();\n\n\u003CSettingRow\n    label=\"Wheel Event:\"\n    options={[\n        { label: 'Scroll', value: 'scroll' },\n        { label: 'Zoom', value: 'zoom' },\n        { label: 'None', value: 'none' },\n    ]}\n    value={options.wheelEventAction}\n    onChange={(newValue: string) => { graphInstance.setOptions({ wheelEventAction: newValue }); }}\n/>\n```\n\nThis fragment shows the export path that temporarily prepares the graph canvas for image generation and then restores it.\n\n```tsx\nconst canvasDom = await graphInstance.prepareForImageGeneration();\nconst imageBlob = await domToImageByModernScreenshot(canvasDom, {\n    backgroundColor: graphBackgroundColor\n});\nif (imageBlob) {\n    downloadBlob(imageBlob, 'my-image-name');\n}\nawait graphInstance.restoreAfterImageGeneration();\n```\n\n## What Makes This Example Distinct\n\nThe comparison data places this example near `drag-and-wheel-event`, `selections`, `switch-layout`, and `zoom`, but its teaching goal is narrower. Compared with `drag-and-wheel-event`, it puts more weight on declaring a locked starting state in the initial `RGOptions` and on toggling node dragging explicitly, not just on exploring wheel and canvas drag modes from shared controls.\n\nCompared with `selections`, it uses runtime drag options for the opposite purpose. That neighbor turns drag behavior into marquee selection and selected-state feedback, while this example suppresses interaction until the user chooses to re-enable it.\n\nCompared with `switch-layout` and `zoom`, the graph here stays on one stable centered viewer. The runtime updates target interaction permissions on a fixed layout rather than layout playback or preset zoom values.\n\nIts most distinctive combination is a nearly default-styled center-layout hierarchy, initial canvas and node interaction lockdown, three example-level disable switches, and the shared draggable utility window. That makes it a stronger starting point for protected or kiosk-style viewers than for selection, editing, or layout experimentation.\n\n## Where Else This Pattern Applies\n\nThis pattern fits embedded graph viewers that should not react to accidental mouse input on first render. Typical cases include dashboards, kiosk screens, guided presentations, approval views, training demos, and read-only graph panels inside larger business applications.\n\nIt also transfers well to products that need permission-based interaction release. A production screen could start with all movement disabled, then enable zoom or dragging only after a mode switch, a role check, or a user action in surrounding UI, while keeping the same graph data and the same mounted graph instance.\n",false,500,1782615431066]