[{"data":1,"prerenderedAt":7},["ShallowReactive",2],{"example-markdown-content:en:graph-hand-drawn-style-switcher":3},{"markdown":4,"isPaidExample":5,"isTruncated":5,"charLimit":6},"# Switching a Relation Graph Into a Hand-Drawn Presentation Mode\n\n## What This Example Builds\n\nThis example builds a centered hierarchy viewer that can switch between a plain card presentation and a sketch-like hand-drawn skin at runtime. The canvas shows seven KPI-style nodes, six labeled links, a floating control window, and a built-in minimap.\n\nUsers can turn the hand-drawn mode on or off, change the node border style, and choose a texture overlay such as paper, hatch, grid, or dots. The graph structure does not change. Instead, the example keeps one loaded hierarchy and rethemes its nodes, lines, labels, and arrowheads in place.\n\nThe main point of interest is that the sketch effect comes from layered relation-graph features rather than a separate renderer. Custom node slots, SVG filters, SCSS overrides, and graph-instance update APIs work together so one existing graph can adopt a distinct illustrated look.\n\n## How the Data Is Organized\n\nThe data comes from `getJsonData()` and returns one `RGJsonData` object with `rootId: \"a\"`, a flat `nodes` array, and a flat `lines` array. Each node record carries `data.name` and `data.myicon`, while the six lines initially only define `from` and `to`.\n\nThere is a preprocessing step before `setJsonData()`. During initialization, the code iterates over every line and injects a shared label (`Line Text`), random junction-point offsets, a random `junctionOffset`, and the custom `my-hand-drawn-arrow-end` marker. After the data is loaded, later style changes do not rebuild the JSON. They update the already rendered nodes and lines through `updateNodeData()` and `updateLine()`.\n\nIn a real product, the same shape could represent a small org chart, a team dashboard, a capability map, or a feature hierarchy. `data.name` can map to people, departments, products, or services, and the style fields written at runtime can stand for theme presets, presentation modes, or brand-specific illustration variants.\n\n## How relation-graph Is Used\n\n`index.tsx` wraps the example with `RGProvider`, and `MyGraph.tsx` consumes the shared graph context through `RGHooks.useGraphInstance()`. The graph is configured with a `center` layout, explicit `levelGaps`, centered alignment on both axes, and `defaultLineWidth: 2`. Built-in node fill and border rendering are intentionally reduced with `defaultNodeColor: 'transparent'` and `defaultNodeBorderWidth: 0` so the visible card body comes from the custom slot content instead.\n\nThe main extension point is `RGSlotOnNode`. Each node renders a large icon, a name, and three fixed KPI rows, then wraps that content in either `MyNodeContentBox` or `HandDrawnBox` depending on `enableHandDrawn`. `HandDrawnBox` applies asymmetric border-radius values and optional SVG-based textures, while `IconSwitcher` maps `node.data.myicon` to Lucide icons and falls back to `HelpCircle` for unmatched values.\n\nThe example also uses `RGSlotOnView` to mount `RGMiniView`, so the viewer gains a built-in overview panel without custom viewport code. Graph-instance APIs drive the runtime behavior: `setJsonData()` loads the graph, `getNodes()` plus `updateNodeData()` propagate border and texture state, `getLines()` plus `updateLine()` switch line shapes, and `moveToCenter()`, `zoomToFit()`, and `zoomToFitWithAnimation()` keep the viewport aligned after the visual changes.\n\nThe hand-drawn effect extends beyond the node slot. `MySvgFilters` injects the `static-hand-drawn` SVG filter and the custom arrow marker, while `my-relation-graph.scss` targets built-in relation-graph node, line, and label layers under the `.node-filter-hand-drawn` wrapper. That SCSS adds filter distortion, dashed note-like labels, and a checked-state override around the custom content instead of the default graph shadow.\n\nThe floating control surface comes from the shared `DraggableWindow` component. It hosts the style selectors directly in this example, and its settings overlay uses `RGHooks.useGraphStore()` plus `setOptions()` to change wheel and drag behavior. The same shared helper also exposes screenshot export through `prepareForImageGeneration()`, `domToImageByModernScreenshot()`, and `restoreAfterImageGeneration()`.\n\n## Key Interactions\n\nThe primary interaction is the hand-drawn mode switch. Toggling it changes the root wrapper class, swaps the node wrapper component, rewrites every line between `RGLineShape.Curve8` and `RGLineShape.StandardStraight`, and then refits the viewport.\n\nWhen hand-drawn mode is active, two additional selectors appear for border style and background texture. Changing either one triggers a graph-wide pass that writes `nodeBorderStyle` and `nodeBackgroundStyle` into each node's data, so the entire hierarchy changes appearance together.\n\nThe floating window itself is interactive. Users can drag it by the title bar, minimize it, open a settings overlay, and keep it above the graph while exploring the current presentation.\n\nThe settings overlay adds viewer-level controls rather than content editing. It can switch wheel behavior, switch canvas dragging behavior, and export the current graph DOM as an image.\n\n## Key Code Fragments\n\nThis options block shows that the example keeps relation-graph's native center layout and strips the default node body down so slot content can define the visible card style.\n\n```tsx\nconst graphOptions: RGOptions = {\n    debug: true,\n    defaultJunctionPoint: RGJunctionPoint.border,\n    defaultNodeColor: 'transparent',\n    defaultNodeShape: RGNodeShape.rect,\n    defaultNodeBorderWidth: 0,\n    defaultLineWidth: 2,\n    layout: {\n        layoutName: 'center',\n        levelGaps: [500, 400, 400],\n        alignItemsX: 'center',\n        alignItemsY: 'center'\n    }\n};\n```\n\nThis initialization step proves that the line labels, offsets, and custom sketch arrow marker are injected before the graph is loaded.\n\n```tsx\nconst myJsonData: RGJsonData = await getJsonData();\nmyJsonData.lines.forEach(line => {\n    line.text = 'Line Text';\n    line.fromJunctionPointOffsetX = Math.random() * 10;\n    line.fromJunctionPointOffsetY = Math.random() * 10;\n    line.toJunctionPointOffsetX = Math.random() * 10;\n    line.toJunctionPointOffsetY = Math.random() * 10;\n    line.junctionOffset = Math.random() * 20 - 10;\n    line.endMarkerId = 'my-hand-drawn-arrow-end';\n});\nawait graphInstance.setJsonData(myJsonData);\n```\n\nThis runtime update function is the core of the demo: it propagates the selected node styles and line geometry to the already rendered graph.\n\n```tsx\ngraphInstance.getNodes().forEach(node => {\n    graphInstance.updateNodeData(node, {\n        nodeBorderStyle,\n        nodeBackgroundStyle\n    });\n});\ngraphInstance.getLines().forEach(line => {\n    graphInstance.updateLine(line, {\n        lineShape: enableHandDrawn ? RGLineShape.Curve8 : RGLineShape.StandardStraight,\n    });\n});\n```\n\nThis control fragment shows that the hand-drawn toggle is the entry point, and that the border and texture selectors only appear when the sketch mode is enabled.\n\n```tsx\n\u003CSimpleUISelect data={[\n    { value: false, text: 'None' },\n    { value: true, text: 'Hand Drawn Style' }\n]} currentValue={enableHandDrawn} onChange={setEnableHandDrawn} />\n{\n    enableHandDrawn && \u003C>\n        \u003CSimpleUISelect data={[\n            { value: 'rough', text: 'Rough' },\n            { value: 'pencil', text: 'Pencil' },\n            { value: 'thick', text: 'Thick' }\n        ]} currentValue={nodeBorderStyle} onChange={setNodeBorderStyle} />\n    \u003C/>\n}\n```\n\nThis node slot fragment shows how one KPI-card template is wrapped in either the plain box or the hand-drawn box without changing the graph data structure.\n\n```tsx\n\u003CRGSlotOnNode>\n    {({ node }) => {\n        const nodeContent = \u003Cdiv>{/* icon, name, KPI rows */}\u003C/div>;\n        if (enableHandDrawn) {\n            return \u003CHandDrawnBox variant={node.data.nodeBorderStyle} texture={node.data.nodeBackgroundStyle}>\n                {nodeContent}\n            \u003C/HandDrawnBox>\n        } else {\n            return \u003CMyNodeContentBox>{nodeContent}\u003C/MyNodeContentBox>\n        }\n    }}\n\u003C/RGSlotOnNode>\n```\n\nThis wrapper component proves that the hand-drawn card is not just a color theme. The border geometry and stroke weight change by variant.\n\n```tsx\nconst getWobbleStyle = () => {\n  switch (variant) {\n    case \"pencil\":\n      return {\n        borderRadius: \"255px 15px 225px 15px/15px 225px 15px 255px\",\n        borderWidth: \"1px\",\n        borderStyle: \"solid\",\n        borderColor: color,\n      };\n    case \"thick\":\n      return {\n        borderRadius: \"4px 6px 4px 10px / 8px 4px 10px 5px\",\n        borderWidth: \"4px\",\n        borderStyle: \"solid\",\n        borderColor: color,\n      };\n```\n\nThis SCSS fragment shows how the example pushes the hand-drawn mode onto relation-graph's built-in node, line, and label DOM layers.\n\n```scss\n.node-filter-hand-drawn {\n    .relation-graph {\n        .rg-node-peel {\n            .rg-node {\n                filter: url(#static-hand-drawn);\n            }\n        }\n        .rg-line-peel {\n            .rg-line {\n                filter: url(#static-hand-drawn);\n            }\n            .rg-line-label {\n                filter: url(#static-hand-drawn);\n                background: #fff;\n                border: 2px dashed #666;\n            }\n        }\n    }\n}\n```\n\nThis shared helper code shows that screenshot export is handled through graph-instance preparation and restoration rather than by capturing arbitrary page DOM.\n\n```tsx\nconst downloadImage = async () => {\n    const canvasDom = await graphInstance.prepareForImageGeneration();\n    let graphBackgroundColor = graphInstance.getOptions().backgroundColor;\n    if (!graphBackgroundColor || graphBackgroundColor === 'transparent') {\n        graphBackgroundColor = '#ffffff';\n    }\n    const imageBlob = await domToImageByModernScreenshot(canvasDom, {\n        backgroundColor: graphBackgroundColor\n    });\n    if (imageBlob) {\n        downloadBlob(imageBlob, 'my-image-name');\n    }\n```\n\n## What Makes This Example Distinct\n\nThe comparison data places this example near `custom-line-animation`, `custom-line-style`, `node`, and `deep-each`, but its emphasis is different from each of those neighbors. The strongest distinguishing point is that it turns one fixed centered hierarchy into a full graph-skin switcher. A single control window changes node wrappers, border variants, texture overlays, line shapes, line jitter, label treatment, and arrowheads together.\n\nCompared with `custom-line-animation`, this example is less about maintaining a catalog of motion or line presets and more about one cohesive sketchbook presentation mode that spans nodes, lines, labels, and wrappers. Compared with `custom-line-style`, it goes beyond swapping CSS line classes by also propagating node-level border and texture state through `updateNodeData()`.\n\nCompared with `node`, which mainly compares node-rendering techniques side by side, this example keeps one card template and rethemes the whole graph at runtime. Compared with `deep-each`, its graph-instance updates are about whole-scene presentation switching rather than subtree emphasis or focus behavior.\n\nThe rarity data also supports a more specific claim: the combination of `HandDrawnBox`, injected SVG filters, per-node texture selectors, custom arrow markers, and `Curve8` versus straight-line switching is unusually rich for a styling-oriented demo. It is a stronger starting point for teams that need an illustrated or branded presentation layer without replacing relation-graph's renderer.\n\n## Where Else This Pattern Applies\n\nThis pattern applies to dashboards and presentation views where the graph data stays stable but the visual language needs to change. Examples include investor storytelling screens, product strategy diagrams, education content, workshop boards, white-label tenant themes, and marketing-oriented org or capability maps.\n\nIt also applies when teams need a temporary presentation mode rather than a permanent custom renderer. A product can keep its normal relation-graph data model and viewer behavior, then layer on a stylized theme for export, live demos, stakeholder reviews, or a special reporting mode.\n",false,500,1782615373218]