Public
Edited
Mar 19, 2024
Importers
Insert cell
Insert cell
fanpalette({
// strokeWidth: 5,
brighter,
})
(nest)
Insert cell
Insert cell
nest = [ // order index corresponds with level in tree; add styling at will
{kind: "probe", palette: ["black"]},
{kind: "scholen", palette: ["#76B154", "#5BB7DD", "#E09750", "#C53B3B"]},
{kind: "woonwaarden", palette: ["#a1a9d2", "#CFB88C", "darkred", "#9B649D", "#7787BD", "#7B7E94", "#80AE56", "#0BBBEF", "#495570"]},
{kind: "platformeconomie", palette: ["#69a2b4", "#5a889e", "#4c6e89", "#3d5373", "#2f395e", "#201f48"]},
{kind: "waterland", palette: ["#469DDE", "#86912C", "#E69739"]},
{kind: "declaratie", palette: ramp()(stops)(6)}, // each vote has 6 facets
{kind: "nieuwe werken", palette: ramp()(["#B67940", "#F6D994"])(9)}, // each vote has 6 facets
{kind: "topvrouwen", palette: ["#C73734", "#D6773D", "#E5AC49", "#69334C"]},
{kind: "prompt", palette: ramp()()(6)},
{kind: "agility", palette: d3.schemeCategory10},
]
Insert cell
stops = [
"#69A2B4",
"#201F48",
]
Insert cell
fanpalette =
({
strokeWidth= 3,
brighter = 40,
} = {}) =>
(nest = []) =>
{
const µ = (n = 1) => 3 * n;
const w = µ(24);
const h = µ(12);
const leftMargin = µ(38);
const gap = µ(3);
const lighter = lighten(brighter);
const max = d3.max(nest.map(k => (k.palette || []).length));
const pad = d3
.create("svg")
.attr("width", width)
.attr("height", µ(16) * nest.length)
.attr("overflow", "visible")
;
const sheet = pad
.selectAll()
.data(nest)
.join("g")
.attr("transform", (d, i) => `translate(0, ${12 * µ(i + 1) + i * gap})`)
.style("font", "var(--monospace-font")
.style("font-size", "13px")
.style("text-anchor", "end")
;
const swatches = sheet
.selectAll("path")
.data(level => level.palette || [])
;
const blocks = swatches
.join("g")
.attr("transform", (d, i) => `translate(${leftMargin + µ(3) + µ(27) * i}, 0)`)
;
sheet
.append("text")
.text(level => level.kind)
.attr("alignment-baseline", "middle")
.attr("dx", leftMargin)
.attr("dy", -h / 2)
;
blocks
.append("rect")
.attr("width", w)
.attr("height", h)
.attr("y", -h)
.attr("fill", d => lighter(d).hex())
.attr("stroke", d => d)
.attr("stroke-width", strokeWidth)
.attr("rx", 6)
;
blocks
.append("text")
.text(d => d.toUpperCase())
.attr("fill", d => contrast(lighter(d)))
.attr("dx", w / 2)
.attr("dy", -h / 2)
.attr("alignment-baseline", "middle")
.style("text-anchor", "middle")
.style("font-weight", "bold")
;
return pad.node();
}
Insert cell
import {contrast} from "@martien/color-tints-and-shades"
Insert cell
lighten = (
(lightness) =>
(color) =>
{
const {l, c, h} = d3.lch(color);
return d3.lch(l + lightness, c, h);
}
)
Insert cell
tree = (data) => {
const root = d3.hierarchy(data);
const dx = 3;
const dy = 90;
const isPrompt = (node) => node.height === 0;
const label = d => typeof d === "object" ? d.name : d;
const tree = d3.tree;
const padding = 1;
let height;
const r = 3; // radius of nodes
let link; // given a node d, its link (if any)
const stroke = "#555"; // stroke for links
const fill = "white"; // fill for nodes
const linkTarget = "_blank"; // the target attribute for links (if any)
const title = (d, n) => `${n.ancestors().reverse().map(d => d.data.name).join(" » ")}`;
const halo = "#fff", // color of label halo
haloWidth = 3 // padding around the labels
;
// const root = boom
// .copy()
// .each((node, j) => {
// const children = node.children;
// if (children) children.forEach((_, i) => children[i].z = i);
// });

// Compute labels and titles.
const descendants = root.descendants();
const L = label == null ? null : descendants.map(d => label(d.data));

// Compute the layout.
// const dx = 10;
// const dy = width / (root.height + padding) / 2;
tree()
.nodeSize([dx, dy])
.separation((a, b) => (a.parent == b.parent ? 5:15))
(root);

// Center the tree.
let x0 = Infinity;
let x1 = -x0;
root.each(d => {
if (d.x > x1) x1 = d.x;
if (d.x < x0) x0 = d.x;
if (d.height === 1) d.facetY = d.children[0].x - d.x - 15;
});

// Compute the default height.
if (height === undefined) height = x1 - x0 + dx * 2 + 9 * 9;

const svg = d3
.create("svg")
.attr("viewBox", [-dy * padding / 2, x0 - dx - 72, width, height])
.attr("width", width)
.attr("height", height)
.attr("style", "max-width: 100%; height: auto; height: intrinsic;")
.attr("font-family", "monospace")
.attr("font-size", 10);

svg
.append("g")
.attr("fill", "none")
.attr("stroke", "red")
.attr("stroke-opacity", 1)
.attr("stroke-linecap", "round")
// .attr("stroke-linejoin", strokeLinejoin)
.attr("stroke-width", 1)
.selectAll("path")
.data(root.links())
.join("path")
.attr("d",
d3.link(d3.curveBumpX)
.x(d => d.y)
.y(d => d.x))
.attr("stroke", "red")
;

const node = svg
.append("g")
.selectAll("g")
.data(root.descendants())
.join("g")
.attr("transform", d => `translate(${d.y},${d.x})`);

node
.append("circle")
.attr("fill", d => d.children ? stroke : fill)
.attr("stroke", "black")
.attr("stroke-width", 1)
.attr("r", r)
;

if (L) {
node
.append("text")
.text((d, i) => L[i])
.attr("dx", d => isPrompt(d) ? 9 : d.height === 1 ? dy - 6 : d.depth === 0 ? 0 : -9)
.attr("y", d => d.height === 1 ? d.facetY : d.depth === 0 ? x0 - dx - 9 * 5 : 0)
.attr("alignment-baseline", "central")
.attr("text-anchor", d => isPrompt(d) || d.depth === 0
? "start"
: d.height === 1
? "start"
: "end"
)
.attr("font-weight", d => d.height === 1 || d.depth === 0 ? "bold" : "normal")
.attr("font-size", d => d.depth === 0 ? "larger" : "normal")
.attr("stroke", "white")
.attr("stroke-width", 3)
.attr("paint-order", "stroke")
;
}

return svg.node();
}
Insert cell
// convert a plain or dashed indented list to a hierarchy
md2json = (tabSize = 4) => (text) => {
text = text.trim();
var children = [];
for (const [, {length}, dash, name] of text.matchAll(/^(\s*)(- )?(.*)$/gm)) {
const level = length / tabSize;
if (level === 0)
children.push({name});
else {
let parent = children.at(-1);
for (let i = 0; i < level - 1; i++) {
parent = parent.children.at(-1)
}
if (parent.children)
parent.children.push({name})
else
parent.children = [{name}];
}
}
return children[0];
}
Insert cell
md2json(2)(seedling)
Insert cell
tree(md2json(2)(indentedPlainSeed))
Insert cell
seedling = `
Hoe kunnen organisaties in hun personeelsbeleid de principes van Het Nieuwe Werken zo invullen dat deze bijdragen aan duurzaamheid?
Organisatie
Principe
Thema’s en taken zijn leidend, niet de grenzen van de organisatie.
Omschrijving
De organisatie is ingericht rond thema’s; deze worden niet opgeknipt om in bestaande structuren te passen.
Duurzame invulling
Medewerkers werken vanuit bestaande organisaties in thematische projectgroepen en programma’s.
De organisatie kan te allen tijde voldoen aan een veranderende behoefte aan menskracht.
De organisatie heeft zicht op de capaciteiten van de flexibel in te zetten medewerkers.
De organisatie biedt medewerkers inzicht in beschikbare functies en opdrachten.
Leiderschap
Principe
Leiderschap op basis van collectieve ambities van de organisatie.
Omschrijving
Leidinggevenden sturen medewerkers aan op basis van de collectieve ambities van de organisatie.
Duurzame invulling
Leidinggeven geven medewerkers ruimte om naar eigen inzicht (duurzaamheids)resultaten te behalen.
Leidinggevenden en medewerkers formuleren gezamenlijk de gewenste organisatiecultuur en werkhouding.
Leidinggevenden vertonen voorbeeldgedrag, zijn omgevingsbewust en hebben een langetermijnfocus.
De organisatie selecteert medewerkers die de (duurzaamheids)ambities van de organisatie onderschrijven.
Werkplek
Principe
Activiteitsgerelateerde werkplekken en optimale ict-ondersteuning.
Omschrijving
Medewerkers kunnen per situatie de optimale werkplek kiezen, hierin ondersteund door ict-toepassingen.
Duurzame invulling
Medewerkers werken in duurzame, activiteitsgerelateerde ruimtes en hebben geen vaste werkplek.
De organisatie biedt medewerkers overal en altijd toegang tot e-mail, documenten en ict-voorzieningen.
De organisatie benut werkfaciliteiten die medewerkers zelf al bezitten zoals een laptop of mobiele telefoon.
Communicatie
Principe
Transparantie tenzij.
Omschrijving
Medewerkers zijn open over hun werk: naar elkaar, naar belanghebbenden en naar de samenleving.
Duurzame invulling
De organisatie stimuleert en faciliteert kennisdeling en borging hiervan binnen de organisatie.
Medewerkers en organisatie zijn naar elkaar en de maatschappij open over activiteiten, ambities en middelen.
Medewerkers hebben de ruimte om misstanden aan te kaarten, zonder consequenties voor hun baan.
Vaardigheden
Principe
Digitale kennis en vaardigheden zijn onontbeerlijk.
Omschrijving
Medewerkers, ook leidinggevenden, benutten (mobiele) ict-toepassingen in hun dagelijks werk.
Duurzame invulling
Medewerkers werken efficiënt doordat zij (mobiele) ict-toepassingen ten volle benutten.
Medewerkers ontwikkelen digitale kennis en vaardigheden, de organisatie faciliteert hen hierin.
Regie
Principe
Zelf bepalen hoe, waar, wanneer en met wie je werkt aan resultaten.
Omschrijving
Medewerkers worden beoordeeld op hun resultaten; ze bepalen zelf hoe ze deze realiseren.
Duurzame invulling
De organisatie biedt medewerkers faciliteiten zodat ze plaats- en tijdonafhankelijk kunnen werken.
Leidinggevenden en medewerkers maken resultaatafspraken op persoonlijk niveau en op teamniveau.
Vergaderingen vinden niet langer periodiek plaats, maar medewerkers vergaderen (digitaal) naar behoefte.
De organisatie stimuleert medewerkers te reizen per openbaar vervoer, fiets of milieuvriendelijke auto.
Waarde
Principe
Niet je functie, maar talent bepaalt je waarde.
Omschrijving
Talent bepaalt de waarde van een medewerker.
Duurzame invulling
De organisatie beloont medewerkers op basis van hun (duurzaamheids)resultaten en -ontwikkeling.
De organisatie selecteert op houding, inzet, kennis en vaardigheden, niet op leeftijd, anciënniteit en status.
De organisatie benut talenten van mensen met verschillende culturele achtergronden.
De organisatie of de medewerker verbreekt de arbeidsrelatie bij achterblijvende resultaten en ontwikkeling.
Ontwikkeling
Principe
Zelf verantwoordelijk voor je eigen ontwikkeling.
Omschrijving
Medewerkers bepalen in welke richting en hoe zij zich ontwikkelen.
Duurzame invulling
Medewerkers beschikken over een eigen scholingsbudget dat zij naar eigen inzicht kunnen besteden.
Medewerkers maken hun ontwikkeling zichtbaar in een portfolio.
Medewerkers krijgen de kans om in werktijd ervaring op te doen binnen en buiten hun organisatie.
Medewerkers kunnen ontwikkelingstrajecten volgen, zoals opleiding, talentenscan, stage en coach.
Medewerkers krijgen mogelijkheden hun fysieke, psychische en sociale gezondheid te onderhouden.
Maatwerk
Principe
Variëteit is standaard.
Omschrijving
De voorzieningen waarmee medewerkers werken en (arbeids)voorwaarden zijn voor iedereen aanpasbaar.
Duurzame invulling
De organisatie biedt medewerkers flexibele arbeidsvoorwaarden.
Medewerkers beschikken over een eigen werkbudget, bijvoorbeeld voor vervoer en werkfaciliteiten.
`
Insert cell
md2json(2)(indentedDashedSeed) // convert a dashed indented list to a hierarchy
Insert cell
md2json(2)(indentedPlainSeed) // convert a plain indented list to a hierarchy
Insert cell
// convert a text file with a slash-separated hierarchy to a d3.hierarchy
slash2hierarchy = R.pipe(
R.split("\n"),
R.reject((s) => s === ""),
d3.stratify().path(d => d),
d3.hierarchy,
(x) => x.each((n) => n.name = n.data.id.split("/").at(-1))
)
Insert cell
agilityTree = slash2hierarchy(slashedTreeSeed)
Insert cell
// convert a d3.hierarchy to its indented plain text format
tree2text = (tree) => {
const lines = [];
tree.eachBefore(({depth, name}) => lines.push(name.padStart(name.length + depth * 2)));
return lines.join("\n");
}
Insert cell
indentedPlainSeed = tree2text(agilityTree)
Insert cell
Insert cell
Insert cell
import {swatches, ramp} from "@martien/color"
Insert cell
R = require("ramda")
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