Custom JavaScript Controls ========================== The Python and R bindings cover the common options, but every figure they produce is a live ``LightGraph`` instance with a full JavaScript API — you can wire your own buttons, dashboards, or page logic to it. This page shows how to reach the instance from each host and what you can do with it. The instance API ---------------- Whichever way a graph was created, its container element carries the instance as ``container.lightgraph`` (in hand-written JavaScript you also get it back from ``new lightGraph.LightGraph(...)``). The instance offers: * **Methods** — ``setData({nodes, edges})``, ``updateConfig(partial)``, ``zoomToFit(padding?)``, ``getSelection()``, ``selectNodes(ids)``, ``egoFilter(nodeId, depth?)`` / ``clearEgoFilter()`` / ``getVisibleNodes()``, ``settle(ticks?)``, ``resize()``, ``destroy()``. * **Events** — ``on(event, cb)`` / ``off(event, cb)`` for ``nodeClick`` (``{node}``), ``selectionChange`` (array of selected nodes), ``dataLoad``, ``egoFilter``, and ``destroy``. Live example ------------ The toolbar above this graph is plain HTML — three buttons and a status label — talking to the instance. Click a node or use the buttons: .. raw:: html The wiring is just event handlers calling instance methods: .. code-block:: javascript var graph = new lightGraph.LightGraph(document.getElementById('graph'), { nodes: nodes, edges: edges, config: config }); var status = document.getElementById('status'); document.getElementById('btn-starks').onclick = function () { graph.clearEgoFilter(); graph.selectNodes(['Eddard', 'Catelyn', 'Robb', 'Sansa', 'Arya', 'Bran', 'Rickon', 'Jon']); }; document.getElementById('btn-tyrion').onclick = function () { graph.egoFilter('Tyrion', 2); }; document.getElementById('btn-reset').onclick = function () { graph.clearEgoFilter(); graph.selectNodes([]); graph.zoomToFit(); }; graph.on('nodeClick', function (e) { status.textContent = 'Clicked ' + e.node.id; }); graph.on('selectionChange', function (selected) { if (selected.length) status.textContent = selected.length + ' selected'; }); graph.on('egoFilter', function () { status.textContent = graph.getVisibleNodes().length + ' nodes visible'; }); (The full page is generated by ``docs/tools/gen_example_embeds.py`` if you want to copy it as a starting point.) From R: ``htmlwidgets::onRender`` --------------------------------- R's htmlwidgets framework has a standard hook for attaching JavaScript to a rendered widget. The hook receives the widget's DOM element, so the instance is one property away: .. code-block:: r library(lightgraph) library(htmlwidgets) data(got) w <- lightgraph(edges = got, node_metric = lg_pagerank(got), edge_weight_to_width = TRUE) onRender(w, " function(el, x) { var graph = el.lightgraph; graph.on('nodeClick', function (e) { console.log('clicked', e.node.id); }); graph.egoFilter('Tyrion', 2); } ") This works in the RStudio viewer, in R Markdown documents, and in Shiny (wrap the widget in ``renderLightgraph()`` as usual — the ``onRender`` hook travels with it, and ``nodeClick`` handlers can call ``Shiny.setInputValue()`` to send selections back to the server). From Python: extend the generated page -------------------------------------- ``net_vis()`` returns a :class:`~lightgraph.NetworkVisualization` whose ``.html`` property is a complete, self-contained page. Append your own script before saving it. One detail: the page initializes the graph asynchronously, so poll until the instance appears on the container (id ``lightGraph``): .. code-block:: python from lightgraph import net_vis, datasets, pagerank edges = datasets.got() vis = net_vis(edges=edges, node_metric=pagerank(edges)) controls = """ """ html = vis.html.replace('', controls + '') with open('got_custom.html', 'w') as f: f.write(html) Inside Jupyter, ``vis`` renders in a sandboxed iframe for isolation, so scripts on the notebook page cannot reach it — the pattern above (augment the HTML, then save or serve it) is the supported route for custom controls. Going further ------------- ``updateConfig(partial)`` accepts any fragment of the JS ``DEFAULT_CONFIG`` (see :doc:`api`), so a slider wired to ``graph.updateConfig({edges: {widthScale: v}})`` gives you live styling controls; ``setData({nodes, edges})`` swaps the whole graph and reuses the instance. Everything the built-in toolbar and sidebar do goes through this same public API.