Skip to content

Commit 28796be

Browse files
committed
fix svelte5 $state() issue, and style= issue
- modify the way the `$: {}` block runs so it triggers svelte5 reactivity - combine all the `style:--var` attributes into `style=...` - I noticed some perf/browser issues with certain style attributes updating when supplied as a prop - improve the `constrainAndAlignValue()` function in utils
1 parent e6fc6c4 commit 28796be

File tree

7 files changed

+71
-87
lines changed

7 files changed

+71
-87
lines changed

dist/range-slider-pips.js

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* svelte-range-slider-pips ~ 4.0.3
2+
* svelte-range-slider-pips ~ 4.0.4
33
* Multi-Thumb, Accessible, Beautiful Range Slider with Pips
44
* Project home: https://simeydotme.github.io/svelte-range-slider-pips/
55
* © 2025 Simon Goellner <[email protected]> ~ MPL-2.0 License
@@ -1482,16 +1482,6 @@
14821482
if (value <= (limits?.[0] ?? min) || value >= (limits?.[1] ?? max)) {
14831483
return (value = clampValue(value, limits?.[0] ?? min, limits?.[1] ?? max));
14841484
}
1485-
// escape early if the value is at/beyond the known limits
1486-
// if (limits?.[0] && value <= limits[0]) {
1487-
// return limits?.[0];
1488-
// } else if (limits?.[1] && value >= limits[1]) {
1489-
// return limits?.[1];
1490-
// } else if (max && value >= max) {
1491-
// return max;
1492-
// } else if (min && value <= min) {
1493-
// return min;
1494-
// }
14951485
// find the middle-point between steps
14961486
// and see if the value is closer to the
14971487
// next step, or previous step

