function searchCheckbox(
data,
options
) {
options = {
value: [],
optionsCheckboxes: undefined,
format: (d) => d,
optionsSearch: {
format: () => "",
filter: fullSearchFilter
},
height: 300,
debug: false,
...options,
};
function cloneIgnoring(obj, attrToIgnore) {
const { [attrToIgnore]: _, ...rest } = obj;
return rest;
}
let debug = options.debug;
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);
}
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>`;
Object.defineProperty(component, "value", {
get() {
return selectedToArray();
},
set(v) {
selectedFromArray(v);
}
});
function updateValueFromSelected() {
checkboxes.value = selectedToArray();
if (debug) console.log("searchCheckboxes", checkboxes.value);
output.innerHTML = `(${countSelected()} of ${data.length} selected)`;
component.dispatchEvent(new Event("input", { bubbles: true }));
component.style.zIndex = 1;
}
btnAll.addEventListener("click", () => {
changeSome(search.value, true);
updateValueFromSelected();
});
btnNone.addEventListener("click", () => {
changeSome(search.value, false);
updateValueFromSelected();
});
component.value = selectedToArray();
search.addEventListener("input", (evt) => {
for (let check of checkboxes.querySelectorAll("input")) {
if (search.value.includes(data[+check.value])) {
check.parentElement.style.display = "inline-flex";
} else {
check.parentElement.style.display = "none";
}
}
});
checkboxes.addEventListener("input", (evt) => {
evt.stopPropagation();
selectedFromArray(checkboxes.value);
updateValueFromSelected();
});
return component;
}