Skip to content

Commit a3040c9

Browse files
committed
make some functions reactive, improve styling
- some functions were not updating the min/max/val when values were bound reactively - tighten up some margins for sliders with pips
1 parent 5bcc49e commit a3040c9

File tree

3 files changed

+109
-88
lines changed

3 files changed

+109
-88
lines changed

src/RangePips.svelte

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,21 +25,21 @@
2525
2626
$: pipCount = parseInt((max - min) / (step * pipstep), 10);
2727
28-
$: pipVal = function(i) {
29-
return min + i * step * pipstep;
28+
$: pipVal = function(val) {
29+
return min + val * step * pipstep;
3030
};
3131
32-
$: isSelected = function(i) {
33-
return values.some(v => v === i);
32+
$: isSelected = function(val) {
33+
return values.some(v => v === val);
3434
};
3535
36-
$: inRange = function(i) {
36+
$: inRange = function(val) {
3737
if (range === "min") {
38-
return values[0] < i;
38+
return values[0] < val;
3939
} else if (range === "max") {
40-
return values[0] > i;
40+
return values[0] > val;
4141
} else if (range) {
42-
return values[0] < i && values[1] > i;
42+
return values[0] < val && values[1] > val;
4343
}
4444
};
4545
</script>
@@ -54,16 +54,15 @@
5454
--pip-in-range-text: var(--range-pip-in-range-text, var(--pip-active-text));
5555
}
5656
:global(.rangeSlider__pips) {
57-
height: 1em;
5857
position: absolute;
58+
height: 1em;
5959
left: 0;
6060
right: 0;
6161
bottom: -1em;
6262
}
6363
:global(.rangeSlider__pips.vertical) {
6464
height: auto;
6565
width: 1em;
66-
position: absolute;
6766
left: 100%;
6867
right: auto;
6968
top: 0;

src/RangeSlider.svelte

Lines changed: 93 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
export let id;
2323
export let prefix = "";
2424
export let suffix = "";
25-
export let formatter = v => v;
25+
export let formatter = (v) => v;
2626
export let handleFormatter = formatter;
2727
2828
// stylistic props
@@ -40,17 +40,79 @@
4040
4141
// save spring-tweened copies of the values for use
4242
// when changing values and animating the handle/range nicely
43-
let springPositions = spring(values.map(v => 50), springValues);
43+
let springPositions = spring(
44+
values.map((v) => 50),
45+
springValues
46+
);
4447
4548
// watch the values array, and trim / clamp the values to the steps
4649
// and boundaries set up in the slider on change
47-
$: values = trimRange(values).map(v => alignValueToStep(v));
50+
$: values = trimRange(values).map((v) => alignValueToStep(v));
4851
4952
// update the spring function so that movement can happen in the UI
5053
$: {
51-
springPositions.set(values.map(v => percentOf(v)));
54+
springPositions.set(values.map((v) => percentOf(v)));
5255
}
5356
57+
/**
58+
* take in a value, and then calculate that value's percentage
59+
* of the overall range (min-max);
60+
* @param {number} val the value we're getting percent for
61+
* @return {number} the percentage value
62+
**/
63+
$: percentOf = function (val) {
64+
let perc = ((val - min) / (max - min)) * 100;
65+
if (perc >= 100) {
66+
return 100;
67+
} else if (perc <= 0) {
68+
return 0;
69+
} else {
70+
return parseFloat(perc.toFixed(precision));
71+
}
72+
};
73+
74+
/**
75+
* clamp a value from the range so that it always
76+
* falls within the min/max values
77+
* @param {number} val the value to clamp
78+
* @return {number} the value after it's been clamped
79+
**/
80+
$: clampValue = function (val) {
81+
// return the min/max if outside of that range
82+
return val <= min ? min : val >= max ? max : val;
83+
};
84+
85+
/**
86+
* align the value with the steps so that it
87+
* always sits on the closest (above/below) step
88+
* @param {number} val the value to align
89+
* @return {number} the value after it's been aligned
90+
**/
91+
$: alignValueToStep = function (val) {
92+
// sanity check for performance
93+
if (val <= min) {
94+
return min;
95+
} else if (val >= max) {
96+
return max;
97+
}
98+
99+
// find the middle-point between steps
100+
// and see if the value is closer to the
101+
// next step, or previous step
102+
let remainder = (val - min) % step;
103+
let aligned = val - remainder;
104+
if (Math.abs(remainder) * 2 >= step) {
105+
aligned += remainder > 0 ? step : -step;
106+
}
107+
// make sure the value is within acceptable limits
108+
aligned = clampValue(aligned);
109+
// make sure the returned value is set to the precision desired
110+
// this is also because javascript often returns weird floats
111+
// when dealing with odd numbers and percentages
112+
113+
return parseFloat(aligned.toFixed(precision));
114+
};
115+
54116
/**
55117
* helper func to get the index of an element in it's DOM container
56118
* @param {object} el dom object reference we want the index of
@@ -96,7 +158,7 @@
96158
function targetIsHandle(el) {
97159
const handles = slider.querySelectorAll(".handle");
98160
const isHandle = Array.prototype.includes.call(handles, el);
99-
const isChild = Array.prototype.some.call(handles, e => e.contains(el));
161+
const isChild = Array.prototype.some.call(handles, (e) => e.contains(el));
100162
return isHandle || isChild;
101163
}
102164
@@ -116,65 +178,6 @@
116178
}
117179
}
118180
119-
/**
120-
* clamp a value from the range so that it always
121-
* falls within the min/max values
122-
* @param {number} val the value to clamp
123-
* @return {number} the value after it's been clamped
124-
**/
125-
function clampValue(val) {
126-
// return the min/max if outside of that range
127-
return val <= min ? min : val >= max ? max : val;
128-
}
129-
130-
/**
131-
* align the value with the steps so that it
132-
* always sits on the closest (above/below) step
133-
* @param {number} val the value to align
134-
* @return {number} the value after it's been aligned
135-
**/
136-
function alignValueToStep(val) {
137-
// sanity check for performance
138-
if (val <= min) {
139-
return min;
140-
} else if (val >= max) {
141-
return max;
142-
}
143-
144-
// find the middle-point between steps
145-
// and see if the value is closer to the
146-
// next step, or previous step
147-
let remainder = (val - min) % step;
148-
let aligned = val - remainder;
149-
if (Math.abs(remainder) * 2 >= step) {
150-
aligned += remainder > 0 ? step : -step;
151-
}
152-
// make sure the value is within acceptable limits
153-
aligned = clampValue(aligned);
154-
// make sure the returned value is set to the precision desired
155-
// this is also because javascript often returns weird floats
156-
// when dealing with odd numbers and percentages
157-
158-
return parseFloat(aligned.toFixed(precision));
159-
}
160-
161-
/**
162-
* take in a value, and then calculate that value's percentage
163-
* of the overall range (min-max);
164-
* @param {number} val the value we're getting percent for
165-
* @return {number} the percentage value
166-
**/
167-
function percentOf(val) {
168-
let perc = ((val - min) / (max - min)) * 100;
169-
if (perc >= 100) {
170-
return 100;
171-
} else if (perc <= 0) {
172-
return 0;
173-
} else {
174-
return parseFloat(perc.toFixed(precision));
175-
}
176-
}
177-
178181
/**
179182
* helper to return the slider dimensions for finding
180183
* the closest handle to user interaction
@@ -197,7 +200,7 @@
197200
let iPos = 0;
198201
let iPercent = 0;
199202
let iVal = 0;
200-
if ( vertical ) {
203+
if (vertical) {
201204
iPos = clientPos - dims.y;
202205
iPercent = (iPos / dims.height) * 100;
203206
iVal = ((max - min) / 100) * iPercent + min;
@@ -208,7 +211,7 @@
208211
}
209212
210213
let closest;
211-
214+
212215
// if we have a range, and the handles are at the same
213216
// position, we want a simple check if the interaction
214217
// value is greater than return the second handle
@@ -244,7 +247,7 @@
244247
let iPos = 0;
245248
let iPercent = 0;
246249
let iVal = 0;
247-
if ( vertical ) {
250+
if (vertical) {
248251
iPos = clientPos - dims.y;
249252
iPercent = (iPos / dims.height) * 100;
250253
iVal = ((max - min) / 100) * iPercent + min;
@@ -459,13 +462,28 @@
459462
height: 0.5em;
460463
margin: 1em;
461464
}
465+
:global(.rangeSlider, .rangeSlider *) {
466+
user-select: none;
467+
}
468+
:global(.rangeSlider.pips) {
469+
margin-bottom: 1.8em;
470+
}
471+
:global(.rangeSlider.pip-labels) {
472+
margin-bottom: 2.8em;
473+
}
462474
:global(.rangeSlider.vertical) {
475+
display: inline-block;
463476
border-radius: 100px;
464477
width: 0.5em;
465478
min-height: 200px;
466479
}
467-
:global(.rangeSlider, .rangeSlider *) {
468-
user-select: none;
480+
:global(.rangeSlider.vertical.pips) {
481+
margin-right: 1.8em;
482+
margin-bottom: 1em;
483+
}
484+
:global(.rangeSlider.vertical.pip-labels) {
485+
margin-right: 2.8em;
486+
margin-bottom: 1em;
469487
}
470488
:global(.rangeSlider .rangeHandle) {
471489
position: absolute;
@@ -580,6 +598,8 @@
580598
class:focus
581599
class:min={range === 'min'}
582600
class:max={range === 'max'}
601+
class:pips
602+
class:pip-labels={first === 'label' || last === 'label' || rest === 'label'}
583603
on:touchstart|preventDefault={sliderInteractStart}
584604
on:mousedown={sliderInteractStart}>
585605
{#each values as value, index}
@@ -596,19 +616,18 @@
596616
aria-valuemax={range === true && index === 0 ? values[1] : max}
597617
aria-valuenow={value}
598618
aria-valuetext="{prefix}{handleFormatter(value)}{suffix}"
599-
aria-orientation="{vertical ? 'vertical' : 'horizontal'}">
619+
aria-orientation={vertical ? 'vertical' : 'horizontal'}>
600620
<span class="rangeNub" />
601621
{#if float}
602-
<span class="rangeFloat">
603-
{prefix}{handleFormatter(value)}{suffix}
604-
</span>
622+
<span class="rangeFloat">{prefix}{handleFormatter(value)}{suffix}</span>
605623
{/if}
606624
</span>
607625
{/each}
608626
{#if range}
609627
<span
610628
class="rangeBar"
611-
style="{vertical ? 'top' : 'left'}: {rangeStart($springPositions)}%; {vertical ? 'bottom' : 'right'}: {rangeEnd($springPositions)}%;" />
629+
style="{vertical ? 'top' : 'left'}: {rangeStart($springPositions)}%; {vertical ? 'bottom' : 'right'}:
630+
{rangeEnd($springPositions)}%;" />
612631
{/if}
613632
{#if pips}
614633
<RangePips

test/src/App.svelte

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
let values = [20, 40, 60, 80];
66
let day = [3];
77
let hue = [244];
8+
let dynamic = [0,50];
89
const formatter = v => {
910
return num.format(v);
1011
};
@@ -49,23 +50,25 @@
4950
<div class="content" style="--range-handle-focus: {color}; --range-handle: {lightColor}">
5051

5152
<RangeSlider vertical range values={[10,30]} pips rest="label" />
53+
<RangeSlider vertical range="min" values={[10]} pips rest="label" />
54+
<RangeSlider vertical range="max" values={[30]} pips rest="label" />
55+
<br>
5256
<RangeSlider />
5357
<RangeSlider bind:values />{values}
5458
<RangeSlider float />
5559
<RangeSlider float pips />
5660
<RangeSlider float pips first="label" last="label" />
5761
<RangeSlider float pips first="label" last="label" rest="label" />
5862
<br>
59-
<br>
6063
<RangeSlider range values={[35,65]} float pips />
6164
<RangeSlider range="min" values={[65]} float />
6265
<RangeSlider range="max" values={[35]} pips />
6366
<br>
6467
<RangeSlider float pips step={10} pipstep={1} />
6568
<RangeSlider float pips step={10} pipstep={2} />
66-
<RangeSlider float pips step={0.1} pipstep={2} max={10} />
69+
<RangeSlider float pips step={0.1} min={dynamic[0]} max={dynamic[1]} />
6770
<br>
68-
<RangeSlider float pips first="label" last="label" rest pipstep={1} />
71+
<RangeSlider float pips first="label" last="label" rest pipstep={1} bind:values={dynamic} range />
6972
<RangeSlider prefix="$" range values={[20,80]} float pips first="label" last="label" />
7073
<RangeSlider prefix="~" suffix="m²" {formatter} range values={[100,3000]} min={100} max={3000} step={50} float pips first="label" last="label" id="clr-test" />
7174
<RangeSlider handleFormatter={()=>""} formatter={(v)=>`${v}% O²`} step={1} float pips first="label" last="label" />
@@ -97,6 +100,6 @@
97100
}
98101
}
99102
:global(.rangeSlider.rangeSlider) {
100-
margin-top: 3em;
103+
/* margin-bottom: 3em; */
101104
}
102105
</style>

0 commit comments

Comments
 (0)