primitives icon indicating copy to clipboard operation
primitives copied to clipboard

[Slider] Incorrect range mapping

Open latin-1 opened this issue 2 years ago • 41 comments

Bug report

Dragging the thumb or clicking on the track.

Current Behavior

Current Behavior

Expected behavior

Expected Behavior

Reproducible example

CodeSandbox Template

Suggested solution

Modify the mapping function.

$$ \frac12W_{thumb}+V_{percent}(W_{track}-W_{thumb}) $$

Additional context

Other implementations:

  • https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/range
  • https://react-spectrum.adobe.com/react-spectrum/Slider.html

Note: There is also another type of slider:

Another Type

  • https://mui.com/material-ui/react-slider/
  • https://baseweb.design/components/slider/

Your environment

Software Name(s) Version
Radix Package(s) @radix-ui/react-slider 1.1.0
React n/a
Browser
Assistive tech
Node n/a
npm/yarn
Operating System

latin-1 avatar Feb 20 '23 09:02 latin-1

Hey @latin-1, the current implementation is by design. We interpolate the size of the thumb over the whole range so it doesn't get out of bounds (outside of its natural bounding box).

What is "correct" is up to interpretation, for example, our implementation matches the native input range on MacOS:

CleanShot 2023-02-20 at 15 57 22@2x

That said, I agree that this is perhaps a little opinionated, and something we have discussed about revisiting at some point in the future, by potentially providing a way to opt into different positioning strategies.

benoitgrelard avatar Feb 20 '23 15:02 benoitgrelard

@benoitgrelard You misunderstood what I meant.

The native one and yours do not behave in the same way. For example, if I press the mouse at the center of the thumb and then drag to the right edge, the cursor should remain at the center. If I click close to the edge, it should seek to the track edge (the min/max value) rather than a value close to the edge. That's how the native one act.

latin-1 avatar Feb 20 '23 16:02 latin-1

The third picture is just for reference. It's not related to this issue.

latin-1 avatar Feb 20 '23 16:02 latin-1

// current behavior:
value = event.offsetX / tracker.width
// expected behavior:
value = clamp((event.offsetX - thumb.width / 2) / (tracker.width - thumb.width), 0, 1)

latin-1 avatar Feb 20 '23 16:02 latin-1

That's not the behaviour I see, you can clearly see it jump here:

https://user-images.githubusercontent.com/1539897/220157300-0800a81e-1f4f-4f5b-98ae-1acebde6e921.mp4

benoitgrelard avatar Feb 20 '23 16:02 benoitgrelard

@benoitgrelard I mean close to, especially, the distance between the cursor and the edge should less than half of the thumb size.

latin-1 avatar Feb 20 '23 16:02 latin-1

and in your video, after seek, the cursor is at the center of the thumb. but it's not true for radix-ui

latin-1 avatar Feb 20 '23 16:02 latin-1

I'm sorry I'm not really following. Your initial diagram are showing one thing, and you're talking about something else now which isn't super clear. The position of the the native one seems to jump too depending on where the cursor is when starting the slide.

benoitgrelard avatar Feb 20 '23 16:02 benoitgrelard

and in your video, after seek, the cursor is at the center of the thumb. but it's not true for radix-ui

Yeah I see that, but I don't think one is necessarily better than the other, that's just a slight different in implementation. We interpolate the thumb width over the whole range, ours is entirely driven on the pointer position.

benoitgrelard avatar Feb 20 '23 16:02 benoitgrelard

Yeah I see that, but I don't think one is necessarily better than the other, that's just a slight different in implementation. We interpolate the thumb width over the whole range, ours is entirely driven on the pointer position.

I don't think so. No one other than radix-ui implements the slider in this way. You can try to increase the thumb size and see the weird behavior. While dragging, the thumb does not follows the cursor properly.

latin-1 avatar Feb 20 '23 16:02 latin-1

While dragging, the thumb does not follows the cursor properly.

I know it doesn't, as I said "we interpolate the thumb width over the whole range" and because we keep it in-bound it will look as if it's moving slightly under the pointer.

benoitgrelard avatar Feb 20 '23 16:02 benoitgrelard

In our project, we use a large thumb (~100px) and that is quite problematic.

latin-1 avatar Feb 20 '23 16:02 latin-1

and because we keep it in-bound

It's not related to whatever in-bound or out-bound. No one other than radix-ui implements the in-bound slider in this way.

latin-1 avatar Feb 20 '23 16:02 latin-1

I haven't seen many examples of libraries even having an in-bound layout like this. Do you know any so we can compare?

