Published
Edited
Feb 23, 2021
6 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
createIconIdSelector = () => {
let value = "battery-charging";
// see: https://observablehq.com/@nikita-sharov/bootstrap-icons
// see also: https://observablehq.com/@jashkenas/url-querystrings-and-hash-parameters
if (location.hash !== "") {
const id = location.hash.slice(1, location.hash.length); // removing the hash character
if (symbols.has(id)) {
value = id;
}
}
return select({
title: "Icon",
options: identifiers,
description: "Use <a href='https://observablehq.com/@nikita-sharov/bootstrap-icons'>@nikita-sharov/bootstrap-icons</a> for pre-filtering.",
value
});
}
Insert cell
symbols = {
const url = "https://cdn.jsdelivr.net/npm/bootstrap-icons@1.3.0/bootstrap-icons.svg";
const literal = await fetch(url).then(response => response.text());
const symbols = html`${literal}`.querySelectorAll("symbol");
const keyValuePairs = Array.from(symbols, symbol => [symbol.id, symbol]);
return new Map(keyValuePairs);
}
Insert cell
identifiers = [...symbols.keys()].sort()
Insert cell
createIconNumberSlider = () => {
const getOneBasedValue = () => identifiers.indexOf(viewof iconId.value) + 1;
const iconNumber = slider({
title: "Number",
min: 1,
max: identifiers.length,
value: getOneBasedValue(),
step: 1,
description: "Use keyboard arrow keys to step one icon at a time, when focused."
});
iconNumber.addEventListener("input", (e) => {
// avoid endless loops
if (e.detail !== "ignore") {
viewof iconId.input.value = identifiers[iconNumber.value - 1];
viewof iconId.dispatchEvent(new CustomEvent("input"));
}
});
viewof iconId.addEventListener("input", () => {
iconNumber.input.value = getOneBasedValue();
iconNumber.dispatchEvent(new CustomEvent("input", {detail: "ignore"}));
})
return iconNumber;
}
Insert cell
showIcon = () => {
const cellCount = 16;
const sideLength = cellCount * cellLength;
const composition = `
<svg class="inspector" width="${sideLength + 1}" height="${sideLength + 1}">
<g class="grid">
${grid(sideLength, cellLength)}
</g>
<g class="icon" style="color: ${fillColor}">
${symbols.get(iconId).outerHTML}
<use href="#${iconId}" fill-opacity="${fillOpacity}" />
</g>
</svg>`;

return html`
<figure>
${composition}
<figcaption>All icons are designed in a 16x16 grid, so far (v1.3).</figcaption>
</figure>`;
}
Insert cell
grid = (sideLength, cellLength) => {
const magicOffset = 0.5; // see: https://stackoverflow.com/a/61013292

const className = (tick) => {
if ((tick === magicOffset) || (tick === (sideLength + magicOffset))) {
return "outer-grid-line";
} else {
return "inner-grid-line";
}
}
const verticalLine = (x) =>
`<line class="${className(x)}" x1="${x}" y1="${magicOffset}" x2="${x}" y2="${sideLength + magicOffset}" />`;
const horizontalLine = (y) =>
`<line class="${className(y)}" x1="${magicOffset}" y1="${y}" x2="${sideLength + magicOffset}" y2="${y}" />`;
let grid = "";
for (let tick = magicOffset; tick <= (sideLength + magicOffset); tick += cellLength) {
grid += verticalLine(tick);
grid += horizontalLine(tick);
}
return grid;
}
Insert cell
html`<style>
.outer-grid-line {
stroke: #767676;
}

.inspector:hover .outer-grid-line {
stroke: #4f4f4f;
}

.inner-grid-line {
stroke: #e8e8e9;
}
</style>`
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