Public
Edited
May 8, 2023
1 fork
14 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
renderLayout(layout, [width, (width * 2) / 3])
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
module = source => {
const script = compiler.parseComponent(source).script;
return script && script.content;
}
Insert cell
events = source => {
const matchSet = matches => [...new Set(matches.map(([match, val]) => val))];
return matchSet([...source.matchAll(/\$emit\('([^']+)'/g)]); // cheap events detection
}
Insert cell
Insert cell
sfcModule = module => {
const tree = esprima.parseModule(module);
const exportsType = "ExportDefaultDeclaration";
const exports = tree.body.find(({ type }) => type == exportsType);
const findProp = (props, name) => props.find(prop => prop.key.name == name);
const declProps = exports.declaration.properties;
const safe = (func, def) => value => (value == undefined ? def : func(value));
const compValues = comps => comps.value.properties.map(prop => prop.key.name);
const components = safe(compValues, [])(findProp(declProps, 'components'));
const propElems = props => props.value.elements.map(element => element.value);
const props = safe(propElems, [])(findProp(declProps, 'props'));
return { components, props };
}
Insert cell
componentDeps = {
const deps = module => module && sfcModule(module);
const info = src => ({ events: events(src), ...deps(module(src)) });
const addInfo = ({ source, color }) => ({ ...info(source), color });
const pairs = [...componentSources.entries()];
return new Map(pairs.map(([name, info]) => [name, addInfo(info)]));
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
graph = {
// utilitary
const children_ = name => componentDeps.get(name).components || [];
const filter = (ids1, ids2) => ids1.filter(id => ids2.includes(id));
const unique = values => [...new Set(values)];

// Names
const allNames = [...componentDeps.keys()];
const filterSubs = (name, names) => filter(children_(name), names);
const walk = name => [name, ...filterSubs(name, allNames).map(walk)].flat();
const names = root == ' none' ? allNames : unique(walk(root));
// Edges
const edgeId = (id, from, to) => ({ id, sources: [from], targets: [to] });
const edge = (from, to) => edgeId(`${from}->${to}`, from, to);
const edgesFrom = name => filterSubs(name, names).map(sub => edge(name, sub));
const edges = names.map(edgesFrom).flat();

// Children
const splitlines = (ids, mapper, width) => {
const reducer = ({ lines, count }, id) => {
const str = mapper(id);
const length = str.length;
const [line, newCount] =
10 + (count + length) * 6.4 > width
? [[], length]
: [lines.pop() || [], count + length];
line.push(str);
lines.push(line);
return { lines, count: newCount };
};
const lines = (ids || []).reduce(reducer, { lines: [], count: 0 }).lines;
return lines.map(line => line.join(','));
};
const children = names.map(name => {
const { props, events, color } = componentDeps.get(name);
const width = Math.max(120, name.length * 8.4 + 10);
const proplines = splitlines(props || [], id => id, width);
const eventlines = splitlines(events, id => `@${id}`, width);
const lines = [...proplines, ...eventlines];
const height = 25 + lines.length * 15;
const info = { label: name, text: lines.join('\n'), color };
return { id: name, width, height, info };
});

const layoutOptions = { 'elk.algorithm': 'layered' };
return { id: "root", layoutOptions, children, edges };
}
Insert cell
layout = new ELK().layout(graph)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
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