benoitgrelard avatar Feb 20 '23 16:02 benoitgrelard

I haven't seen many examples of libraries even having an in-bound layout like this.

I don't have either. But as far as I can tell, the iOS one, the Web's native one, the one in Adobe Spectrum, are behave in the same way, which is different from yours.

latin-1 avatar Feb 20 '23 16:02 latin-1

Here, to make sure we are talking about the same thing. This is what I mean by:

We interpolate the thumb width over the whole range

Current one (in bounds), with the compensation of the thumb width:

https://user-images.githubusercontent.com/1539897/220164099-15437cc2-5dce-4783-b776-c09781b131ca.mp4


If we didn't do compensate to keep in bounds (what most implementations do, thumb goes outside of bounding box):

https://user-images.githubusercontent.com/1539897/220164144-3d5366cb-7a23-473b-9547-393c0624850f.mp4


So you can see that what you are describing is directly related to the compensation to keep in-bounds.

benoitgrelard avatar Feb 20 '23 16:02 benoitgrelard

So you can see that what you are describing is directly related to the compensation to keep in-bounds.

I don't think so. For example, React Spectrum, which is in-bound, acts like the second one with a slightly longer track. That's the common pattern for in-bound sliders. The Web's native one does the same.

AFAIK, the only other implementation interpolate the position over whole range, is the WAI-ARIA's color viewer slider. But the multi-thumb slider example from WAI-ARIA just follows the native one. It's hard to say which is recommended from WAI-ARIA.

latin-1 avatar Feb 20 '23 17:02 latin-1

https://www.w3.org/WAI/ARIA/apg/patterns/slider/examples/slider-color-viewer/ https://www.w3.org/WAI/ARIA/apg/patterns/slider-multithumb/examples/slider-multithumb/

latin-1 avatar Feb 20 '23 17:02 latin-1

It's not in-bounds right? Or am I missing something?

https://user-images.githubusercontent.com/1539897/220168428-d27561de-d8c8-407c-a2a4-5f8d70986954.mp4

benoitgrelard avatar Feb 20 '23 17:02 benoitgrelard

@benoitgrelard https://react-spectrum.adobe.com/react-spectrum/Slider.html I mean this one.

latin-1 avatar Feb 20 '23 17:02 latin-1

Oh I see weird, their react-aria seems to be out-bounds, but the react-spectrum one which is built on top seems to be in-bounds as you point out. I can't figure out what they're doing to change that though looking at the source… 🤔

benoitgrelard avatar Feb 20 '23 17:02 benoitgrelard

I think I found it, a combination of margin compensation and width:

CleanShot 2023-02-20 at 17 36 47@2x

benoitgrelard avatar Feb 20 '23 17:02 benoitgrelard

Yep, if I deactivate our compensation, we can still achieve in-bounds using negative margins like this:

https://user-images.githubusercontent.com/1539897/220171672-6c44113a-22cf-4d6f-929a-c2396ce5c30f.mp4

Can you confirm this is what you'd be after?

benoitgrelard avatar Feb 20 '23 17:02 benoitgrelard

Yes that's what I mean. Currently, the only way to achieve this is, using a zero-width thumb container.

latin-1 avatar Feb 20 '23 17:02 latin-1

Ok, thanks for confirming. So one thing we could do potentially is offer a way to opt-out of the thumb compensation. Would that be good for you?

benoitgrelard avatar Feb 20 '23 18:02 benoitgrelard

Yes and no. I still think the current behavior is problematic though. Anyway, I'm OK with a new option. It's also useful for constructing an out-bounds slider.

latin-1 avatar Feb 20 '23 18:02 latin-1

What would this not enable?

benoitgrelard avatar Feb 20 '23 18:02 benoitgrelard

I'm fine with either as long as the behavior is well documented.

latin-1 avatar Feb 20 '23 19:02 latin-1

Hello, I'd like to chime in here, if I can.

Isn't current behaviour a bit problematic with a thicker range? As the range doesn't end in the middle of the thumb, if both have a border radius, we'll see a gap between them for value close to the minimum.

image

@latin-1's suggested solution would solve this problem.

image

I worked around it by turning the thumb into 0 width container with the :after, but that gets me an out of bounds slider, and I'd rather have an in-bounds one but without the gap.

Rephrasing for better understanding: I like the current Thumb position, but not the current Range width.

hasparus avatar Feb 24 '23 12:02 hasparus

Can you post a sandbox of your case @hasparus?

benoitgrelard avatar Feb 24 '23 12:02 benoitgrelard