createInput = (name, { min = 0, max = 1, step = 0.01, initialValue = 0.5 }) => {
const nValues = 100;
const innerHeight = height - margins.top - margins.bottom;
const innerWidth = width - margins.left - margins.right;
const yBase = height - margins.bottom;
const values = d3.range(min, max, (max - min) / nValues).concat(max);
const bandwidth = Math.max(1, Math.floor(innerWidth / nValues) - 1);
const halfMarkerWidth = 3 / 2;
const path = svg`<path fill="none" stroke="#e0e0e0" stroke-width=${bandwidth} />`;
const circle = svg`<circle fill="${resultColor}" r=5 />`;
const resultMarker = svg`<rect fill="${resultColor}" width=${2 *
halfMarkerWidth} y=${height - margins.bottom} />`;
const span = html`<span></span>`;
const inputLabel = html`<div class="input-label" />`;
const resultLabel = html`<div class="result-label"/>`;
const theSvg = svg`<svg viewbox="0 0 ${width} ${height}" style="cursor: col-resize">
${path}
${circle}
${resultMarker}
</svg>`;
const node = html`
<div class="input-graph" style="position: relative" >
${theSvg}
${inputLabel}
${resultLabel}
<h3 class="x-label">${tex`${name}`}</span>
</div>
`;
const xScale = d3
.scaleLinear([min, max], [margins.left, width - margins.right])
.clamp(true);
const yScale = d3.scaleLinear().range([height - margins.bottom, margins.top]);
const xAxisGenerator = d3.axisBottom(xScale);
const xAxisContainer = d3
.select(theSvg)
.append('g')
.attr("transform", `translate(${0}, ${height - margins.top})`)
.call(xAxisGenerator);
const yAxisGenerator = d3.axisRight(yScale).ticks(Math.round(height / 30));
const yAxisContainer = d3
.select(theSvg)
.append('g')
.attr("transform", `translate(${width - margins.right}, ${0})`)
.call(yAxisGenerator);
function update(event) {
const results = values.map(v => computeValueFor(name, v));
yScale.domain(d3.extent(results));
yAxisContainer.call(yAxisGenerator);
const value = viewof input.value[name];
const result = computeValueFor(name, value);
const x = xScale(value);
const y = yScale(result);
circle.setAttribute("cx", x);
circle.setAttribute("cy", y);
resultMarker.setAttribute("x", x - halfMarkerWidth);
resultMarker.setAttribute("y", y);
resultMarker.setAttribute("height", yBase - y);
inputLabel.innerText = value.toFixed(2);
inputLabel.style.left = x + "px";
inputLabel.style.top = height + "px";
resultLabel.innerText = result.toFixed(2);
resultLabel.style.left = x + "px";
resultLabel.style.top = y + "px";
if (event.detail.name === name) return;
path.setAttribute(
"d",
values
.map(
(v, i) =>
`M${xScale(v)},${yBase}v${yScale(computeValueFor(name, v)) - yBase}`
)
.join("")
);
}
const updateByMouse = event =>
setInputValue(name, quantizeWithStep(xScale.invert(event.x), min, step));
d3.select(theSvg)
.call(d3.drag().on("drag", updateByMouse))
.on("pointerdown", updateByMouse);
const listenerArgs = [eventName, update];
viewof input.addEventListener(...listenerArgs);
invalidation.then(() => viewof input.removeEventListener(...listenerArgs));
update({ detail: { name: null } });
return node;
}