function searchCheckbox(
data,
options
) {
options = {
value: [],
optionsCheckboxes: undefined,
optionsSearch: {
filter: fullSearchFilter
},
height: 300,
...options
};
function cloneIgnoring(obj, attrToIgnore) {
const { [attrToIgnore]: _, ...rest } = obj;
return rest;
}
data = Array.from(data);
let checkboxes = Inputs.checkbox(
data,
options.optionsCheckboxes || cloneIgnoring(options, "label")
);
const search = Inputs.search(data, options.optionsSearch || options);
const btnAll = html`<button>All</button>`;
const btnNone = html`<button>None</button>`;
let selected = new Map(Array.from(options.value).map((d) => [d, true]));
function countSelected() {
return Array.from(selected.entries()).filter(([k, v]) => v).length;
}
function changeSome(sel, changeTo) {
for (let o of sel) selected.set(o, changeTo);
}
function selectedFromArray(sel) {
changeSome(data, false);
changeSome(sel, true);
}
function selectedToArray() {
return Array.from(selected.entries())
.filter(([k, v]) => v)
.map(([k, v]) => k);
}
// HTML
let output = htl.html`<output style="font-size: 80%; font-style: italics">(${countSelected()} of ${
data.length
} selected)</output>`;
const component = htl.html`${
options.label ? htl.html`<label>${options.label}</label>` : ""
}
${output}
<div style="display:flex">
${search}
<div style="margin: 0 5px"> ${btnAll} </div>
<div> ${btnNone} </div>
</div>
<div style="max-height: ${options.height}px; overflow: auto">${checkboxes}</div>`;
// Update the display whenever the value changes
Object.defineProperty(component, "value", {
get() {
return selectedToArray();
},
set(v) {
selectedFromArray(v);
}
});
function updateValueFromSelected() {
debugger;
checkboxes.value = selectedToArray();
output.innerHTML = `(${countSelected()} of ${data.length} selected)`;
component.dispatchEvent(new Event("input", { bubbles: true }));
}
btnAll.addEventListener("click", () => {
changeSome(search.value, true);
updateValueFromSelected();
});
btnNone.addEventListener("click", () => {
changeSome(search.value, false);
console.log("None", selectedToArray());
updateValueFromSelected();
});
component.value = selectedToArray();
search.addEventListener("input", (evt) => {
evt.stopPropagation();
// Hide all the checkboxes that aren't in the searchbox result
for (let check of checkboxes.querySelectorAll("input")) {
if (search.value.includes(data[+check.value])) {
check.parentElement.style.display = "inline-block";
} else {
check.parentElement.style.display = "none";
}
}
// We don't really need to update when value when searching
// component.dispatchEvent(new Event("input", { bubbles: true }));
});
checkboxes.addEventListener("input", (evt) => {
// avoids duplicated events
evt.stopPropagation();
selectedFromArray(checkboxes.value);
updateValueFromSelected();
});
return component;
}