inputs icon indicating copy to clipboard operation
inputs copied to clipboard

Range arrows don't work with format function

Open mcmcclur opened this issue 4 years ago • 5 comments

Try the following in an Observable notebook:

Inputs.range([0, 1], { format: (x) => x + 2 })

Next, try to adjust the value using the little arrows in the number field circled in the image below. I get a complaint that the value is outside the range. I'd guess that those arrows are tied to the display, rather than the value.

Screen Shot 2021-10-23 at 8 46 21 AM

mcmcclur avatar Oct 23 '21 12:10 mcmcclur

The format function should return a string value that is compatible with native number parsing.

Maybe the documentation should mention that the format function also needs to (more or less) faithfully represent the value?

Fil avatar Oct 23 '21 14:10 Fil

Maybe the documentation should mention that the format function also needs to (more or less) faithfully represent the value?

Yes, or we should deprecate the option or even remove it, because most of the time specifying a custom format will break the input.

mbostock avatar Oct 23 '21 14:10 mbostock

Maybe the documentation should mention that the format function also needs to (more or less) faithfully represent the value?

Yeah, that might be all you can do since I guess the box is an HTML element of the form

<input type="number"></input>

I suppose there's not a lot you can do with that. It seems inconsistent with other inputs, though, where there's great flexibility between the input display and the returned value. My usage case is more like:

viewof depth = Inputs.range([0, 9], { step: 1, format: (n) => 2 ** n })

where depth refers to a number of times a quantity has been doubled and I'd like to the display to refer new magnitude.

mcmcclur avatar Oct 23 '21 14:10 mcmcclur

I don’t think there’s a way to do this with the number input because the steps are not regularly spaced (e.g., clicking up should jump from 4 to 8, not 4 to 5). So, I think your best bet is to show the number of times a quantity has doubled.

FWIW, This will mostly do what you want, where magnitude is powers of two:

viewof magnitude = Object.assign(Inputs.range([2**0, 2**9], {
  value: 256,
  transform: (n) => Math.log2(n),
  invert: (n) => 2**Math.round(n),
  validate: (e) => e.valueAsNumber === 2**Math.round(Math.log2(e.valueAsNumber))
}), {
  onsubmit: (e) => {
    e.currentTarget.value = 2**Math.round(Math.log2(e.currentTarget.value));
    e.preventDefault();
  }
})

The input will not report a value if it’s not a power of two. So if you use the number input, you get the awkward situation that it shows non-powers of two and ignores the input. You can hit Enter in the number input and it will snap to the closest valid value. But I think this awkwardness is unavoidable with this style of interface, so I’d stick to the simpler approach:

viewof depth = Inputs.range([0, 9], {step: 1})

mbostock avatar Oct 23 '21 15:10 mbostock

Thanks for the advice! In the end, I simply hid the number input with CSS and redisplayed the information in another location. It's the "Side length" slider in this notebook: https://observablehq.com/@mcmcclur/box-counting-dimension-examples

mcmcclur avatar Oct 25 '21 18:10 mcmcclur