[{"data":1,"prerenderedAt":7},["ShallowReactive",2],{"example-markdown-content:en:graph-zoom-presets":3},{"markdown":4,"isPaidExample":5,"isTruncated":5,"charLimit":6},"# Driving a Top-Down Tree with Fixed Zoom Presets\n\n## What This Example Builds\n\nThis example builds a full-height tree viewer with a floating control card that lets the user force the canvas to one of five exact zoom levels. The graph itself is a top-down hierarchy with tall light-blue rectangular nodes and subdued curved links, so scale changes are easy to see without other visual noise.\n\nUsers can click `100%`, `80%`, `40%`, `20%`, or `10%` in the floating selector. On first load the example mounts the inline dataset, centers the viewport, applies the current zoom preset, and shows a success toast near the top-right corner of the page.\n\nThe main point is not tree layout by itself. The example is a compact reference for controlling relation-graph zoom from external React state instead of relying only on wheel gestures or built-in navigation helpers.\n\n## How the Data Is Organized\n\nThe data is declared inline inside `initializeGraph()` as one `RGJsonData` object with a `rootId`, a flat `nodes` array, and a flat `lines` array. Structurally, it is a normal tree-shaped hierarchy rooted at `a`, with large branch groups under `b`, `c`, `d`, and `e`.\n\nThere is no fetch step and no preprocessing layer before `setJsonData()`. The dataset literal is constructed in the component and passed straight into the graph instance, which keeps the example focused on viewport control rather than on data loading or transformation.\n\nIn a real application, the same shape could represent an organization tree, a product taxonomy, a file hierarchy, a dependency outline, or any read-only knowledge view where users need predictable zoom levels from surrounding UI. One source detail is worth noting: the line record `{ id: 'e', to: 'e2' }` omits a `from` field, so this example is more useful as a zoom-control pattern than as a strict line-schema reference.\n\n## How relation-graph Is Used\n\n`index.tsx` wraps the page in `RGProvider`, which makes the active graph instance available to hooks. `MyGraph.tsx` then uses `RGHooks.useGraphInstance()` to load data and control the viewport, while the shared `CanvasSettingsPanel` inside `DraggableWindow.tsx` uses both `RGHooks.useGraphInstance()` and `RGHooks.useGraphStore()` to read and mutate live canvas options.\n\nThe graph options lock the demo onto one tree layout: `layoutName: 'tree'`, `from: 'top'`, `treeNodeGapH: 10`, and `treeNodeGapV: 100`. The example also sets rectangular nodes, a narrow `30 x 100` default node size, curved links, and top-bottom junction points so the graph reads as a vertical tree rather than a general network.\n\nThere are no custom node slots, line slots, canvas slots, or editing handles in this example. Visual customization comes from `my-relation-graph.scss`, which overrides the default node and line appearance under `.relation-graph` instead of replacing rendering with custom components.\n\nThe graph instance API flow is explicit and small. `setJsonData()` loads the inline tree, `moveToCenter()` normalizes the first view, and `setZoom()` applies both the initial preset and every later preset change. The shared helper window adds secondary runtime controls with `setOptions()` for wheel and drag behavior, plus `prepareForImageGeneration()`, `getOptions()`, and `restoreAfterImageGeneration()` for screenshot export.\n\n## Key Interactions\n\nThe primary interaction is preset zoom selection. Clicking a value in `SimpleUISelect` updates React state, and a `useEffect([zoom])` bridge forwards that value into `graphInstance.setZoom()`.\n\nThe floating helper window adds a second layer of interaction around the graph. Its title bar can be dragged, the content can be minimized and restored, and the settings button opens an overlay panel above the same window shell.\n\nInside that settings overlay, users can switch wheel behavior among `scroll`, `zoom`, and `none`, and switch canvas drag behavior among `selection`, `move`, and `none`. Those controls are inherited from the shared helper, so they are present here but remain secondary to the zoom-preset lesson.\n\nThe helper also exposes image export. It prepares the graph canvas DOM for capture, renders it to a blob through `modern-screenshot`, downloads the file, and then restores the graph state.\n\n## Key Code Fragments\n\nThis fragment shows that the example is fixed to a top-down tree with tall rectangular nodes and curved links.\n\n```tsx\nconst graphOptions: RGOptions = {\n    layout: {\n        layoutName: 'tree',\n        from: 'top',\n        treeNodeGapH: 10,\n        treeNodeGapV: 100\n    },\n    defaultNodeShape: RGNodeShape.rect,\n    defaultNodeWidth: 30,\n    defaultLineShape: RGLineShape.StandardCurve,\n    defaultJunctionPoint: RGJunctionPoint.tb,\n```\n\nThis fragment shows the flat inline `RGJsonData` structure that is loaded directly into the graph instance.\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        // ...many more nodes omitted\n    ],\n    lines: [\n        { id: 'l1', from: 'a', to: 'b' }, { id: 'l2', from: 'b', to: 'b1' },\n        // ...many more lines omitted\n    ]\n};\n```\n\nThis fragment proves that initialization is a one-shot load, recenter, and apply-current-zoom sequence.\n\n```tsx\nawait graphInstance.setJsonData(myJsonData);\ngraphInstance.moveToCenter();\ngraphInstance.setZoom(zoom);\nSimpleGlobalMessage.showMessage({\n    type: 'success',\n    message: `Set Zoom To: ${zoom}%`\n});\n```\n\nThis fragment shows the state-to-instance bridge that keeps the selected preset synchronized with the live viewport.\n\n```tsx\nconst setGraphZoom = () => {\n    graphInstance.setZoom(zoom);\n};\n\nuseEffect(() => {\n    setGraphZoom();\n}, [zoom]);\n```\n\nThis fragment shows that the example-specific UI is a compact external selector with five fixed percentages.\n\n```tsx\n\u003CSimpleUISelect\n    data={[\n        { value: 100, text: '100%' },\n        { value: 80, text: '80%' },\n        { value: 40, text: '40%' },\n        { value: 20, text: '20%' },\n        { value: 10, text: '10%' }\n    ]}\n    currentValue={zoom}\n    onChange={(newValue: string) => {\n```\n\nThis fragment shows the shared settings overlay mutating live canvas behavior through the graph instance.\n\n```tsx\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={wheelMode}\n    onChange={(newValue: string) => { graphInstance.setOptions({ wheelEventAction: newValue }); }}\n/>\n```\n\nThis fragment shows the screenshot export path built around relation-graph's image-preparation lifecycle.\n\n```tsx\nconst canvasDom = await graphInstance.prepareForImageGeneration();\nlet graphBackgroundColor = graphInstance.getOptions().backgroundColor;\nif (!graphBackgroundColor || graphBackgroundColor === 'transparent') {\n    graphBackgroundColor = '#ffffff';\n}\nconst imageBlob = await domToImageByModernScreenshot(canvasDom, {\n    backgroundColor: graphBackgroundColor\n});\nawait graphInstance.restoreAfterImageGeneration();\n```\n\n## What Makes This Example Distinct\n\nThe comparison output makes the distinction clear: this example lives near other floating-window viewer utilities such as `disable-effect`, `gee-thumbnail-diagram`, `drag-and-wheel-event`, and `switch-layout`, but its example-specific UI is much narrower. The current rarity data identifies the preset zoom selector itself as the unusual part, not the shared helper window.\n\nCompared with `disable-effect`, this example is not about locking or unlocking interaction. It keeps navigation available and focuses on choosing exact scale presets, which is more useful when a product needs deterministic viewport states instead of permission toggles.\n\nCompared with `gee-thumbnail-diagram`, it emphasizes known percentages rather than overview navigation. The user does not drag a minimap viewport here; the surrounding UI simply pushes a chosen zoom value into `setZoom()`.\n\nCompared with `drag-and-wheel-event` and `switch-layout`, the dataset and layout stay effectively fixed after load. The graph does not become a broader behavior playground; the main lesson is the explicit React-state-to-`setZoom()` synchronization pattern around an otherwise stable tree.\n\nThe strongest distinctive combination is a top-origin tree, one-shot inline loading, immediate `moveToCenter()`, a five-value external zoom selector, and imperative `setZoom()` calls on the live graph instance. That makes this example a focused starting point for predictable viewport scaling rather than for layout mutation, minimap navigation, or interaction-mode experimentation.\n\n## Where Else This Pattern Applies\n\nThis pattern transfers well to embedded graph viewers that need predictable named zoom states from surrounding UI. Typical cases include dashboard panels, guided walkthroughs, presentation modes, knowledge viewers inside sidebars, and admin pages where the graph should open at a known scale and allow users to jump between a few approved levels.\n\nIt also applies to products that want zoom presets tied to external state such as route parameters, saved user preferences, toolbar buttons, or onboarding steps. The same structure can be extended to keyboard shortcuts, compact mobile controls, or per-scene zoom defaults without changing how the dataset is loaded into relation-graph.\n",false,500,1782615427218]