Skip to content

Commit 538ebb2

Browse files
committed
- fix a little bit of reactivity for some props
- rangeGapMin/Max - was not properly updating if changed from outside - rangeFormatter - also - improve some comments - remove uneeded variable - update tests to cover some reactive prop changes
1 parent f0d144c commit 538ebb2

File tree

16 files changed

+1578
-624
lines changed

16 files changed

+1578
-624
lines changed

README.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ npm install svelte-range-slider-pips --save-dev # if you prefer npm
6969

7070
> **Note:** Please read the [upgrade guide](./docs/upgrade.md) for details, as there's some changes which might break your styling/ui.
7171
72-
### In a svelte 4/5 project
72+
### In a svelte 4 project
7373

7474
Assuming you have a Svelte app up and running;
7575

@@ -92,6 +92,15 @@ Assuming you have a Svelte app up and running;
9292

9393
---
9494

95+
### In a svelte 5 project
96+
97+
Svelte 5 works almost exactly the same as Svelte 4.
98+
99+
**Note:** Runes Mode -- specifically `$state()` -- has some issues when binding with an `<input />` element.
100+
My suggestion would be to use legacy mode if you can (not runes), or [read this issue for a workaround.](https://github.com/simeydotme/svelte-range-slider-pips/issues/174)
101+
102+
---
103+
95104
### In a svelte 3 project
96105

97106
_Version 3 of Range Slider Pips is not compatible with Svelte 3. [see below](#svelte-3)_

dist/range-slider-pips.js

Lines changed: 315 additions & 295 deletions
Large diffs are not rendered by default.

dist/range-slider-pips.mjs

Lines changed: 315 additions & 295 deletions
Large diffs are not rendered by default.

dist/svelte/components/RangeSlider.svelte

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,10 @@ const checkFormatters = () => {
144144
console.error("handleFormatter must be a function");
145145
handleFormatter = formatter;
146146
}
147+
if (rangeFormatter === void 0) {
148+
console.error("rangeFormatter must be a function, or null");
149+
rangeFormatter = null;
150+
}
147151
};
148152
checkMinMax();
149153
checkValueIsNumber();
@@ -155,13 +159,15 @@ $: values, updateValue();
155159
$: ariaLabels, checkAriaLabels();
156160
$: min, checkMinMax();
157161
$: max, checkMinMax();
162+
$: rangeGapMin, checkValuesAgainstRangeGaps();
163+
$: rangeGapMax, checkValuesAgainstRangeGaps();
158164
$: formatter, checkFormatters();
159165
$: handleFormatter, checkFormatters();
166+
$: rangeFormatter, checkFormatters();
160167
$: hasRange = range === true && values.length === 2 || (range === "min" || range === "max") && values.length === 1;
161168
$: {
162-
const trimmedAlignedValues = trimRange(
163-
values.map((v) => constrainAndAlignValue(v, min, max, step, precision, limits))
164-
);
169+
const trimmedValues = trimRange(values);
170+
const trimmedAlignedValues = trimmedValues.map((v) => constrainAndAlignValue(v, min, max, step, precision, limits));
165171
if (!(values.length === trimmedAlignedValues.length) || !values.every((element, index) => coerceFloat(element, precision) === trimmedAlignedValues[index])) {
166172
values = trimmedAlignedValues;
167173
}
@@ -171,12 +177,14 @@ $: {
171177
springValues
172178
);
173179
} else {
174-
requestAnimationFrame(() => {
175-
springPositions.set(
176-
values.map((v) => valueAsPercent(v, min, max)),
177-
{ hard: !spring }
178-
);
179-
});
180+
if (slider) {
181+
requestAnimationFrame(() => {
182+
springPositions.set(
183+
values.map((v) => valueAsPercent(v, min, max)),
184+
{ hard: !spring }
185+
);
186+
});
187+
}
180188
}
181189
valueLength = values.length;
182190
}
@@ -533,7 +541,6 @@ function ariaLabelFormatter(value2, index) {
533541
>
534542
{#each values as value, index}
535543
{@const zindex = `${focus && activeHandle === index ? 3 : ''}`}
536-
{@const translate = `calc((${sliderSize}px * ${$springPositions[index] / 100}))`}
537544
<span
538545
role="slider"
539546
class="rangeHandle"

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.1",
3+
"version": "4.0.2",
44
"description": "Multi-Thumb, Accessible, Beautiful Range Slider with Pips",
55
"repository": {
66
"type": "git",

src/lib/components/RangeSlider.svelte

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@
104104
}
105105
};
106106
107-
// check that "value" is a number, or set it as the average
107+
// check that "value" is a number, or set it as the first value in the values array
108108
const updateValue = () => {
109109
checkValueIsNumber();
110110
// sync value with values
@@ -184,6 +184,10 @@
184184
console.error('handleFormatter must be a function');
185185
handleFormatter = formatter;
186186
}
187+
if (rangeFormatter === undefined) {
188+
console.error('rangeFormatter must be a function, or null');
189+
rangeFormatter = null;
190+
}
187191
};
188192
189193
// fixup the value/values at render
@@ -199,17 +203,20 @@
199203
$: ariaLabels, checkAriaLabels();
200204
$: min, checkMinMax();
201205
$: max, checkMinMax();
206+
$: rangeGapMin, checkValuesAgainstRangeGaps();
207+
$: rangeGapMax, checkValuesAgainstRangeGaps();
202208
$: formatter, checkFormatters();
203209
$: handleFormatter, checkFormatters();
210+
$: rangeFormatter, checkFormatters();
204211
$: hasRange =
205212
(range === true && values.length === 2) || ((range === 'min' || range === 'max') && values.length === 1);
206213
207214
$: {
208-
// trim the range so it remains as a min/max (only 2 handles)
209-
// and also align the handles to the steps
210-
const trimmedAlignedValues = trimRange(
211-
values.map((v) => constrainAndAlignValue(v, min, max, step, precision, limits))
212-
);
215+
// if a range, then trim so it remains as a min/max (only 2 handles)
216+
const trimmedValues = trimRange(values);
217+
// and also align the handles to the steps/limits
218+
const trimmedAlignedValues = trimmedValues.map((v) => constrainAndAlignValue(v, min, max, step, precision, limits));
219+
// update the values if they needed to be fixed
213220
if (
214221
!(values.length === trimmedAlignedValues.length) ||
215222
!values.every((element, index) => coerceFloat(element, precision) === trimmedAlignedValues[index])
@@ -230,12 +237,14 @@
230237
} else {
231238
// update the value of the spring function for animated handles
232239
// whenever the values has updated
233-
requestAnimationFrame(() => {
234-
springPositions.set(
235-
values.map((v) => valueAsPercent(v, min, max)),
236-
{ hard: !spring }
237-
);
238-
});
240+
if (slider) {
241+
requestAnimationFrame(() => {
242+
springPositions.set(
243+
values.map((v) => valueAsPercent(v, min, max)),
244+
{ hard: !spring }
245+
);
246+
});
247+
}
239248
}
240249
// set the valueLength for the next check
241250
valueLength = values.length;
@@ -791,7 +800,6 @@
791800
>
792801
{#each values as value, index}
793802
{@const zindex = `${focus && activeHandle === index ? 3 : ''}`}
794-
{@const translate = `calc((${sliderSize}px * ${$springPositions[index] / 100}))`}
795803
<span
796804
role="slider"
797805
class="rangeHandle"

src/routes/test/nav/test-nav.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,10 @@
155155
"name": "binding",
156156
"path": "",
157157
"children": [
158+
{
159+
"name": "minmax",
160+
"path": "/test/range-slider/values/binding/minmax"
161+
},
158162
{
159163
"name": "multiple",
160164
"path": "/test/range-slider/values/binding/multiple"

src/routes/test/range-slider/minmax/+page.svelte

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
<script lang="ts">
22
import RangeSlider from '$lib/components/RangeSlider.svelte';
3+
4+
// Dynamic min/max state for testing
5+
let dynamicMin = 0;
6+
let dynamicMax = 100;
7+
let dynamicValues = [25, 75];
8+
9+
// Toggle state for min/max changes
10+
let toggleMin = 0;
11+
let toggleMax = 100;
12+
let toggleValues = [30, 70];
313
</script>
414

515
<div class="slider-list">
@@ -112,4 +122,82 @@
112122

113123
<h3>Infinite values</h3>
114124
<RangeSlider id="invalid-infinite" pips first="label" last="label" min={-Infinity} max={Infinity} />
125+
126+
<h2>Dynamic Min/Max Tests</h2>
127+
<h3>Dynamic min/max with bound inputs</h3>
128+
<RangeSlider
129+
id="dynamic-minmax"
130+
pips
131+
all="label"
132+
float
133+
bind:min={dynamicMin}
134+
bind:max={dynamicMax}
135+
bind:values={dynamicValues}
136+
/>
137+
<div class="controls">
138+
<label>
139+
Min: <input type="number" bind:value={dynamicMin} step="any" />
140+
</label>
141+
<label>
142+
Max: <input type="number" bind:value={dynamicMax} step="any" />
143+
</label>
144+
<p>Current values: {dynamicValues}</p>
145+
</div>
146+
147+
<h3>Dynamic min/max with toggle buttons</h3>
148+
<RangeSlider
149+
id="toggle-minmax"
150+
pips
151+
all="label"
152+
float
153+
bind:min={toggleMin}
154+
bind:max={toggleMax}
155+
bind:values={toggleValues}
156+
/>
157+
<div class="controls">
158+
<button id="btn_toggle_min_positive" on:click={() => (toggleMin = 20)}>Set Min to 20</button>
159+
<button id="btn_toggle_min_negative" on:click={() => (toggleMin = -50)}>Set Min to -50</button>
160+
<button id="btn_toggle_min_decimal" on:click={() => (toggleMin = 10.5)}>Set Min to 10.5</button>
161+
<button id="btn_toggle_min_zero" on:click={() => (toggleMin = 0)}>Set Min to 0</button>
162+
<button id="btn_toggle_max_small" on:click={() => (toggleMax = 50)}>Set Max to 50</button>
163+
<button id="btn_toggle_max_negative" on:click={() => (toggleMax = -20)}>Set Max to -20</button>
164+
<button id="btn_toggle_max_decimal" on:click={() => (toggleMax = 75.5)}>Set Max to 75.5</button>
165+
<button id="btn_toggle_max_large" on:click={() => (toggleMax = 1000)}>Set Max to 1000</button>
166+
<button
167+
id="btn_toggle_both_positive"
168+
on:click={() => {
169+
toggleMin = 20;
170+
toggleMax = 80;
171+
}}>Set Range 20-80</button
172+
>
173+
<button
174+
id="btn_toggle_both_negative"
175+
on:click={() => {
176+
toggleMin = -80;
177+
toggleMax = -20;
178+
}}>Set Range -80 to -20</button
179+
>
180+
<button
181+
id="btn_toggle_both_cross_zero"
182+
on:click={() => {
183+
toggleMin = -50;
184+
toggleMax = 50;
185+
}}>Set Range -50 to 50</button
186+
>
187+
<button
188+
id="btn_toggle_both_decimal"
189+
on:click={() => {
190+
toggleMin = 10.5;
191+
toggleMax = 90.5;
192+
}}>Set Range 10.5 to 90.5</button
193+
>
194+
<p>Current values: {toggleValues}</p>
195+
</div>
115196
</div>
197+
198+
<style>
199+
.controls {
200+
display: grid;
201+
grid-template-columns: repeat(4, max-content);
202+
}
203+
</style>
Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,57 @@
11
<script lang="ts">
22
import RangeSlider from '$components/RangeSlider.svelte';
3+
4+
// State for the toggle tests
5+
let isRange = false;
6+
let toggleValues = [15, 45, 75];
7+
8+
let isRangeMin = false;
9+
let toggleMinValues = [30, 60, 80];
10+
11+
let isRangeMax = false;
12+
let toggleMaxValues = [20, 50, 70];
313
</script>
414

515
<div class="slider-list">
616
<h5>Single handle range sliders <em>(should not be a range due to only one value)</em></h5>
7-
<RangeSlider id="single-handle-true" range={true} values={[40]} />
8-
<RangeSlider id="single-handle-true-value" range={true} value={40} />
17+
<RangeSlider id="single-handle-true" range values={[40]} />
18+
<RangeSlider id="single-handle-true-value" range value={40} />
919

1020
<h5>Double handle range sliders</h5>
11-
<RangeSlider id="double-handle-true" range={true} values={[25, 75]} />
12-
<RangeSlider id="double-handle-true-negative" range={true} values={[-75, 25]} min={-100} max={100} />
13-
<RangeSlider id="double-handle-true-constrained" range={true} values={[-130, 125]} min={-100} max={100} />
21+
<RangeSlider id="double-handle-true" range values={[25, 75]} />
22+
<RangeSlider id="double-handle-true-negative" range values={[-75, 25]} min={-100} max={100} />
23+
<RangeSlider id="double-handle-true-constrained" range values={[-130, 125]} min={-100} max={100} />
1424

1525
<h5>Triple handle range sliders <em>(should trim to double handle)</em></h5>
16-
<RangeSlider id="triple-handle-true" range={true} values={[15, 45, 75]} />
17-
<RangeSlider id="triple-handle-true-negative" range={true} values={[-75, 55, 15]} min={-50} />
26+
<RangeSlider id="triple-handle-true" range values={[15, 45, 75]} />
27+
<RangeSlider id="triple-handle-true-negative" range values={[-75, 55, 15]} min={-50} />
28+
29+
<hr />
30+
31+
<h5>Toggle range mode test</h5>
32+
<RangeSlider id="toggle-range-test" range={isRange} bind:values={toggleValues} />
33+
<div class="controls">
34+
<button id="btn_toggle_range" on:click={() => (isRange = !isRange)}>
35+
Toggle Range Mode ({isRange ? 'true' : 'false'})
36+
</button>
37+
<p>Current values: {toggleValues}</p>
38+
</div>
39+
40+
<h5>Toggle range="min" mode test</h5>
41+
<RangeSlider id="toggle-range-min-test" range={isRangeMin ? 'min' : false} bind:values={toggleMinValues} />
42+
<div class="controls">
43+
<button id="btn_toggle_range_min" on:click={() => (isRangeMin = !isRangeMin)}>
44+
Toggle Range Min Mode ({isRangeMin ? 'true' : 'false'})
45+
</button>
46+
<p>Current values: {toggleMinValues}</p>
47+
</div>
48+
49+
<h5>Toggle range="max" mode test</h5>
50+
<RangeSlider id="toggle-range-max-test" range={isRangeMax ? 'max' : false} bind:values={toggleMaxValues} />
51+
<div class="controls">
52+
<button id="btn_toggle_range_max" on:click={() => (isRangeMax = !isRangeMax)}>
53+
Toggle Range Max Mode ({isRangeMax ? 'true' : 'false'})
54+
</button>
55+
<p>Current values: {toggleMaxValues}</p>
56+
</div>
1857
</div>

src/routes/test/range-slider/range/gaps/+page.svelte

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,17 @@
106106

107107
<h2>Dynamic Gap Tests</h2>
108108
<h3>Dynamic gaps - gap constraints can be updated at runtime</h3>
109-
<RangeSlider id="dynamic-gaps" range values={dynamicValues} rangeGapMin={dynamicGapMin} rangeGapMax={dynamicGapMax} />
109+
<RangeSlider
110+
id="dynamic-gaps"
111+
range
112+
bind:values={dynamicValues}
113+
bind:rangeGapMin={dynamicGapMin}
114+
bind:rangeGapMax={dynamicGapMax}
115+
on:change={handleChange}
116+
on:start={handleStart}
117+
on:stop={handleStop}
118+
/>
119+
{dynamicValues} / min: {dynamicGapMin} / max: {dynamicGapMax}
110120
<div class="controls">
111121
<button id="btn_increase_min" on:click={() => (dynamicGapMin += 5)}>Increase Min Gap</button>
112122
<button id="btn_decrease_min" on:click={() => (dynamicGapMin -= 5)}>Decrease Min Gap</button>

0 commit comments

Comments
 (0)