Will do, but first I have a couple of notes:
Selection:
Youâre calling d3.selectAll() (for iNode, rNode), even though youâre only accessing the first element.
Implicit dependencies / side effects:
Cells with side effects cause problems when you start to reuse/import your code in other notebooks. E.g., if you were to import renderNodes into another notebooks, then renderLines would never run, unless you explicitely import and output it as well.
yield vs return:
A single yield at the end of a cell (or function) can be replaced with return.
Yielding in Observable:
When you use yield
inside a cell, Observable turns your cell into a generator. Generator cells in Observable are evaluated on every frame (via requestAnimationFrame).
Getting an element into the DOM:
Youâve currently split your code into renderNodes and renderLines because you want to use getBoundingClientRect in the second part. But splitting isnât necessary, because you can yield the element intermittently:
{
const svgNode = /* ... */
// This returns the DOM element to Observable's runtime,
// which hands it off to Observable's Inspector,
// who in turn attaches it to the DOM.
yield svgNode;
// On the next frame, Observable continues to retrieve values
// from the generator.
// The element is now in the DOM, and .getBoundingClientRect() will work.
// ... do other stuff ...
}
Regarding the function:
Splitting your code into pieces still makes sense, but these pieces should have clearly defined inputs and outputs (arguments and return values). While Observable abstracts away the handling of dependencies, I still recommend to write functions whenever your code may run with different configurations.
With renderLines, your code could look something like this:
renderNodes = {
const svgNode = /* ... */
// do the renderNodes stuff
yield svgNode;
renderLines(svgNode, connectionPairs);
}
This is just an example, I didnât dive into the code deep enough to determine wether the approach is sensible for this instance.
A pattern that I often use in my notebooks is to start a cell like this:
chart = {
const {
width = 400,
height = 300,
// other things that could be customizable
} = {};
// Rest of the cell.
}
Whenever I think something should be customizable I put it into this declaration block. And should I decide to turn the whole thing into a function, all I need to do is this:
function createChart(options = {}) {
const {
width = 400,
height = 300,
// other things that could be customizable
} = options;
// Rest of the function.
}