Skip to content

Commit 588c0a9

Browse files
aruzarhuntabyte
authored
fix(Slider): update tick position calculation for consistent scaling (#1375)
Co-authored-by: ar <ar@ar.org> Co-authored-by: Hunter Johnston <johnstonhuntera@gmail.com> Co-authored-by: Hunter Johnston <64506580+huntabyte@users.noreply.github.com>
1 parent 1d5bc0f commit 588c0a9

File tree

5 files changed

+60
-10
lines changed

5 files changed

+60
-10
lines changed

.changeset/fresh-gifts-bathe.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"bits-ui": patch
3+
---
4+
5+
fix(Slider): update tick position calculation for consistent scaling

docs/content/components/slider.md

+11-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ description: Allows users to select a value from a continuous range by sliding a
44
---
55

66
<script>
7-
import { APISection, ComponentPreviewV2, SliderDemo, SliderDemoMultiple, Callout } from '$lib/components/index.js'
7+
import { APISection, ComponentPreviewV2, SliderDemo, SliderDemoMultiple, SliderDemoTicks, Callout } from '$lib/components/index.js'
88
let { schemas } = $props()
99
</script>
1010

@@ -151,11 +151,11 @@ If the `value` prop has more than one value, the slider will render multiple thu
151151
{#snippet children({ ticks, thumbs })}
152152
<Slider.Range />
153153
154-
{#each thumbs as index}
154+
{#each thumbs as index (index)}
155155
<Slider.Thumb {index} />
156156
{/each}
157157
158-
{#each ticks as index}
158+
{#each ticks as index (index)}
159159
<Slider.Tick {index} />
160160
{/each}
161161
{/snippet}
@@ -164,6 +164,14 @@ If the `value` prop has more than one value, the slider will render multiple thu
164164

165165
To determine the number of ticks that will be rendered, you can simply divide the `max` value by the `step` value.
166166

167+
<ComponentPreviewV2 name="slider-demo-ticks" componentName="Slider">
168+
169+
{#snippet preview()}
170+
<SliderDemoTicks />
171+
{/snippet}
172+
173+
</ComponentPreviewV2>
174+
167175
## Single Type
168176

169177
Set the `type` prop to `"single"` to allow only one accordion item to be open at a time.

docs/src/lib/components/demos/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ export { default as ScrollAreaDemoCustom } from "./scroll-area-demo-custom.svelt
5757
export { default as SeparatorDemo } from "./separator-demo.svelte";
5858
export { default as SliderDemo } from "./slider-demo.svelte";
5959
export { default as SliderDemoMultiple } from "./slider-demo-multiple.svelte";
60+
export { default as SliderDemoTicks } from "./slider-demo-ticks.svelte";
6061
export { default as SwitchDemo } from "./switch-demo.svelte";
6162
export { default as SwitchDemoCustom } from "./switch-demo-custom.svelte";
6263
export { default as TabsDemo } from "./tabs-demo.svelte";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<script lang="ts">
2+
import { Slider } from "bits-ui";
3+
4+
let value = $state([5, 7]);
5+
</script>
6+
7+
<div class="w-full md:max-w-[280px]">
8+
<Slider.Root
9+
step={1}
10+
min={0}
11+
max={10}
12+
type="multiple"
13+
bind:value
14+
class="relative flex w-full touch-none select-none items-center"
15+
>
16+
{#snippet children({ ticks, thumbs })}
17+
<span
18+
class="bg-dark-10 relative h-2 w-full grow cursor-pointer overflow-hidden rounded-full"
19+
>
20+
<Slider.Range class="bg-foreground absolute h-full" />
21+
</span>
22+
{#each thumbs as thumb}
23+
<Slider.Thumb
24+
index={thumb}
25+
class="border-border-input bg-background hover:border-dark-40 focus-visible:ring-foreground dark:bg-foreground dark:shadow-card focus-visible:outline-hidden z-5 block size-[25px] cursor-pointer rounded-full border shadow-sm transition-colors focus-visible:ring-2 focus-visible:ring-offset-2 active:scale-[0.98] disabled:pointer-events-none disabled:opacity-50"
26+
/>
27+
{/each}
28+
{#each ticks as tick}
29+
<Slider.Tick
30+
index={tick}
31+
class="dark:bg-background/20 bg-background z-1 h-2 w-[1px]"
32+
/>
33+
{/each}
34+
{/snippet}
35+
</Slider.Root>
36+
</div>

packages/bits-ui/src/lib/bits/slider/slider.svelte.ts

+7-7
Original file line numberDiff line numberDiff line change
@@ -305,12 +305,9 @@ class SliderSingleRootState extends SliderBaseRootState {
305305
const currValue = this.opts.value.current;
306306

307307
return Array.from({ length: count }, (_, i) => {
308-
const tickPosition = i * (step / difference) * 100;
308+
const tickPosition = i * step;
309309

310-
const scale = linearScale(
311-
[this.opts.min.current, this.opts.max.current],
312-
this.getThumbScale()
313-
);
310+
const scale = linearScale([0, (count - 1) * step], this.getThumbScale());
314311

315312
const isFirst = i === 0;
316313
const isLast = i === count - 1;
@@ -623,12 +620,15 @@ class SliderMultiRootState extends SliderBaseRootState {
623620
const currValue = this.opts.value.current;
624621

625622
return Array.from({ length: count }, (_, i) => {
626-
const tickPosition = i * (step / difference) * 100;
623+
const tickPosition = i * step;
624+
625+
const scale = linearScale([0, (count - 1) * step], this.getThumbScale());
627626

628627
const isFirst = i === 0;
629628
const isLast = i === count - 1;
630629
const offsetPercentage = isFirst ? 0 : isLast ? -100 : -50;
631-
const style = getTickStyles(this.direction, tickPosition, offsetPercentage);
630+
631+
const style = getTickStyles(this.direction, scale(tickPosition), offsetPercentage);
632632
const tickValue = min + i * step;
633633
const bounded =
634634
currValue.length === 1

0 commit comments

Comments
 (0)