Public
Edited
Sep 5, 2023
Insert cell
Insert cell
Insert cell
Insert cell
mutable debug = {
}
Insert cell
Insert cell
nestedTree = (origRoot, showOwnerLinks = false) => {
const root = prepareRoot(origRoot);
const nodes = root.descendants();
const links = root.links();
const height = (nodes.length + 1) * nodeSize;

const viewBox = [-nodeSize / 2, (-nodeSize * 3) / 2, width / 1.5, height];

const svg = d3
.create("svg")
.attr("height", height)
.attr("viewBox", viewBox)
.attr("style", "max-width: 100%; height: auto; font: 10px sans-serif;");

const g = svg
.append("g")
.attr("transform", `translate(${showOwnerLinks ? 220 : 0},0)`)
.attr("name", "move-to-middle");

const linksGroup = g
.append("g")
.attr("fill", "none")
.attr("stroke", "#333")
.attr("name", "links");

const nodesGroup = g.append("g").attr("name", "nodes");

const ownerGroup = g.append("g").attr("name", "owner-arcs");

// initial draw
draw(nodes, links);

function draw(nodes, links) {
const link = linksGroup
.selectAll("path")
.data(links, (l) => `${l.source.data.id}-${l.target.data.id}`)
.join((enter) => enter.append("path").attr("d", linkPath));

const node = nodesGroup
.selectAll("g")
.data(nodes, (d) => d.data.id)
.join((enter) => drawNode(enter));

if (showOwnerLinks) {
const owners = nodes.filter((d) => {
// only show owner links between components, not host elements or fragments
return (
!d.data.isDOM &&
d.data.owner &&
d.data.name !== "[ ]" &&
d.data.name !== "Fragment"
);
});

const ownerArcs = ownerGroup
.selectAll("path")
.data(owners)
.join("path")
.attr("stroke", "black")
.attr("stroke-dasharray", 2)
.attr("fill", "none")
.attr("d", (d, i) => {
// Arc from callerNode to current node (d)
const { x1, y1, x2, y2, r } = d.data;

return arc({ x1, y1, x2, y2, r }); // ideally wouldn't have arc but arc-y curve
});
}
}

return Object.assign(svg.node(), { draw });
}
Insert cell
nodeCircleFill = (d) => d.data.name && isLowercase(d.data.name) && "white"
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
nestedTree(makeOwnersRoot(memoSmartRoot))
Insert cell
function drawNode(enter) {
const node = enter
.append("g")
.attr("name", (d) => d.data.name)
.attr("transform", (d) => `translate(0,${d.index * nodeSize})`);

const r = 3;
const nodeCircle = node
.append("circle")
.attr("cx", (d) => d.depth * nodeSize)
.attr("r", 3)
.attr("fill", nodeCircleFill)
.attr("stroke", "black");

const state = node
.selectAll("circle.hook")
.data((d) => {
console.log(
"data",
d.data.state ? { state: d.data.state, cx: d.depth * nodeSize } : []
);

return d.data.state
? d.data.state.map((state) => ({ state, cx: d.depth * nodeSize }))
: [];
})
.join("g");
// todo polar coords position based on index on group

state
.append("circle")
// orbiting circle
.attr("fill", "orange")
.attr("stroke", "hsl(0deg 0% 40% / 30%)")
.attr("cx", (d) => {
return d.cx + 4;
})
.attr("cy", -r - 4)
.attr("r", 2);
// circle around (maybe better for wrappers like memo / forwardRef)
// .attr("fill", "none")
// .attr("stroke", "#333")
// .attr("cx", (d) => {
// return d.cx; // + 3;
// })
// .attr("cy", 0)
// .attr("r", 6);

state
.append("text")
.attr("x", (d) => d.cx + 9)
.attr("cy", -r - 4)
.attr("font-size", "0.75em")
.attr("dy", "-0.7em")
.attr("fill", "hsl(0deg 0% 40%)")
.text((d) => d.state);

node
.append("text")
.attr("dy", "0.32em")
.attr("x", (d) => d.depth * nodeSize + 7)
.attr("stroke", "white")
.attr("stroke-width", 0.2)
.attr("paint-order", "stroke")
.text((d) => d.data.name)
.append("tspan")
.text((d) => ` (${d.data.id})`);

node.append("title").text((d) =>
d
.ancestors()
.reverse()
.map((d) => d.data.name)
.join("/")
);
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
nodeSize = 25
Insert cell
Insert cell
Insert cell
function linkPath(d) {
return `M${d.source.depth * nodeSize},${d.source.index * nodeSize}
V${d.target.index * nodeSize}
h${nodeSize}
`;
}
Insert cell
function strokeDashArray(d) {
const l = this.getTotalLength();
return `${l} ${l}`;
}
Insert cell
function strokeDashOffset(d) {
return this.getTotalLength();
}
Insert cell
function arc({ x1, y1, x2, y2, r }) {

return `
M${x1},${y1}
A${r},${r} 0,0,${y1 < y2 ? 0 : 1} ${x2},${y2}`;
// A rx ry x-axis-rotation large-arc-flag sweep-flag x y
}
Insert cell
Insert cell
Insert cell
memoInit = ({
id: "1",
name: "App",
state: ["color"],
children: [
{
id: "2",
name: "div",
owner: "1",
children: [
{
id: "3",
name: "input",
owner: "1",
children: []
},
{
id: "4",
name: "p",
owner: "1",
children: []
},
{
id: "5",
name: "ExpensiveTree",
owner: "1",
children: [
{
id: "6",
name: "p",
owner: "5",
children: []
}
]
}
]
}
]
})
Insert cell
memoSmart = ({
id: "1",
name: "App",
children: [
{
id: "2",
name: "ColorPicker",
owner: "1",
state: ["color"],
children: [
{
id: "3",
name: "div",
owner: "2",
children: [
{
id: "3",
name: "input",
owner: "2",
children: []
},
{
id: "4",
name: "p",
owner: "1",
children: []
},
{
id: "5",
name: "ExpensiveTree",
owner: "1",
children: [
{
id: "6",
name: "p",
owner: "5",
children: []
}
]
}
]
}
]
}
]
})
Insert cell
Insert cell
Insert cell
Insert cell
isLowercase = (string) => {
const firstLetter = string[0];

return firstLetter === firstLetter.toLowerCase();
}
Insert cell
d3 = require("d3@7")
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more