|
22 | 22 | export let id; |
23 | 23 | export let prefix = ""; |
24 | 24 | export let suffix = ""; |
25 | | - export let formatter = v => v; |
| 25 | + export let formatter = (v) => v; |
26 | 26 | export let handleFormatter = formatter; |
27 | 27 |
|
28 | 28 | // stylistic props |
|
40 | 40 |
|
41 | 41 | // save spring-tweened copies of the values for use |
42 | 42 | // 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 | + ); |
44 | 47 |
|
45 | 48 | // watch the values array, and trim / clamp the values to the steps |
46 | 49 | // 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)); |
48 | 51 |
|
49 | 52 | // update the spring function so that movement can happen in the UI |
50 | 53 | $: { |
51 | | - springPositions.set(values.map(v => percentOf(v))); |
| 54 | + springPositions.set(values.map((v) => percentOf(v))); |
52 | 55 | } |
53 | 56 |
|
| 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 | +
|
54 | 116 | /** |
55 | 117 | * helper func to get the index of an element in it's DOM container |
56 | 118 | * @param {object} el dom object reference we want the index of |
|
96 | 158 | function targetIsHandle(el) { |
97 | 159 | const handles = slider.querySelectorAll(".handle"); |
98 | 160 | 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)); |
100 | 162 | return isHandle || isChild; |
101 | 163 | } |
102 | 164 |
|
|
116 | 178 | } |
117 | 179 | } |
118 | 180 |
|
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 | | -
|
178 | 181 | /** |
179 | 182 | * helper to return the slider dimensions for finding |
180 | 183 | * the closest handle to user interaction |
|
197 | 200 | let iPos = 0; |
198 | 201 | let iPercent = 0; |
199 | 202 | let iVal = 0; |
200 | | - if ( vertical ) { |
| 203 | + if (vertical) { |
201 | 204 | iPos = clientPos - dims.y; |
202 | 205 | iPercent = (iPos / dims.height) * 100; |
203 | 206 | iVal = ((max - min) / 100) * iPercent + min; |
|
208 | 211 | } |
209 | 212 |
|
210 | 213 | let closest; |
211 | | - |
| 214 | +
|
212 | 215 | // if we have a range, and the handles are at the same |
213 | 216 | // position, we want a simple check if the interaction |
214 | 217 | // value is greater than return the second handle |
|
244 | 247 | let iPos = 0; |
245 | 248 | let iPercent = 0; |
246 | 249 | let iVal = 0; |
247 | | - if ( vertical ) { |
| 250 | + if (vertical) { |
248 | 251 | iPos = clientPos - dims.y; |
249 | 252 | iPercent = (iPos / dims.height) * 100; |
250 | 253 | iVal = ((max - min) / 100) * iPercent + min; |
|
459 | 462 | height: 0.5em; |
460 | 463 | margin: 1em; |
461 | 464 | } |
| 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 | + } |
462 | 474 | :global(.rangeSlider.vertical) { |
| 475 | + display: inline-block; |
463 | 476 | border-radius: 100px; |
464 | 477 | width: 0.5em; |
465 | 478 | min-height: 200px; |
466 | 479 | } |
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; |
469 | 487 | } |
470 | 488 | :global(.rangeSlider .rangeHandle) { |
471 | 489 | position: absolute; |
|
580 | 598 | class:focus |
581 | 599 | class:min={range === 'min'} |
582 | 600 | class:max={range === 'max'} |
| 601 | + class:pips |
| 602 | + class:pip-labels={first === 'label' || last === 'label' || rest === 'label'} |
583 | 603 | on:touchstart|preventDefault={sliderInteractStart} |
584 | 604 | on:mousedown={sliderInteractStart}> |
585 | 605 | {#each values as value, index} |
|
596 | 616 | aria-valuemax={range === true && index === 0 ? values[1] : max} |
597 | 617 | aria-valuenow={value} |
598 | 618 | aria-valuetext="{prefix}{handleFormatter(value)}{suffix}" |
599 | | - aria-orientation="{vertical ? 'vertical' : 'horizontal'}"> |
| 619 | + aria-orientation={vertical ? 'vertical' : 'horizontal'}> |
600 | 620 | <span class="rangeNub" /> |
601 | 621 | {#if float} |
602 | | - <span class="rangeFloat"> |
603 | | - {prefix}{handleFormatter(value)}{suffix} |
604 | | - </span> |
| 622 | + <span class="rangeFloat">{prefix}{handleFormatter(value)}{suffix}</span> |
605 | 623 | {/if} |
606 | 624 | </span> |
607 | 625 | {/each} |
608 | 626 | {#if range} |
609 | 627 | <span |
610 | 628 | 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)}%;" /> |
612 | 631 | {/if} |
613 | 632 | {#if pips} |
614 | 633 | <RangePips |
|
0 commit comments