dist/range-slider-pips.mjs

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* svelte-range-slider-pips ~ 4.0.3
2+
* svelte-range-slider-pips ~ 4.0.4
33
* Multi-Thumb, Accessible, Beautiful Range Slider with Pips
44
* Project home: https://simeydotme.github.io/svelte-range-slider-pips/
55
* © 2025 Simon Goellner <[email protected]> ~ MPL-2.0 License
@@ -1476,16 +1476,6 @@ const constrainAndAlignValue = function (value, min, max, step, precision = 2, l
14761476
if (value <= (limits?.[0] ?? min) || value >= (limits?.[1] ?? max)) {
14771477
return (value = clampValue(value, limits?.[0] ?? min, limits?.[1] ?? max));
14781478
}
1479-
// escape early if the value is at/beyond the known limits
1480-
// if (limits?.[0] && value <= limits[0]) {
1481-
// return limits?.[0];
1482-
// } else if (limits?.[1] && value >= limits[1]) {
1483-
// return limits?.[1];
1484-
// } else if (max && value >= max) {
1485-
// return max;
1486-
// } else if (min && value <= min) {
1487-
// return min;
1488-
// }
14891479
// find the middle-point between steps
14901480
// and see if the value is closer to the
14911481
// next step, or previous step

dist/svelte/utils.js

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -77,16 +77,6 @@ export const constrainAndAlignValue = function (value, min, max, step, precision
7777
if (value <= (limits?.[0] ?? min) || value >= (limits?.[1] ?? max)) {
7878
return (value = clampValue(value, limits?.[0] ?? min, limits?.[1] ?? max));
7979
}
80-
// escape early if the value is at/beyond the known limits
81-
// if (limits?.[0] && value <= limits[0]) {
82-
// return limits?.[0];
83-
// } else if (limits?.[1] && value >= limits[1]) {
84-
// return limits?.[1];
85-
// } else if (max && value >= max) {
86-
// return max;
87-
// } else if (min && value <= min) {
88-
// return min;
89-
// }
9080
// find the middle-point between steps
9181
// and see if the value is closer to the
9282
// next step, or previous step

package-lock.json

Lines changed: 6 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "svelte-range-slider-pips",
3-
"version": "4.0.3",
3+
"version": "4.0.4",
44
"description": "Multi-Thumb, Accessible, Beautiful Range Slider with Pips",
55
"repository": {
66
"type": "git",

src/lib/components/RangeSlider.svelte

Lines changed: 58 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@
132132
};
133133
134134
const checkValueIsNumber = () => {
135-
if (typeof value !== 'number') {
135+
if (!isFiniteNumber(value)) {
136136
value = (max + min) / 2;
137137
console.error("'value' prop should be a Number");
138138
}
@@ -142,6 +142,9 @@
142142
if (!Array.isArray(values)) {
143143
values = [value];
144144
console.error("'values' prop should be an Array");
145+
} else if (values.some((v) => !isFiniteNumber(v))) {
146+
values = values.map((v) => (isFiniteNumber(v) ? v : (max + min) / 2));
147+
console.error("'values' prop should be an Array of Numbers");
145148
}
146149
};
147150
@@ -211,44 +214,61 @@
211214
$: hasRange =
212215
(range === true && values.length === 2) || ((range === 'min' || range === 'max') && values.length === 1);
213216
214-
$: {
217+
$: ((uValues, uValue) => {
215218
// if a range, then trim so it remains as a min/max (only 2 handles)
216-
const trimmedValues = trimRange(values, range);
219+
const trimmedValues = trimRange(uValues, range);
217220
// and also align the handles to the steps/limits
218221
const trimmedAlignedValues = trimmedValues.map((v) => constrainAndAlignValue(v, min, max, step, precision, limits));
219222
// update the values if they needed to be fixed
220223
if (
221-
!(values.length === trimmedAlignedValues.length) ||
222-
!values.every((item, i) => coerceFloat(item, precision) === trimmedAlignedValues[i])
224+
!(uValues.length === trimmedAlignedValues.length) ||
225+
!uValues.every((item, i) => coerceFloat(item, precision) === trimmedAlignedValues[i])
223226
) {
224-
values = trimmedAlignedValues;
227+
uValues = trimmedAlignedValues;
225228
}
226229
227-
// check if the valueLength (length of values[]) has changed,
228-
// because if so we need to re-seed the spring function with the
229-
// new values array.
230-
if (valueLength !== values.length) {
231-
// set the initial spring values when the slider initialises,
232-
// or when values array length has changed
233-
springPositions = springStore(
234-
values.map((v) => valueAsPercent(v, min, max)),
235-
springValues
236-
);
237-
} else {
238-
// update the value of the spring function for animated handles
239-
// whenever the values has updated
240-
if (slider) {
241-
requestAnimationFrame(() => {
242-
springPositions.set(
243-
values.map((v) => valueAsPercent(v, min, max)),
244-
{ hard: !spring }
245-
);
246-
});
247-
}
230+
// When the values array length changes, we must recreate the spring function
231+
// because the spring store is bound to a specific array length. Attempting to
232+
// update a spring with a different array size would cause errors or unexpected
233+
// behavior. For existing arrays, we update the spring values for smooth animations.
234+
if (valueLength !== uValues.length) {
235+
// create spring if there's no spring yet, or length changed
236+
createSpring(uValues);
237+
} else if (slider) {
238+
// only update the spring values if the slider is mounted
239+
updateSpring(uValues);
248240
}
241+
242+
// update the external values
243+
values = uValues;
244+
249245
// set the valueLength for the next check
250-
valueLength = values.length;
251-
}
246+
valueLength = uValues.length;
247+
})(values, value);
248+
249+
/**
250+
* create a spring function to animate the handles
251+
* @param values the values to animate
252+
*/
253+
const createSpring = (values: number[]) => {
254+
springPositions = springStore(
255+
values.map((v) => valueAsPercent(v, min, max)),
256+
springValues
257+
);
258+
};
259+
260+
/**
261+
* update the spring function to animate the handles
262+
* @param values the values to animate
263+
*/
264+
const updateSpring = (values: number[]) => {
265+
requestAnimationFrame(() => {
266+
springPositions.set(
267+
values.map((v) => valueAsPercent(v, min, max)),
268+
{ hard: !spring }
269+
);
270+
});
271+
};
252272
253273
/**
254274
* the orientation of the handles/pips based on the
@@ -792,15 +812,15 @@
792812
class:rsFocus={focus}
793813
class:rsPips={pips}
794814
class:rsPipLabels={all === 'label' || first === 'label' || last === 'label' || rest === 'label'}
795-
style:--slider-length={sliderSize}
796-
{style}
815+
style={`--slider-length: ${sliderSize};${style ?? ''}`}
797816
on:mousedown={sliderInteractStart}
798817
on:mouseup={sliderInteractEnd}
799818
on:touchstart|preventDefault={sliderInteractStart}
800819
on:touchend|preventDefault={sliderInteractEnd}
801820
>
802821
{#each values as value, index}
803-
{@const zindex = `${focus && activeHandle === index ? 3 : ''}`}
822+
{@const zindex = focus && activeHandle === index ? `z-index: 3; ` : ``}
823+
{@const mountOpacity = isMounted ? `` : `opacity: 0; `}
804824
<span
805825
role="slider"
806826
class="rangeHandle"
@@ -810,8 +830,7 @@
810830
on:blur={sliderBlurHandle}
811831
on:focus={sliderFocusHandle}
812832
on:keydown={sliderKeydown}
813-
style:--handle-pos={$springPositions[index]}
814-
style="z-index: {zindex}; {isMounted ? '' : 'opacity: 0;'}"
833+
style={`--handle-pos: ${$springPositions[index]};${zindex}${mountOpacity}`}
815834
aria-label={ariaLabels[index]}
816835
aria-valuemin={range === true && index === 1 ? values[0] : min}
817836
aria-valuemax={range === true && index === 0 ? values[1] : max}
@@ -841,13 +860,14 @@
841860
></span>
842861
{/if}
843862
{#if hasRange}
863+
{@const rangeStart = rangeStartPercent($springPositions)}
864+
{@const rangeEnd = rangeEndPercent($springPositions)}
865+
{@const rangeSize = rangeEnd - rangeStart}
866+
{@const mountOpacity = isMounted ? `` : `opacity: 0; `}
844867
<span
845868
class="rangeBar"
846869
class:rsPress={rangePressed}
847-
style:--range-start={rangeStartPercent($springPositions)}
848-
style:--range-end={rangeEndPercent($springPositions)}
849-
style:--range-size={rangeEndPercent($springPositions) - rangeStartPercent($springPositions)}
850-
style={isMounted ? '' : 'opacity: 0;'}
870+
style={`--range-start:${rangeStart};--range-end:${rangeEnd};--range-size:${rangeSize};${mountOpacity};`}
851871
>
852872
{#if rangeFloat}
853873
<span class="rangeFloat">

src/lib/utils.ts

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -82,20 +82,13 @@ export const constrainAndAlignValue = function (
8282
precision: number = 2,
8383
limits: [number, number] | null = null
8484
) {
85+
value = isFiniteNumber(value) ? value : limits?.[0] ?? min;
86+
8587
// if limits are provided, clamp the value between the limits
8688
// if no limits are provided, clamp the value between the min and max
8789
// before we start aligning the value
88-
value = clampValue(value, limits?.[0] ?? min, limits?.[1] ?? max);
89-
90-
// escape early if the value is at/beyond the known limits
91-
if (limits?.[0] && value <= limits[0]) {
92-
return limits?.[0];
93-
} else if (limits?.[1] && value >= limits[1]) {
94-
return limits?.[1];
95-
} else if (max && value >= max) {
96-
return max;
97-
} else if (min && value <= min) {
98-
return min;
90+
if (value <= (limits?.[0] ?? min) || value >= (limits?.[1] ?? max)) {
91+
return (value = clampValue(value, limits?.[0] ?? min, limits?.[1] ?? max));
9992
}
10093

10194
// find the middle-point between steps

0 commit comments

Comments
 (0)