viewof rangeValue = {
const width = 500;
const marginLR = 24;
const chartWidth = width - marginLR * 2;
const chartHeight = 60;
const labelRowHeight = 20;
const rectHeight = chartHeight - labelRowHeight;
const handleWidth = 8;
const domain = [domainFrom, domainTo];
const range = [0, chartWidth];
const scale = d3.scaleLinear(domain, range);
const domainDiff = domainTo - domainFrom;
const rangeColorKeys = Object.keys(rangeColors);
const ranges = rangeColorKeys.map((key, i) => {
return {
color: rangeColors[key],
offset: domain[0] + i * Math.round(domainDiff / rangeColorKeys.length),
size: Math.round(domainDiff / rangeColorKeys.length)
};
});
const handleData = ranges.slice(1).map((d, i) => {
return {
x: scale(d.offset)
};
});
const svg = html`<svg width=${width} height=${chartHeight}>
<marker
id="arrow"
viewBox="0 0 10 10"
refX="0"
refY="5"
markerWidth="6"
markerHeight="6"
orient="auto">
<path d="M 0 0 L 10 5 L 0 10 z" fill="grey" />
</marker>
</svg>`;
const vis = d3
.select(svg)
.append("g")
.attr("transform", `translate(${marginLR})`);
const dragBehaviour = d3
.drag()
.container(vis)
.on("drag", (e) => {
const handle = e.subject;
const handleIndex = handleData.indexOf(handle);
const minExtent =
handleIndex === 0 ? range[0] : handleData[handleIndex - 1].x + 1;
const maxExtent =
handleIndex === handleData.length - 1
? range[1]
: handleData[handleIndex + 1].x - 1;
handle.x = Math.min(maxExtent, Math.max(minExtent, e.x));
update();
});
const rects = vis.selectAll("rect").data(ranges).enter().append("rect");
const handles = vis
.selectAll("g")
.data(handleData)
.enter()
.append("g")
.classed("handle", true)
.call(dragBehaviour);
const handleRects = handles
.style("cursor", "ew-resize")
.append("rect")
.attr("x", -handleWidth / 2)
.attr("y", 0)
.attr("width", handleWidth)
.attr("height", rectHeight + labelRowHeight)
.attr("fill", "rgba(10,10,10,0.0)");
const handleLines = handles
.append("line")
.classed("handle-line", true)
.attr("x1", 0)
.attr("x2", "0")
.attr("y2", 0)
.attr("y1", rectHeight + labelRowHeight / 4)
.attr("stroke", "grey")
.attr("marker-start", "url(#arrow)");
const labelRow = vis
.append("g")
.classed("labels", true)
.attr("transform", `translate(0, ${rectHeight})`);
const labels = labelRow
.selectAll("text")
.data([{ x: 0 }].concat(handleData).concat([{ x: range[1] }]))
.enter()
.append("text")
.attr("text-anchor", "middle")
.attr("y", labelRowHeight)
.attr("font-size", 14);
update();
function update() {
labels.attr("x", (d) => d.x).text((d) => Math.round(scale.invert(d.x)));
ranges.forEach((d, i) => {
d.offset =
i === 0 ? domain[0] : Math.round(scale.invert(handleData[i - 1].x));
d.size =
i === ranges.length - 1
? domain[1] - d.offset
: Math.round(scale.invert(handleData[i].x)) - d.offset;
});
rects
.attr("transform", (d) => `translate(${scale(d.offset)})`)
.attr("x", 0)
.attr("y", 0)
.attr("width", (d) => scale(domain[0] + d.size))
.attr("height", rectHeight)
.attr("fill", (d) => d.color);
handles.attr("transform", (d) => `translate(${d.x})`);
svg.value = ranges;
svg.dispatchEvent(new CustomEvent("input"));
}
return svg;
}