JavaScript is required

Graph Zoom Presets

This example loads an inline top-down tree into relation-graph and controls the viewport through five fixed zoom presets in a floating helper window. It initializes the graph with `setJsonData()`, `moveToCenter()`, and `setZoom()`, then keeps later preset changes synchronized from React state while still exposing shared canvas settings and image export utilities.

Driving a Top-Down Tree with Fixed Zoom Presets

What This Example Builds

This 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.

Users 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.

The 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.

How the Data Is Organized

The 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.

There 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.

In 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.

How relation-graph Is Used

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.

The 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.

There 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.

The 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.

Key Interactions

The 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().

The 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.

Inside 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.

The 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.

Key Code Fragments

This fragment shows that the example is fixed to a top-down tree with tall rectangular nodes and curved links.

const graphOptions: RGOptions = {
    layout: {
        layoutName: 'tree',
        from: 'top',
        treeNodeGapH: 10,
        treeNodeGapV: 100
    },
    defaultNodeShape: RGNodeShape.rect,
    defaultNodeWidth: 30,
    defaultLineShape: RGLineShape.StandardCurve,
    defaultJunctionPoint: RGJunctionPoint.tb,

This fragment shows the flat inline RGJsonData structure that is loaded directly into the graph instance.

const myJsonData: RGJsonData = {
    rootId: 'a',
    nodes: [
        { id: 'a', text: 'a' }, { id: 'b', text: 'b' }, { id: 'b1', text: 'b1' },
        // ...many more nodes omitted
    ],
    lines: [
        { id: 'l1', from: 'a', to: 'b' }, { id: 'l2', from: 'b', to: 'b1' },
        // ...many more lines omitted
    ]
};

This fragment proves that initialization is a one-shot load, recenter, and apply-current-zoom sequence.

await graphInstance.setJsonData(myJsonData);
graphInstance.moveToCenter();
graphInstance.setZoom(zoom);
SimpleGlobalMessage.showMessage({
    type: 'success',
    message: `Set Zoom To: ${zoom}%`
});

This fragment shows the state-to-instance bridge that keeps the selected preset synchronized with the live viewport.

const setGraphZoom = () => {
    graphInstance.setZoom(zoom);
};

useEffect(() => {
    setGraphZoom();
}, [zoom]);

This fragment shows that the example-specific UI is a compact external selector with five fixed percentages.

<SimpleUISelect
    data={[
        { value: 100, text: '100%' },
        { value: 80, text: '80%' },
        { value: 40, text: '40%' },
        { value: 20, text: '20%' },
        { value: 10, text: '10%' }
    ]}
    currentValue={zoom}
    onChange={(newValue: string) => {

This fragment shows the shared settings overlay mutating live canvas behavior through the graph instance.

<SettingRow
    label="Wheel Event:"
    options={[
        { label: 'Scroll', value: 'scroll' },
        { label: 'Zoom', value: 'zoom' },
        { label: 'None', value: 'none' },
    ]}
    value={wheelMode}
    onChange={(newValue: string) => { graphInstance.setOptions({ wheelEventAction: newValue }); }}
/>

This fragment shows the screenshot export path built around relation-graph’s image-preparation lifecycle.

const canvasDom = await graphInstance.prepareForImageGeneration();
let graphBackgroundColor = graphInstance.getOptions().backgroundColor;
if (!graphBackgroundColor || graphBackgroundColor === 'transparent') {
    graphBackgroundColor = '#ffffff';
}
const imageBlob = await domToImageByModernScreenshot(canvasDom, {
    backgroundColor: graphBackgroundColor
});
await graphInstance.restoreAfterImageGeneration();

What Makes This Example Distinct

The 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.

Compared 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.

Compared 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().

Compared 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.

The 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.

Where Else This Pattern Applies

This 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.

It 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.