I’d like to input integers k and n and then generate a k x n array
of input boxes where a user can enter a k x n matrix.
The goal is to illustrate row reduction.
I’ve done this in javascript, but I’d like to do it in observable so I can embed it in a quarto document.
The “form” input seems to just stack the input fields. Any other approaches?
Inputs library is a little limited when it comes to layout. Observable Framework works better in this regard, since you can put the inputs inside arbitrary HTML. I’d suggest checking it out.
Sticking to Notebooks/Quarto, there are a few options on the forums if you search that allow you to wrap arbitrary HTML as an Input. Instead of reproducing those here, I think you might appreciate knowing how it works under the hood. By using Generators.observe
you can make arbitrarily complex inputs and other dynamic elements. You can see the docs for it here. And here is an example of it in use:
viewof data = Generators.observe((notify) => {
// prepare some UI
const el = htl.html`<div>
<input name="a">
<input name="b">
</div>`;
// Assign a value to it
Object.assign(el, { value: { a: "", b: "" } });
// Emit the first value
notify(el);
// Create a listener that updates the value and calls notify when the inputs change
function onChange(event) {
let name = event.target.getAttribute("name");
let value = event.target.value;
el.value = { ...el.value, [name]: value };
notify(el);
}
el.addEventListener("input", onChange);
// Return a clean up function that will be called when the cell is disposed
return () => {
el.removeEventListener("input", onChange);
};
})
Inputs.form() has a template
option that lets you pass in your own HTML wrapper:
viewof matrix = Inputs.form(
d3.range(7 * 5).map(d => Inputs.number([0, 100], {value: 0, width: "auto"})),
{
template(inputs) {
return htl.html`<div style="display:grid;grid-template-columns:repeat(7, 1fr)">${inputs}`;
}
}
)
I saw this template argument but the documentation was pretty minimal. This is very helpful, thanks!
If you need labels for your input groups you might also find this notebook helpful:
Here’s another brief example that I wanted to share. Given a helper function like
details = (label, open) => inputs => htl.html`<details ${{open}}>
<summary style="cursor: pointer; width: max-content; font: bold 13px/1.5 var(--sans-serif);">${label}</summary>
<div style="padding-left: 1em">${
Array.isArray(inputs) ? inputs : Object.values(inputs)
}`
that returns a template callback, you can create complex collapsible forms like the following: