Skip to content

Commit ebfc946

Browse files
committed
add event dispatchers for start/change/end interactions
- Also provide the values of the handles and indexes changed - Add some tests for the dispatched events - Update Readme.md the Change event will also fire for keyboard interactions, but currently I decided to avoid the "start" event for keyboard users as it is kind of tricky to implement, and the "end" event is impossible to implement. I may consider a focus/blur event at a later time. resolves: #6
1 parent bc8766f commit ebfc946

File tree

8 files changed

+318
-93
lines changed

8 files changed

+318
-93
lines changed

README.md

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ A reactive, accessible, multi-thumb, range slider with the ability to display "p
77
![Svelte Range Slider -- focussed, including some pips](test/public/slider.png)
88

99

10-
**[🔗 _For full documentation and examples, see the Github Pages_](https://simeydotme.github.io/svelte-range-slider-pips/)**
10+
**[📔📘📖 _Full Documentation & Examples_](https://simeydotme.github.io/svelte-range-slider-pips/)**
1111

1212

1313
---
@@ -31,8 +31,8 @@ A reactive, accessible, multi-thumb, range slider with the ability to display "p
3131

3232
Open your project and use the command line to install the package;
3333
```bash
34-
yarn add --dev svelte-range-slider-pips # or
35-
npm install --save-dev svelte-range-slider-pips # if you prefer npm
34+
yarn add svelte-range-slider-pips --dev # or
35+
npm install svelte-range-slider-pips --save-dev # if you prefer npm
3636
```
3737

3838
## usage
@@ -107,7 +107,14 @@ prop | type | default | description
107107
**handleFormatter** | `Function` | `formatter` | A function to re-format values on the handle/float before they are displayed. Defaults to the same function given to the `formatter` property
108108
**springValues** | `Object` | `{ stiffness: 0.15, damping: 0.4 }` | Svelte spring physics object to change the behaviour of the handle when moving
109109

110-
**[🔗 _For full documentation and examples, see the Github Pages_](https://simeydotme.github.io/svelte-range-slider-pips/)**
110+
### slider events (dispatched)
111+
event | example | `event.detail` | description
112+
------|------------|--------|-------------
113+
**start** | `on:start={(e) => { ... }}` | `{ activeHandle: Integer, value: Float, values: Array }` | Event fired when the user begins interaction with the slider
114+
**change** | `on:change={(e) => { ... }}` | `{ activeHandle: Integer, previousValue: Float, value: Float, values: Array }` | Event fired when the user changes the value; returns the previous value, also
115+
**stop** | `on:stop={(e) => { ... }}` | `{ activeHandle: Integer, startValue: Float, value: Float, values: Array }` | Event fired when the user stops interacting with slider; returns the beginning value, also
116+
117+
**[📔📘📖 _Full Documentation & Examples_](https://simeydotme.github.io/svelte-range-slider-pips/)**
111118

112119

113120
## contribute

dist/svelte-range-slider-pips.js

Lines changed: 111 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/**
2-
* svelte-range-slider-pips ~ 1.5.3
2+
* svelte-range-slider-pips ~ 1.6.0
33
* Multi-Thumb, Accessible, Beautiful Range Slider with Pips
4-
* © MPL-2.0 ~ Simon Goellner <[email protected]> ~ 7/1/2021
4+
* © MPL-2.0 ~ Simon Goellner <[email protected]> ~ 16/1/2021
55
*/
66
(function (global, factory) {
77
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
@@ -126,11 +126,35 @@
126126
function toggle_class(element, name, toggle) {
127127
element.classList[toggle ? 'add' : 'remove'](name);
128128
}
129+
function custom_event(type, detail) {
130+
const e = document.createEvent('CustomEvent');
131+
e.initCustomEvent(type, false, false, detail);
132+
return e;
133+
}
129134

130135
let current_component;
131136
function set_current_component(component) {
132137
current_component = component;
133138
}
139+
function get_current_component() {
140+
if (!current_component)
141+
throw new Error(`Function called outside component initialization`);
142+
return current_component;
143+
}
144+
function createEventDispatcher() {
145+
const component = get_current_component();
146+
return (type, detail) => {
147+
const callbacks = component.$$.callbacks[type];
148+
if (callbacks) {
149+
// TODO are there situations where events could be dispatched
150+
// in a server (non-DOM) environment?
151+
const event = custom_event(type, detail);
152+
callbacks.slice().forEach(fn => {
153+
fn.call(component, event);
154+
});
155+
}
156+
};
157+
}
134158

135159
const dirty_components = [];
136160
const binding_callbacks = [];
@@ -1104,16 +1128,16 @@
11041128

11051129
function get_each_context$1(ctx, list, i) {
11061130
const child_ctx = ctx.slice();
1107-
child_ctx[52] = list[i];
1108-
child_ctx[54] = i;
1131+
child_ctx[58] = list[i];
1132+
child_ctx[60] = i;
11091133
return child_ctx;
11101134
}
11111135

1112-
// (671:6) {#if float}
1136+
// (724:6) {#if float}
11131137
function create_if_block_2$1(ctx) {
11141138
let span;
11151139
let t0;
1116-
let t1_value = /*handleFormatter*/ ctx[18](/*value*/ ctx[52]) + "";
1140+
let t1_value = /*handleFormatter*/ ctx[18](/*value*/ ctx[58]) + "";
11171141
let t1;
11181142
let t2;
11191143

@@ -1133,7 +1157,7 @@
11331157
},
11341158
p(ctx, dirty) {
11351159
if (dirty[0] & /*prefix*/ 32768) set_data(t0, /*prefix*/ ctx[15]);
1136-
if (dirty[0] & /*handleFormatter, values*/ 262145 && t1_value !== (t1_value = /*handleFormatter*/ ctx[18](/*value*/ ctx[52]) + "")) set_data(t1, t1_value);
1160+
if (dirty[0] & /*handleFormatter, values*/ 262145 && t1_value !== (t1_value = /*handleFormatter*/ ctx[18](/*value*/ ctx[58]) + "")) set_data(t1, t1_value);
11371161
if (dirty[0] & /*suffix*/ 65536) set_data(t2, /*suffix*/ ctx[16]);
11381162
},
11391163
d(detaching) {
@@ -1142,7 +1166,7 @@
11421166
};
11431167
}
11441168

1145-
// (653:2) {#each values as value, index}
1169+
// (706:2) {#each values as value, index}
11461170
function create_each_block$1(ctx) {
11471171
let span1;
11481172
let span0;
@@ -1167,22 +1191,22 @@
11671191
attr(span1, "role", "slider");
11681192
attr(span1, "class", "rangeHandle");
11691193
attr(span1, "tabindex", "0");
1170-
attr(span1, "style", span1_style_value = "" + ((/*vertical*/ ctx[5] ? "top" : "left") + ": " + /*$springPositions*/ ctx[24][/*index*/ ctx[54]] + "%; z-index: " + (/*activeHandle*/ ctx[22] === /*index*/ ctx[54] ? 3 : 2) + ";"));
1194+
attr(span1, "style", span1_style_value = "" + ((/*vertical*/ ctx[5] ? "top" : "left") + ": " + /*$springPositions*/ ctx[24][/*index*/ ctx[60]] + "%; z-index: " + (/*activeHandle*/ ctx[22] === /*index*/ ctx[60] ? 3 : 2) + ";"));
11711195

1172-
attr(span1, "aria-valuemin", span1_aria_valuemin_value = /*range*/ ctx[1] === true && /*index*/ ctx[54] === 1
1196+
attr(span1, "aria-valuemin", span1_aria_valuemin_value = /*range*/ ctx[1] === true && /*index*/ ctx[60] === 1
11731197
? /*values*/ ctx[0][0]
11741198
: /*min*/ ctx[2]);
11751199

1176-
attr(span1, "aria-valuemax", span1_aria_valuemax_value = /*range*/ ctx[1] === true && /*index*/ ctx[54] === 0
1200+
attr(span1, "aria-valuemax", span1_aria_valuemax_value = /*range*/ ctx[1] === true && /*index*/ ctx[60] === 0
11771201
? /*values*/ ctx[0][1]
11781202
: /*max*/ ctx[3]);
11791203

1180-
attr(span1, "aria-valuenow", span1_aria_valuenow_value = /*value*/ ctx[52]);
1181-
attr(span1, "aria-valuetext", span1_aria_valuetext_value = "" + (/*prefix*/ ctx[15] + /*handleFormatter*/ ctx[18](/*value*/ ctx[52]) + /*suffix*/ ctx[16]));
1204+
attr(span1, "aria-valuenow", span1_aria_valuenow_value = /*value*/ ctx[58]);
1205+
attr(span1, "aria-valuetext", span1_aria_valuetext_value = "" + (/*prefix*/ ctx[15] + /*handleFormatter*/ ctx[18](/*value*/ ctx[58]) + /*suffix*/ ctx[16]));
11821206
attr(span1, "aria-orientation", span1_aria_orientation_value = /*vertical*/ ctx[5] ? "vertical" : "horizontal");
11831207
toggle_class(span1, "hoverable", /*hover*/ ctx[7]);
1184-
toggle_class(span1, "active", /*focus*/ ctx[20] && /*activeHandle*/ ctx[22] === /*index*/ ctx[54]);
1185-
toggle_class(span1, "press", /*handlePressed*/ ctx[21] && /*activeHandle*/ ctx[22] === /*index*/ ctx[54]);
1208+
toggle_class(span1, "active", /*focus*/ ctx[20] && /*activeHandle*/ ctx[22] === /*index*/ ctx[60]);
1209+
toggle_class(span1, "press", /*handlePressed*/ ctx[21] && /*activeHandle*/ ctx[22] === /*index*/ ctx[60]);
11861210
},
11871211
m(target, anchor) {
11881212
insert(target, span1, anchor);
@@ -1214,27 +1238,27 @@
12141238
if_block = null;
12151239
}
12161240

1217-
if (dirty[0] & /*vertical, $springPositions, activeHandle*/ 20971552 && span1_style_value !== (span1_style_value = "" + ((/*vertical*/ ctx[5] ? "top" : "left") + ": " + /*$springPositions*/ ctx[24][/*index*/ ctx[54]] + "%; z-index: " + (/*activeHandle*/ ctx[22] === /*index*/ ctx[54] ? 3 : 2) + ";"))) {
1241+
if (dirty[0] & /*vertical, $springPositions, activeHandle*/ 20971552 && span1_style_value !== (span1_style_value = "" + ((/*vertical*/ ctx[5] ? "top" : "left") + ": " + /*$springPositions*/ ctx[24][/*index*/ ctx[60]] + "%; z-index: " + (/*activeHandle*/ ctx[22] === /*index*/ ctx[60] ? 3 : 2) + ";"))) {
12181242
attr(span1, "style", span1_style_value);
12191243
}
12201244

1221-
if (dirty[0] & /*range, values, min*/ 7 && span1_aria_valuemin_value !== (span1_aria_valuemin_value = /*range*/ ctx[1] === true && /*index*/ ctx[54] === 1
1245+
if (dirty[0] & /*range, values, min*/ 7 && span1_aria_valuemin_value !== (span1_aria_valuemin_value = /*range*/ ctx[1] === true && /*index*/ ctx[60] === 1
12221246
? /*values*/ ctx[0][0]
12231247
: /*min*/ ctx[2])) {
12241248
attr(span1, "aria-valuemin", span1_aria_valuemin_value);
12251249
}
12261250

1227-
if (dirty[0] & /*range, values, max*/ 11 && span1_aria_valuemax_value !== (span1_aria_valuemax_value = /*range*/ ctx[1] === true && /*index*/ ctx[54] === 0
1251+
if (dirty[0] & /*range, values, max*/ 11 && span1_aria_valuemax_value !== (span1_aria_valuemax_value = /*range*/ ctx[1] === true && /*index*/ ctx[60] === 0
12281252
? /*values*/ ctx[0][1]
12291253
: /*max*/ ctx[3])) {
12301254
attr(span1, "aria-valuemax", span1_aria_valuemax_value);
12311255
}
12321256

1233-
if (dirty[0] & /*values*/ 1 && span1_aria_valuenow_value !== (span1_aria_valuenow_value = /*value*/ ctx[52])) {
1257+
if (dirty[0] & /*values*/ 1 && span1_aria_valuenow_value !== (span1_aria_valuenow_value = /*value*/ ctx[58])) {
12341258
attr(span1, "aria-valuenow", span1_aria_valuenow_value);
12351259
}
12361260

1237-
if (dirty[0] & /*prefix, handleFormatter, values, suffix*/ 360449 && span1_aria_valuetext_value !== (span1_aria_valuetext_value = "" + (/*prefix*/ ctx[15] + /*handleFormatter*/ ctx[18](/*value*/ ctx[52]) + /*suffix*/ ctx[16]))) {
1261+
if (dirty[0] & /*prefix, handleFormatter, values, suffix*/ 360449 && span1_aria_valuetext_value !== (span1_aria_valuetext_value = "" + (/*prefix*/ ctx[15] + /*handleFormatter*/ ctx[18](/*value*/ ctx[58]) + /*suffix*/ ctx[16]))) {
12381262
attr(span1, "aria-valuetext", span1_aria_valuetext_value);
12391263
}
12401264

@@ -1247,11 +1271,11 @@
12471271
}
12481272

12491273
if (dirty[0] & /*focus, activeHandle*/ 5242880) {
1250-
toggle_class(span1, "active", /*focus*/ ctx[20] && /*activeHandle*/ ctx[22] === /*index*/ ctx[54]);
1274+
toggle_class(span1, "active", /*focus*/ ctx[20] && /*activeHandle*/ ctx[22] === /*index*/ ctx[60]);
12511275
}
12521276

12531277
if (dirty[0] & /*handlePressed, activeHandle*/ 6291456) {
1254-
toggle_class(span1, "press", /*handlePressed*/ ctx[21] && /*activeHandle*/ ctx[22] === /*index*/ ctx[54]);
1278+
toggle_class(span1, "press", /*handlePressed*/ ctx[21] && /*activeHandle*/ ctx[22] === /*index*/ ctx[60]);
12551279
}
12561280
},
12571281
d(detaching) {
@@ -1263,7 +1287,7 @@
12631287
};
12641288
}
12651289

1266-
// (676:2) {#if range}
1290+
// (729:2) {#if range}
12671291
function create_if_block_1$1(ctx) {
12681292
let span;
12691293
let span_style_value;
@@ -1288,7 +1312,7 @@
12881312
};
12891313
}
12901314

1291-
// (682:2) {#if pips}
1315+
// (735:2) {#if pips}
12921316
function create_if_block$1(ctx) {
12931317
let rangepips;
12941318
let current;
@@ -1419,10 +1443,10 @@
14191443
listen(window, "mouseup", /*bodyMouseUp*/ ctx[35]),
14201444
listen(window, "touchend", /*bodyTouchEnd*/ ctx[36]),
14211445
listen(window, "keydown", /*bodyKeyDown*/ ctx[37]),
1422-
listen(div, "touchstart", prevent_default(/*sliderInteractStart*/ ctx[31])),
14231446
listen(div, "mousedown", /*sliderInteractStart*/ ctx[31]),
1424-
listen(div, "touchend", prevent_default(/*sliderInteractEnd*/ ctx[32])),
1425-
listen(div, "mouseup", /*sliderInteractEnd*/ ctx[32])
1447+
listen(div, "mouseup", /*sliderInteractEnd*/ ctx[32]),
1448+
listen(div, "touchstart", prevent_default(/*sliderInteractStart*/ ctx[31])),
1449+
listen(div, "touchend", prevent_default(/*sliderInteractEnd*/ ctx[32]))
14261450
];
14271451

14281452
mounted = true;
@@ -1590,6 +1614,7 @@
15901614
let { handleFormatter = formatter } = $$props;
15911615
let { precision = 2 } = $$props;
15921616
let { springValues = { stiffness: 0.15, damping: 0.4 } } = $$props;
1617+
const dispatch = createEventDispatcher();
15931618

15941619
// dom references
15951620
let slider;
@@ -1601,6 +1626,8 @@
16011626
let handlePressed = false;
16021627
let keyboardActive = false;
16031628
let activeHandle = values.length - 1;
1629+
let startValue;
1630+
let previousValue;
16041631

16051632
// save spring-tweened copies of the values for use
16061633
// when changing values and animating the handle/range nicely
@@ -1750,6 +1777,13 @@
17501777

17511778
// set the value for the handle, and align/clamp it
17521779
$$invalidate(0, values[index] = value, values);
1780+
1781+
// fire the change event when the handle moves,
1782+
// and store the previous value for the next time
1783+
if (previousValue !== alignValueToStep(value)) {
1784+
eChange();
1785+
previousValue = alignValueToStep(value);
1786+
}
17531787
}
17541788

17551789
/**
@@ -1859,6 +1893,11 @@
18591893
$$invalidate(21, handlePressed = true);
18601894
$$invalidate(22, activeHandle = getClosestHandle(clientPos));
18611895

1896+
// fire the start event
1897+
startValue = values[activeHandle];
1898+
1899+
eStart();
1900+
18621901
// for touch devices we want the handle to instantly
18631902
// move to the position touched for more responsive feeling
18641903
if (e.type === "touchstart") {
@@ -1872,6 +1911,11 @@
18721911
* @param {event} e the event from browser
18731912
**/
18741913
function sliderInteractEnd(e) {
1914+
// fire the stop event for touch devices
1915+
if (e.type === "touchend") {
1916+
eStop();
1917+
}
1918+
18751919
$$invalidate(21, handlePressed = false);
18761920
}
18771921

@@ -1911,12 +1955,18 @@
19111955
// this only works if a handle is active, which can
19121956
// only happen if there was sliderInteractStart triggered
19131957
// on the slider, already
1914-
if (handleActivated && (el === slider || slider.contains(el))) {
1915-
$$invalidate(20, focus = true);
1958+
if (handleActivated) {
1959+
if (el === slider || slider.contains(el)) {
1960+
$$invalidate(20, focus = true);
19161961

1917-
if (!targetIsHandle(el)) {
1918-
handleInteract(normalisedClient(e));
1962+
if (!targetIsHandle(el)) {
1963+
handleInteract(normalisedClient(e));
1964+
}
19191965
}
1966+
1967+
// fire the stop event for mouse device
1968+
// when the body is triggered with an active handle
1969+
eStop();
19201970
}
19211971

19221972
handleActivated = false;
@@ -1939,6 +1989,32 @@
19391989
}
19401990
}
19411991

1992+
function eStart() {
1993+
dispatch("start", {
1994+
activeHandle,
1995+
value: alignValueToStep(startValue),
1996+
values: values.map(v => alignValueToStep(v))
1997+
});
1998+
}
1999+
2000+
function eStop() {
2001+
dispatch("stop", {
2002+
activeHandle,
2003+
startValue: alignValueToStep(startValue),
2004+
value: alignValueToStep(values[activeHandle]),
2005+
values: values.map(v => alignValueToStep(v))
2006+
});
2007+
}
2008+
2009+
function eChange() {
2010+
dispatch("change", {
2011+
activeHandle,
2012+
previousValue: alignValueToStep(previousValue) || alignValueToStep(startValue) || alignValueToStep(values[activeHandle]),
2013+
value: alignValueToStep(values[activeHandle]),
2014+
values: values.map(v => alignValueToStep(v))
2015+
});
2016+
}
2017+
19422018
function div_binding($$value) {
19432019
binding_callbacks[$$value ? "unshift" : "push"](() => {
19442020
slider = $$value;
@@ -1983,20 +2059,20 @@
19832059
* @param {number} val the value to clamp
19842060
* @return {number} the value after it's been clamped
19852061
**/
1986-
$$invalidate(45, clampValue = function (val) {
2062+
$$invalidate(47, clampValue = function (val) {
19872063
// return the min/max if outside of that range
19882064
return val <= min ? min : val >= max ? max : val;
19892065
});
19902066
}
19912067

1992-
if ($$self.$$.dirty[0] & /*min, max, step*/ 28 | $$self.$$.dirty[1] & /*clampValue, precision*/ 16640) {
2068+
if ($$self.$$.dirty[0] & /*min, max, step*/ 28 | $$self.$$.dirty[1] & /*clampValue, precision*/ 65792) {
19932069
/**
19942070
* align the value with the steps so that it
19952071
* always sits on the closest (above/below) step
19962072
* @param {number} val the value to align
19972073
* @return {number} the value after it's been aligned
19982074
**/
1999-
$$invalidate(44, alignValueToStep = function (val) {
2075+
$$invalidate(46, alignValueToStep = function (val) {
20002076
// sanity check for performance
20012077
if (val <= min) {
20022078
return min;
@@ -2025,7 +2101,7 @@
20252101
});
20262102
}
20272103

2028-
if ($$self.$$.dirty[0] & /*values*/ 1 | $$self.$$.dirty[1] & /*alignValueToStep*/ 8192) {
2104+
if ($$self.$$.dirty[0] & /*values*/ 1 | $$self.$$.dirty[1] & /*alignValueToStep*/ 32768) {
20292105
// watch the values array, and trim / clamp the values to the steps
20302106
// and boundaries set up in the slider on change
20312107
$$invalidate(0, values = trimRange(values).map(v => alignValueToStep(v)));

0 commit comments

Comments
 (0)