Public
Edited
Feb 2, 2023
Importers
4 stars
Insert cell
Insert cell
colors = ["#007aff", "#fafafa", "#428484", "rgba(0,0,0,0.54)", colorPicker]
Insert cell
selectedColor
Insert cell
viewof selectedColor = SwatchInput(colors, {
// value: initial color
label: "Select color",
// output will be array instead
multiple: false,
output: "contrast"
})
Insert cell
viewof colorPicker = Inputs.color({ value: "#824282", label: "Last color" })
Insert cell
Insert cell
Insert cell
SwatchInput = async (
colors,
{ value, label, multiple = false, output = "check" } = {}
) => {
let indexes = [];
const colorInput = html`<div style="white-space: nowrap; padding-top: 0.5rem; padding-bottom: 0.5rem;">
${colors.map((color, idx) => {
const c = color.toString();
let style = `position: relative; appearance: none; border: none; outline: none; display: inline-block; width: 40px; height: 40px; background-color: ${c}; border: 1px solid rgba(0,0,0,.12); border-radius: 4px; margin-right: 2px;`;
const button = html`<button style="${style}">
<div style="color: ${isWhiteText(c) ? "white" : "black"}">
${output === "check" ? icon("checkmark") : ""}
${output === "contrast" ? html`<code>${contrast()}</code>` : ""}
</div>
</button>`;
button.addEventListener("click", (e) => {
e.preventDefault();
set(node, idx);
});
return button;
})}
</div>`;
const id1 = DOM.uid().id;
let formStyle = "display: flex; align-items: center;";
let node = html`
<form id="${id1}" style="${formStyle}">
${label ? html`<label>${label}</label>` : ""}
${colorInput}
<output>${value ? value.toString() : ""}</output>
</form>
<style>
#${id1} {
display: flex;
align-items: center;
flex-wrap: wrap;
min-height: 25.5px;
--length1: 3.25px;
--length2: 6.5px;
--length3: 13px;
--label-width: 120px;
--input-width: 240px;
font: 13px/1.2 var(--sans-serif);
}
#${id1} > label {
width: 100%;
padding-bottom: 3px;
}
#${id1} > output {
font-family: ui-monospace,var(--monospace);
white-space: pre;
margin-left: var(--length2);
}

@media only screen and (min-width: 30em) {
#${id1} > label {
flex-shrink: 0;
align-self: center;
padding: 5px 0 4px 0;
width: var(--label-width);
margin-right: var(--length2);
}
#${id1} {
flex-wrap: nowrap;
width: calc(var(--input-width) + var(--label-width));
max-width: 100%;
}
}
</style>
`;

// Update the display whenever the value changes
Object.defineProperty(node, "value", {
get() {
return value;
},
set(v) {
const index = +v;
const c = colors[index];
if (multiple) {
if (value === undefined) {
value = [];
}
if (v !== undefined) {
if (indexes.includes(v)) {
indexes = indexes.filter((vv) => vv !== v);
} else {
indexes.push(v);
}
}
value = indexes.map((idx) => colors[idx]);
} else {
value = c;
indexes = [v];
}
restyle();
}
});

// Set the initial value
if (multiple) {
if (value === undefined) {
value = [];
indexes = [];
} else {
indexes = value.map((v) => colors.findIndex((c) => c.equals(v)));
}
set(node);
} else {
let initIndex = value ? colors.findIndex((c) => c.equals(value)) : 0;
set(node, initIndex);
}

function restyle(newIndex) {
const btns = node.querySelectorAll("button");
btns.forEach((btn, idx) => {
let checked = indexes.includes(idx);
let ch = btn.getElementsByTagName("div")[0];
if (checked) {
btn.style.boxShadow = "0 0 0 2px rgba(0,0,0,1)";
btn.style.zIndex = "10";
ch.style.display = "block";
} else {
btn.style.boxShadow = "";
btn.style.zIndex = "";
ch.style.display = "none";
}
});
// select the output
const output = node.querySelector("output");
if (output) {
output.innerText = value.toString();
}
}

restyle();

return node;
}
Insert cell
// helper function
function set(input, value) {
input.value = value;
input.dispatchEvent(new Event("input", { bubbles: true })); // Native events bubble, so we should too
}
Insert cell
import { icon } from "@mateh/icon"
Insert cell
import { ColorRGB, isWhiteText, contrast } from "@mateh/palette"
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