Public
Edited
Apr 25, 2024
1 star
Insert cell
Insert cell
{
const container = htl.html`<div>`;
areachart.style.display = "inline-block";
areachart.style.verticalAlign = "middle";
viewof domain.style.display = "inline-block";
viewof domain.style.verticalAlign = "middle";
container.append(areachart, viewof domain);
return container;
}
Insert cell
domain
Insert cell
areachart = Plot.plot({
color: { type: "categorical", domain: industryDomain },
y: {
transform: (d) => d / 1000,
label: "Unemployed (thousands)"
},

marks: [
Plot.areaY(
industries.filter((d) => domain.indexOf(d.industry) >= 0),
Plot.stackY(
{ order: (d) => domain.indexOf(d.industry) },
{
x: "date",
y: "unemployed",
fill: "industry"
}
)
),
Plot.ruleY([0])
]
})
Insert cell
viewof domain = categoryOrderInput(industryDomain)
Insert cell
function categoryOrderInput(domain, options = {}) {
let {
sampleWidth = 30,
sampleHeight = 18,
width = 220,
font = "9pt sans-serif",
lineHeight = 20,
checkboxes = true,
selectedDomain = domain,
colorScale
} = options;

if (!colorScale) colorScale = Plot.scale({ color: { domain } });

const div = htl.html`<div class="categoryInput">`;

// Interaction callbacks
let selected = null;

function dragOver(e) {
if (e.preventDefault) {
e.preventDefault();
}

if (e.target == selected) return;

if (isBefore(selected, e.target)) {
e.target.parentNode.insertBefore(selected, e.target);
} else {
e.target.parentNode.insertBefore(selected, e.target.nextSibling);
}
e.dataTransfer.dropEffect = "move";
}

function onchanged() {
div.value = div.getValue();
div.dispatchEvent(new CustomEvent("input"));
}

function dragEnd(e) {
if (e.preventDefault) {
e.preventDefault();
}
onchanged();
selected = null;
}

function dragStart(e) {
if (selected) return;
e.dataTransfer.dropEffect = "move";
e.dataTransfer.effectAllowed = "move";
e.dataTransfer.setData("text/plain", null);
selected = e.target;
}

function isBefore(el1, el2) {
let cur;
if (el2.parentNode === el1.parentNode) {
for (cur = el1.previousSibling; cur; cur = cur.previousSibling) {
if (cur === el2) return true;
}
}
return false;
}

Object.assign(div.style, {
lineHeight: `${lineHeight}px`,
width: `${width}px`
});
for (let cat of [...domain].reverse()) {
const row = htl.html`<div class="category" draggable="true">`;
const sample = htl.html`<div>`;
Object.assign(sample.style, {
background: colorScale.apply(cat),
height: `${sampleHeight}px`,
width: `${sampleWidth}px`,
minWidth: `${sampleWidth}px`,
verticalAlign: "middle",
display: "inline-block"
});

row.append(sample);
const checkbox = htl.html`<input type="checkbox">`;
checkbox.checked = selectedDomain.indexOf(cat) >= 0;
checkbox.onchange = onchanged;
row.append(checkbox);

const text = htl.html`<span>${cat}`;
Object.assign(text.style, {
font,
paddingLeft: "3px",
verticalAlign: "middle"
});
row.append(text);
row.value = cat;
div.append(row);
row.ondragend = dragEnd;
row.ondragstart = dragStart;
row.ondragover = dragOver;
}

div.getValue = () => {
const value = [];
for (let el of div.children) {
if (el.querySelector("input").checked) value.push(el.value);
}
return value.reverse();
};
div.value = div.getValue();
return div;
}
Insert cell
industryDomain = [...new Set(industries.map((d) => d.industry))]
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