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;
}