Skip to content

Commit a255891

Browse files
committed
reverse the orientation for vertical, add reverse option
- make vertical sliders start with lower value at bottom - allow to reverse the direction - this is a breaking change for vertical sliders resolves #39
1 parent 2f5e62c commit a255891

File tree

7 files changed

+896
-721
lines changed

7 files changed

+896
-721
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ prop | type | default | description
101101
**range** | `Boolean`/`String` | `false` | Whether to style as a range picker. Use `range='min'` or `range='max'` for min/max variants
102102
**pushy** | `Boolean` | `false` | If `range` is `true`, then this boolean decides if one handle will push the other along
103103
**float** | `Boolean` | `false` | Set true to add a floating label above focussed handles
104-
**vertical** | `Boolean` | `false` | Make the slider render vertically
104+
**vertical** | `Boolean` | `false` | Make the slider render vertically (lower value on bottom)
105105
**pips** | `Boolean` | `false` | Whether to show pips/notches on the slider
106106
**pipstep** | `Number` | `1`/`10`/`20` | Every `nth` step to show a pip for. This has multiple defaults depending on `values` property
107107
**first** | `Boolean`/`String` | `false` | Whether to show a pip or label for the first value on slider. Use `first='label'` to show a label value
@@ -110,6 +110,7 @@ prop | type | default | description
110110
**all** | `Boolean`/`String` | `false` | Whether to show a pip or label for all values. Same as combining `first`, `last` and `rest`. Use `all='label'` to show a label value
111111
**prefix** | `String` | `""` | A string to prefix to all displayed values
112112
**suffix** | `String` | `""` | A string to suffix to all displayed values
113+
**reversed** | `Boolean` | `false` | Reverse the orientation of min/max
113114
**hoverable** | `Boolean` | `true` | Whether hover styles are enabled for both handles and pips/values
114115
**disabled** | `Boolean` | `false` | Determine if the slider is disabled, or enabled _(only disables interactions, and events)_
115116
**id** | `String` | `""` | Give the slider a unique ID for use in styling

dist/svelte-range-slider-pips.js

Lines changed: 379 additions & 328 deletions
Large diffs are not rendered by default.

dist/svelte-range-slider-pips.mjs

Lines changed: 379 additions & 328 deletions
Large diffs are not rendered by default.

