primitives icon indicating copy to clipboard operation
primitives copied to clipboard

[Slider] Add marks on a slider at specific points and allow only those to be selected

Open raviteja83 opened this issue 2 years ago • 45 comments

Feature request

Provide a way to add points on the slider.

Overview

Examples in other libraries

https://mui.com/components/slider/#discrete-sliders

Who does this impact? Who is this for?

Additional context

raviteja83 avatar Feb 24 '22 11:02 raviteja83

Hey @raviteja83,

I believe this was a feature on our list for Slider when we were building it, but we dropped it so we could release a simple version first. I am sure we will get back to that at some point though.

✌️

benoitgrelard avatar Feb 25 '22 09:02 benoitgrelard

We probably won't do this anytime soon and would rather focus on producing more primitives. This is also probably possible in user-land.

benoitgrelard avatar Apr 05 '22 16:04 benoitgrelard

@benoitgrelard just a heads up that i'm not sure if this is that easy user-land because there are offset calculations on the thumb to stop it from overflowing the edges of the range and consumers don't have access to that offset to align the points.

perhaps css grid could help tho? maybe an example in the docs would help if so.

jjenzz avatar Apr 11 '22 10:04 jjenzz

That's a great point @jjenzz, this might need to be offered as part of the component for that reason then.

benoitgrelard avatar Apr 11 '22 14:04 benoitgrelard

I hope marks will be added soon, I had a hard time making them myself

its-monotype avatar Dec 03 '22 16:12 its-monotype

But no, I was joking, I didn’t manage to do it myself until the end, only if the number of marks is equal to the maximum value

its-monotype avatar Dec 03 '22 17:12 its-monotype

Any updates on that? 👀

its-monotype avatar Jan 27 '23 18:01 its-monotype

In case this is slated for (Far in the Future) -- a blog post/stackoverflow post about how this was made would definitely be enough for most people (in React/NextJS hopefully)!

Here's a working example that the Radix team might have access to? image https://workos.com/pricing (Workos.com is the maintainer of RadixUI in case anybody is wondering.)

One very small point of feedback on this implementation ^ there is a tiny bit of jitter on cursor turning from arrow/hand when using the slider -- perhaps, adding hover:cursor-pointer in a more parent element will address issue.

ChungMasterFlex avatar Feb 10 '23 14:02 ChungMasterFlex

Hey @ChungMasterFlex, I think we're clear on what the issue is about. Regarding the example above from the WorkOS pricing page, that doesn't use Radix and is actually all done in Webflow for the marketing pages.

benoitgrelard avatar Feb 10 '23 16:02 benoitgrelard

For those looking for a temporary workaround, you can just mimic Radix' calculation to get the same positions as those the thumb can be at.

// Positioned mark without anything displayed, you can add classes, children etc.
// I'm using Tailwind here, but replace with X
// maxIndex should be 9 if you have 10 steps
// stepIndex controls the position you want to display the mark at.
 <div 
  className={clsx("absolute", orientation === "horizontal" ? "-translate-x-1/2" : "-translate-y-1/2")}
  style={{ [orientation === "horizontal" ? "left" : "top"]: calcStepMarkOffset(stepIndex, maxIndex) }} 
/>
// Then you need these 4 functions I copied almost 1-1 from Radix

// Assuming you know thumb size at coding-time or have a way to measure it.
const THUMB_SIZE = 32;

function calcStepMarkOffset(index: number, maxIndex: number) {
  const percent = convertValueToPercentage(index, 0, maxIndex);
  const thumbInBoundsOffset = getThumbInBoundsOffset(THUMB_SIZE, percent, 1);
  return `calc(${percent}% + ${thumbInBoundsOffset}px)`;
}

function convertValueToPercentage(value: number, min: number, max: number) {
  const maxSteps = max - min;
  const percentPerStep = 100 / maxSteps;
  const percentage = percentPerStep * (value - min);
  return clamp(percentage, { max: 100, min: 0 });
}

function getThumbInBoundsOffset(width: number, left: number, direction: number) {
  const halfWidth = width / 2;
  const halfPercent = 50;
  const offset = linearScale([0, halfPercent], [0, halfWidth]);
  return (halfWidth - offset(left) * direction) * direction;
}

function linearScale(input: readonly [number, number], output: readonly [number, number]) {
  return (value: number) => {
    if (input[0] === input[1] || output[0] === output[1]) return output[0];
    const ratio = (output[1] - output[0]) / (input[1] - input[0]);
    return output[0] + ratio * (value - input[0]);
  };
}

A real implementation might then look similar to this workaround:

// orientation, max etc. need to be passed in the workaround, 
// but a real implementation would inherit them from Slider.Root
<Slider.Mark step={step}>
  {someText}
</Slider.Mark>

nickluger avatar Jun 16 '23 16:06 nickluger

Need one these man

diehardsapp avatar Oct 17 '23 20:10 diehardsapp

Here's also another solution that I came up with: https://codesandbox.io/p/sandbox/divine-http-93zqyd

My use case was that I needed those specific points be equal to specific values (e.g. 18, 25, 35, 45, 55, 65) and wanted user to able to smoothly drag the thumb while only allowing those specific values to be selected.

I mapped those values to integers 0... and set up a onValueChange handler to update state whenever value is updated by rounding the current value to nearest integer:

onValueChange={(newValues) => {
     const roundedValue = Math.round(newValues[0]);
     setMappedValues([valueMapping[roundedValue]]);
}}

and update state (mappedValue):

const [mappedValues, setMappedValues] = useState(() =>[
    valueMapping[Math.round(props?.defaultValue[0] ?? 0)]
]);

(Same works for two thumbs, which was my actual use case)

frixaco avatar Nov 21 '23 08:11 frixaco

considering using this slider but missing this feature. is it possible in 2023?

megetron avatar Nov 27 '23 15:11 megetron

2024 need this feature

liho00 avatar Jan 13 '24 16:01 liho00

Could definitely use this - I think it's universal enough need (that benefits from access to internals) to be considered part of the primitive itself rather than a user-land implementation

faizan-ali avatar Feb 03 '24 23:02 faizan-ali

Up

Patolord avatar Feb 13 '24 17:02 Patolord

up

lui7henrique avatar Feb 17 '24 17:02 lui7henrique

Up

lovrozagar avatar Feb 18 '24 10:02 lovrozagar

Up

ranierimarques avatar Feb 18 '24 19:02 ranierimarques

Up

CreativeCreature7 avatar Feb 20 '24 13:02 CreativeCreature7

Up

lovrozagar avatar Feb 20 '24 14:02 lovrozagar

Up

darrel1925 avatar Feb 25 '24 23:02 darrel1925

Up

mnbeh avatar Mar 02 '24 13:03 mnbeh

UPPPP !

Zakisb avatar Mar 04 '24 09:03 Zakisb

UUUPPPP!! 💯

Vladi-F avatar Mar 06 '24 09:03 Vladi-F

Up

ZackLyon avatar Mar 06 '24 23:03 ZackLyon

Up

lui7henrique avatar Mar 06 '24 23:03 lui7henrique

Up

prenansb avatar Mar 06 '24 23:03 prenansb

Up 🔝

abe1272001 avatar Mar 11 '24 06:03 abe1272001

Up 0_o

maotora avatar Mar 15 '24 13:03 maotora