primitives
primitives copied to clipboard
[Slider] Add marks on a slider at specific points and allow only those to be selected
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
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.
✌️
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 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.
That's a great point @jjenzz, this might need to be offered as part of the component for that reason then.
I hope marks will be added soon, I had a hard time making them myself
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
Any updates on that? 👀
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?
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.
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.
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>
Need one these man
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...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)
considering using this slider but missing this feature. is it possible in 2023?
2024 need this feature
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
Up
up
Up
Up
Up
Up
Up
Up
UPPPP !
UUUPPPP!! 💯
Up
Up
Up
Up 🔝
Up 0_o