src/RangePips.svelte

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
export let step = 1;
88
export let values = [(max + min) / 2];
99
export let vertical = false;
10+
export let reversed = false;
1011
export let hoverable = true;
1112
export let disabled = false;
1213
@@ -24,6 +25,7 @@
2425
2526
// stylistic props
2627
export let focus = undefined;
28+
export let orientationStart = undefined;
2729
2830
// methods
2931
export let percentOf = undefined;
@@ -92,8 +94,9 @@
9294
:global(.rangePips.vertical .pip) {
9395
height: 1px;
9496
width: 0.4em;
95-
top: 0;
9697
left: 0.25em;
98+
top: auto;
99+
bottom: auto;
97100
}
98101
:global(.rangePips .pipVal) {
99102
position: absolute;
@@ -165,14 +168,15 @@
165168
class:disabled
166169
class:hoverable
167170
class:vertical
171+
class:reversed
168172
class:focus
169173
>
170174
{#if ( all && first !== false ) || first }
171175
<span
172176
class="pip first"
173177
class:selected={isSelected(min)}
174178
class:in-range={inRange(min)}
175-
style="{vertical ? 'top' : 'left'}: 0%;"
179+
style="{orientationStart}: 0%;"
176180
on:click={labelClick(min)}
177181
on:touchend|preventDefault={labelClick(min)}
178182
>
@@ -191,7 +195,7 @@
191195
class="pip"
192196
class:selected={isSelected(pipVal(i))}
193197
class:in-range={inRange(pipVal(i))}
194-
style="{vertical ? 'top' : 'left'}: {percentOf(pipVal(i))}%;"
198+
style="{orientationStart}: {percentOf(pipVal(i))}%;"
195199
on:click={labelClick(pipVal(i))}
196200
on:touchend|preventDefault={labelClick(pipVal(i))}
197201
>
@@ -210,7 +214,7 @@
210214
class="pip last"
211215
class:selected={isSelected(max)}
212216
class:in-range={inRange(max)}
213-
style="{vertical ? 'top' : 'left'}: 100%;"
217+
style="{orientationStart}: 100%;"
214218
on:click={labelClick(max)}
215219
on:touchend|preventDefault={labelClick(max)}
216220
>

src/RangeSlider.svelte

Lines changed: 56 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
export let values = [(max + min) / 2];
1313
export let vertical = false;
1414
export let float = false;
15+
export let reversed = false;
1516
export let hoverable = true;
1617
export let disabled = false;
1718
@@ -142,6 +143,13 @@
142143
return parseFloat(aligned.toFixed(precision));
143144
};
144145
146+
/**
147+
* the orientation of the handles/pips based on the
148+
* input values of vertical and reversed
149+
**/
150+
$: orientationStart = vertical ? reversed ? 'top' : 'bottom' : reversed ? 'right' : 'left';
151+
$: orientationEnd = vertical ? reversed ? 'bottom' : 'top' : reversed ? 'left' : 'right';
152+
145153
/**
146154
* helper func to get the index of an element in it's DOM container
147155
* @param {object} el dom object reference we want the index of
@@ -219,26 +227,27 @@
219227
// of the slider, as it may have changed size
220228
const dims = getSliderDimensions();
221229
// calculate the interaction position, percent and value
222-
let hPos = 0;
223-
let hPercent = 0;
224-
let hVal = 0;
230+
let handlePos = 0;
231+
let handlePercent = 0;
232+
let handleVal = 0;
225233
if (vertical) {
226-
hPos = clientPos.clientY - dims.top;
227-
hPercent = (hPos / dims.height) * 100;
228-
hVal = ((max - min) / 100) * hPercent + min;
234+
handlePos = clientPos.clientY - dims.top;
235+
handlePercent = (handlePos / dims.height) * 100;
236+
handlePercent = reversed ? handlePercent : 100 - handlePercent;
229237
} else {
230-
hPos = clientPos.clientX - dims.left;
231-
hPercent = (hPos / dims.width) * 100;
232-
hVal = ((max - min) / 100) * hPercent + min;
238+
handlePos = clientPos.clientX - dims.left;
239+
handlePercent = (handlePos / dims.width) * 100;
240+
handlePercent = reversed ? 100 - handlePercent : handlePercent;
233241
}
242+
handleVal = ((max - min) / 100) * handlePercent + min;
234243
235244
let closest;
236245
237246
// if we have a range, and the handles are at the same
238247
// position, we want a simple check if the interaction
239248
// value is greater than return the second handle
240249
if (range === true && values[0] === values[1]) {
241-
if (hVal > values[1]) {
250+
if (handleVal > values[1]) {
242251
return 1;
243252
} else {
244253
return 0;
@@ -248,7 +257,7 @@
248257
// to the interaction value
249258
} else {
250259
closest = values.indexOf(
251-
[...values].sort((a, b) => Math.abs(hVal - a) - Math.abs(hVal - b))[0]
260+
[...values].sort((a, b) => Math.abs(handleVal - a) - Math.abs(handleVal - b))[0]
252261
);
253262
}
254263
return closest;
@@ -272,12 +281,13 @@
272281
if (vertical) {
273282
handlePos = clientPos.clientY - dims.top;
274283
handlePercent = (handlePos / dims.height) * 100;
275-
handleVal = ((max - min) / 100) * handlePercent + min;
284+
handlePercent = reversed ? handlePercent : 100 - handlePercent;
276285
} else {
277286
handlePos = clientPos.clientX - dims.left;
278287
handlePercent = (handlePos / dims.width) * 100;
279-
handleVal = ((max - min) / 100) * handlePercent + min;
288+
handlePercent = reversed ? 100 - handlePercent : handlePercent;
280289
}
290+
handleVal = ((max - min) / 100) * handlePercent + min;
281291
// move handle to the value
282292
moveHandle(activeHandle, handleVal);
283293
}
@@ -616,10 +626,21 @@
616626
height: 1.4em;
617627
width: 1.4em;
618628
top: 0.25em;
619-
left: 0.25em;
629+
bottom: auto;
620630
transform: translateY(-50%) translateX(-50%);
621631
z-index: 2;
622632
}
633+
:global(.rangeSlider.reversed .rangeHandle) {
634+
transform: translateY(-50%) translateX(50%);
635+
}
636+
:global(.rangeSlider.vertical .rangeHandle) {
637+
left: 0.25em;
638+
top: auto;
639+
transform: translateY(50%) translateX(-50%);
640+
}
641+
:global(.rangeSlider.vertical.reversed .rangeHandle) {
642+
transform: translateY(-50%) translateX(-50%);
643+
}
623644
:global(.rangeSlider .rangeNub),
624645
:global(.rangeSlider .rangeHandle:before) {
625646
position: absolute;
@@ -629,7 +650,7 @@
629650
border-radius: 10em;
630651
height: 100%;
631652
width: 100%;
632-
transition: all 0.2s ease;
653+
transition: box-shadow 0.2s ease;
633654
}
634655
:global(.rangeSlider .rangeHandle:before) {
635656
content: "";
@@ -660,10 +681,22 @@
660681
:global(.rangeSlider.range .rangeHandle:nth-of-type(2) .rangeNub) {
661682
transform: rotate(45deg);
662683
}
684+
:global(.rangeSlider.range.reversed .rangeHandle:nth-of-type(1) .rangeNub) {
685+
transform: rotate(45deg);
686+
}
687+
:global(.rangeSlider.range.reversed .rangeHandle:nth-of-type(2) .rangeNub) {
688+
transform: rotate(-135deg);
689+
}
663690
:global(.rangeSlider.range.vertical .rangeHandle:nth-of-type(1) .rangeNub) {
664-
transform: rotate(-45deg);
691+
transform: rotate(135deg);
665692
}
666693
:global(.rangeSlider.range.vertical .rangeHandle:nth-of-type(2) .rangeNub) {
694+
transform: rotate(-45deg);
695+
}
696+
:global(.rangeSlider.range.vertical.reversed .rangeHandle:nth-of-type(1) .rangeNub) {
697+
transform: rotate(-45deg);
698+
}
699+
:global(.rangeSlider.range.vertical.reversed .rangeHandle:nth-of-type(2) .rangeNub) {
667700
transform: rotate(135deg);
668701
}
669702
:global(.rangeSlider .rangeFloat) {
@@ -753,6 +786,7 @@
753786
class:disabled
754787
class:hoverable
755788
class:vertical
789+
class:reversed
756790
class:focus
757791
class:min={range === 'min'}
758792
class:max={range === 'max'}
@@ -773,7 +807,7 @@
773807
on:blur={sliderBlurHandle}
774808
on:focus={sliderFocusHandle}
775809
on:keydown={sliderKeydown}
776-
style="{vertical ? 'top' : 'left'}: {$springPositions[index]}%; z-index: {activeHandle === index ? 3 : 2};"
810+
style="{orientationStart}: {$springPositions[index]}%; z-index: {activeHandle === index ? 3 : 2};"
777811
aria-valuemin={range === true && index === 1 ? values[0] : min}
778812
aria-valuemax={range === true && index === 0 ? values[1] : max}
779813
aria-valuenow={value}
@@ -794,8 +828,8 @@
794828
{#if range}
795829
<span
796830
class="rangeBar"
797-
style="{vertical ? 'top' : 'left'}: {rangeStart($springPositions)}%; {vertical ? 'bottom' : 'right'}:
798-
{rangeEnd($springPositions)}%;" />
831+
style="{orientationStart}: {rangeStart($springPositions)}%;
832+
{orientationEnd}: {rangeEnd($springPositions)}%;" />
799833
{/if}
800834
{#if pips}
801835
<RangePips
@@ -805,6 +839,9 @@
805839
{step}
806840
{range}
807841
{vertical}
842+
{reversed}
843+
{orientationStart}
844+
{orientationEnd}
808845
{hoverable}
809846
{disabled}
810847
{all}

test/public/global.css

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,17 @@ body {
1212
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
1313
}
1414

15+
header {
16+
position: fixed;
17+
top: 0;
18+
left: 0;
19+
right: 0;
20+
padding: 20px;
21+
background: rgb(42, 53, 70);
22+
color: white;
23+
z-index: 6;
24+
}
25+
1526
a {
1627
color: rgb(0,100,200);
1728
text-decoration: none;

0 commit comments

Comments
 (0)