function scentedCheckbox(data, attr = (d) => d[0], _options) {
let options = {
barWidth: 50,
barFill: "#cdcdcd",
barBorder: "#bbb",
barBorderWidth: "0.5px",
showTotal: false,
selectAll: true,
format: (d) => d,
cutoff: 5,
othersLabel: "Others",
arrayAttrib: false,
valueFont: `ultra-condensed 0.8em "Fira Sans", sans-serif`,
valueFmt: d3.format(",d"),
search: false,
checkbox: Inputs.checkbox,
maxHeight: null,
..._options
};
if (options.arrayAttrib || (data.length && Array.isArray(attr(data[0])))) {
let unrolledData = [];
data = data
.map((d) => {
const attrD = attr(d);
return attrD
? attrD.map((attrValue) => ({ ...d, __unrolledAttr: attrValue }))
: [{ ...d, __unrolledAttr: attrD }];
})
.flat();
attr = (d) => d.__unrolledAttr;
options.arrayAttrib = true;
}
const dataGrouped = d3.group(data, attr);
let dataGroupedSorted = Array.from(dataGrouped.entries()).sort((a, b) =>
d3.descending(a[1].length, b[1].length)
);
console.log("dataGrouped", dataGrouped);
let othersKeys = [];
if (options.cutoff) {
let dataGroupsSortedWithCutoff = [];
let othersGroup = [options.othersLabel, []];
for (let [k, v] of dataGroupedSorted) {
if (v.length > options.cutoff) {
dataGroupsSortedWithCutoff.push([k, v]);
} else {
othersGroup[1].push(v);
othersKeys.push(k);
}
}
if (othersGroup[1].length > 0) {
dataGroupsSortedWithCutoff.push(othersGroup);
dataGroupedSorted = dataGroupsSortedWithCutoff;
dataGrouped.set(options.othersLabel, othersGroup[1]);
}
}
const keys = dataGroupedSorted.map((d) => d[0]);
const values = dataGroupedSorted.map((d) => d[1]);
const maxValue = d3.max(values, (v) => v.length);
const totalValue = d3.sum(values, (v) => v.length);
const fmtPct = d3.format(".1%");
const fmt = d3.format(",");
const x = d3
.scaleLinear()
.domain([0, options.showTotal ? totalValue : maxValue])
.range([0, options.barWidth]);
const oldFormat = options.format;
options.format = (d) => {
const dValue = dataGrouped.get(d).length;
return htl.html`${oldFormat(d)}
<span
style='
position: relative;
top: 0px;
left: 0px;
display: inline-block;
'
title='${d} ${dValue} records ${fmtPct(dValue / totalValue)}'
>
<span
style='
min-width:${x.range()[1]}px;
border: solid ${options.barBorderWidth} ${options.barBorder};
display:flex;
height: 100%;
'> </span>
<span
style='
min-width:${x(dValue)}px;
background:${options.barFill};
display:flex;
position: absolute;
top: ${options.barBorderWidth};
left: ${options.barBorderWidth};
height: calc(100% - 3 * ${options.barBorderWidth});
align-items: center;
font: ${options.valueFont};
'>${options.valueFmt(dValue)}</span>
</span>
`;
};
if (options.selectAll && !options.value) {
options.value = keys;
}
let checkboxes;
if (options.search) {
checkboxes = searchCheckbox(keys, options);
} else {
checkboxes = options.checkbox(keys, options);
}
const target = htl.html`<div
style="${
options.maxHeight ? `max-height:${options.maxHeight}; overflow: scroll;` : ""
}">${checkboxes}</div>`;
checkboxes.oninput = (evt) => {
setValue();
};
function setValue() {
let value = [];
for (let v of checkboxes.value) {
if (v === options.othersLabel) {
value = value.concat(othersKeys);
} else {
value.push(v);
}
}
target.value = value;
target.dispatchEvent(new CustomEvent("input", { bubbles: true }));
}
setValue();
target.addEventListener("input", () => {
checkboxes.dispatchEvent(new Event("input", { bubbles: false }));
});
return target;
}