From ebfc94688570a0788e35031665b2a194c67ff591 Mon Sep 17 00:00:00 2001 From: simeydotme Date: Sat, 16 Jan 2021 19:57:35 +0800 Subject: [PATCH] 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 --- README.md | 15 ++- dist/svelte-range-slider-pips.js | 146 +++++++++++++++++++++++------- dist/svelte-range-slider-pips.mjs | 146 +++++++++++++++++++++++------- package.json | 2 +- src/RangeSlider.svelte | 65 +++++++++++-- test/package.json | 2 +- test/src/App.svelte | 22 ++++- test/yarn.lock | 13 +-- 8 files changed, 318 insertions(+), 93 deletions(-) diff --git a/README.md b/README.md index 274cdf3..c58e7f5 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ A reactive, accessible, multi-thumb, range slider with the ability to display "p ![Svelte Range Slider -- focussed, including some pips](test/public/slider.png) -**[🔗 _For full documentation and examples, see the Github Pages_](https://simeydotme.github.io/svelte-range-slider-pips/)** +**[📔📘📖 _Full Documentation & Examples_](https://simeydotme.github.io/svelte-range-slider-pips/)** --- @@ -31,8 +31,8 @@ A reactive, accessible, multi-thumb, range slider with the ability to display "p Open your project and use the command line to install the package; ```bash -yarn add --dev svelte-range-slider-pips # or -npm install --save-dev svelte-range-slider-pips # if you prefer npm +yarn add svelte-range-slider-pips --dev # or +npm install svelte-range-slider-pips --save-dev # if you prefer npm ``` ## usage @@ -107,7 +107,14 @@ prop | type | default | description **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 **springValues** | `Object` | `{ stiffness: 0.15, damping: 0.4 }` | Svelte spring physics object to change the behaviour of the handle when moving -**[🔗 _For full documentation and examples, see the Github Pages_](https://simeydotme.github.io/svelte-range-slider-pips/)** +### slider events (dispatched) +event | example | `event.detail` | description +------|------------|--------|------------- +**start** | `on:start={(e) => { ... }}` | `{ activeHandle: Integer, value: Float, values: Array }` | Event fired when the user begins interaction with the slider +**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 +**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 + +**[📔📘📖 _Full Documentation & Examples_](https://simeydotme.github.io/svelte-range-slider-pips/)** ## contribute diff --git a/dist/svelte-range-slider-pips.js b/dist/svelte-range-slider-pips.js index 8006d7c..91b9d96 100644 --- a/dist/svelte-range-slider-pips.js +++ b/dist/svelte-range-slider-pips.js @@ -1,7 +1,7 @@ /** - * svelte-range-slider-pips ~ 1.5.3 + * svelte-range-slider-pips ~ 1.6.0 * Multi-Thumb, Accessible, Beautiful Range Slider with Pips - * © MPL-2.0 ~ Simon Goellner ~ 7/1/2021 + * © MPL-2.0 ~ Simon Goellner ~ 16/1/2021 */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : @@ -126,11 +126,35 @@ function toggle_class(element, name, toggle) { element.classList[toggle ? 'add' : 'remove'](name); } + function custom_event(type, detail) { + const e = document.createEvent('CustomEvent'); + e.initCustomEvent(type, false, false, detail); + return e; + } let current_component; function set_current_component(component) { current_component = component; } + function get_current_component() { + if (!current_component) + throw new Error(`Function called outside component initialization`); + return current_component; + } + function createEventDispatcher() { + const component = get_current_component(); + return (type, detail) => { + const callbacks = component.$$.callbacks[type]; + if (callbacks) { + // TODO are there situations where events could be dispatched + // in a server (non-DOM) environment? + const event = custom_event(type, detail); + callbacks.slice().forEach(fn => { + fn.call(component, event); + }); + } + }; + } const dirty_components = []; const binding_callbacks = []; @@ -1104,16 +1128,16 @@ function get_each_context$1(ctx, list, i) { const child_ctx = ctx.slice(); - child_ctx[52] = list[i]; - child_ctx[54] = i; + child_ctx[58] = list[i]; + child_ctx[60] = i; return child_ctx; } - // (671:6) {#if float} + // (724:6) {#if float} function create_if_block_2$1(ctx) { let span; let t0; - let t1_value = /*handleFormatter*/ ctx[18](/*value*/ ctx[52]) + ""; + let t1_value = /*handleFormatter*/ ctx[18](/*value*/ ctx[58]) + ""; let t1; let t2; @@ -1133,7 +1157,7 @@ }, p(ctx, dirty) { if (dirty[0] & /*prefix*/ 32768) set_data(t0, /*prefix*/ ctx[15]); - if (dirty[0] & /*handleFormatter, values*/ 262145 && t1_value !== (t1_value = /*handleFormatter*/ ctx[18](/*value*/ ctx[52]) + "")) set_data(t1, t1_value); + if (dirty[0] & /*handleFormatter, values*/ 262145 && t1_value !== (t1_value = /*handleFormatter*/ ctx[18](/*value*/ ctx[58]) + "")) set_data(t1, t1_value); if (dirty[0] & /*suffix*/ 65536) set_data(t2, /*suffix*/ ctx[16]); }, d(detaching) { @@ -1142,7 +1166,7 @@ }; } - // (653:2) {#each values as value, index} + // (706:2) {#each values as value, index} function create_each_block$1(ctx) { let span1; let span0; @@ -1167,22 +1191,22 @@ attr(span1, "role", "slider"); attr(span1, "class", "rangeHandle"); attr(span1, "tabindex", "0"); - 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) + ";")); + 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) + ";")); - attr(span1, "aria-valuemin", span1_aria_valuemin_value = /*range*/ ctx[1] === true && /*index*/ ctx[54] === 1 + attr(span1, "aria-valuemin", span1_aria_valuemin_value = /*range*/ ctx[1] === true && /*index*/ ctx[60] === 1 ? /*values*/ ctx[0][0] : /*min*/ ctx[2]); - attr(span1, "aria-valuemax", span1_aria_valuemax_value = /*range*/ ctx[1] === true && /*index*/ ctx[54] === 0 + attr(span1, "aria-valuemax", span1_aria_valuemax_value = /*range*/ ctx[1] === true && /*index*/ ctx[60] === 0 ? /*values*/ ctx[0][1] : /*max*/ ctx[3]); - attr(span1, "aria-valuenow", span1_aria_valuenow_value = /*value*/ ctx[52]); - attr(span1, "aria-valuetext", span1_aria_valuetext_value = "" + (/*prefix*/ ctx[15] + /*handleFormatter*/ ctx[18](/*value*/ ctx[52]) + /*suffix*/ ctx[16])); + attr(span1, "aria-valuenow", span1_aria_valuenow_value = /*value*/ ctx[58]); + attr(span1, "aria-valuetext", span1_aria_valuetext_value = "" + (/*prefix*/ ctx[15] + /*handleFormatter*/ ctx[18](/*value*/ ctx[58]) + /*suffix*/ ctx[16])); attr(span1, "aria-orientation", span1_aria_orientation_value = /*vertical*/ ctx[5] ? "vertical" : "horizontal"); toggle_class(span1, "hoverable", /*hover*/ ctx[7]); - toggle_class(span1, "active", /*focus*/ ctx[20] && /*activeHandle*/ ctx[22] === /*index*/ ctx[54]); - toggle_class(span1, "press", /*handlePressed*/ ctx[21] && /*activeHandle*/ ctx[22] === /*index*/ ctx[54]); + toggle_class(span1, "active", /*focus*/ ctx[20] && /*activeHandle*/ ctx[22] === /*index*/ ctx[60]); + toggle_class(span1, "press", /*handlePressed*/ ctx[21] && /*activeHandle*/ ctx[22] === /*index*/ ctx[60]); }, m(target, anchor) { insert(target, span1, anchor); @@ -1214,27 +1238,27 @@ if_block = null; } - 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) + ";"))) { + 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) + ";"))) { attr(span1, "style", span1_style_value); } - if (dirty[0] & /*range, values, min*/ 7 && span1_aria_valuemin_value !== (span1_aria_valuemin_value = /*range*/ ctx[1] === true && /*index*/ ctx[54] === 1 + if (dirty[0] & /*range, values, min*/ 7 && span1_aria_valuemin_value !== (span1_aria_valuemin_value = /*range*/ ctx[1] === true && /*index*/ ctx[60] === 1 ? /*values*/ ctx[0][0] : /*min*/ ctx[2])) { attr(span1, "aria-valuemin", span1_aria_valuemin_value); } - if (dirty[0] & /*range, values, max*/ 11 && span1_aria_valuemax_value !== (span1_aria_valuemax_value = /*range*/ ctx[1] === true && /*index*/ ctx[54] === 0 + if (dirty[0] & /*range, values, max*/ 11 && span1_aria_valuemax_value !== (span1_aria_valuemax_value = /*range*/ ctx[1] === true && /*index*/ ctx[60] === 0 ? /*values*/ ctx[0][1] : /*max*/ ctx[3])) { attr(span1, "aria-valuemax", span1_aria_valuemax_value); } - if (dirty[0] & /*values*/ 1 && span1_aria_valuenow_value !== (span1_aria_valuenow_value = /*value*/ ctx[52])) { + if (dirty[0] & /*values*/ 1 && span1_aria_valuenow_value !== (span1_aria_valuenow_value = /*value*/ ctx[58])) { attr(span1, "aria-valuenow", span1_aria_valuenow_value); } - 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]))) { + 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]))) { attr(span1, "aria-valuetext", span1_aria_valuetext_value); } @@ -1247,11 +1271,11 @@ } if (dirty[0] & /*focus, activeHandle*/ 5242880) { - toggle_class(span1, "active", /*focus*/ ctx[20] && /*activeHandle*/ ctx[22] === /*index*/ ctx[54]); + toggle_class(span1, "active", /*focus*/ ctx[20] && /*activeHandle*/ ctx[22] === /*index*/ ctx[60]); } if (dirty[0] & /*handlePressed, activeHandle*/ 6291456) { - toggle_class(span1, "press", /*handlePressed*/ ctx[21] && /*activeHandle*/ ctx[22] === /*index*/ ctx[54]); + toggle_class(span1, "press", /*handlePressed*/ ctx[21] && /*activeHandle*/ ctx[22] === /*index*/ ctx[60]); } }, d(detaching) { @@ -1263,7 +1287,7 @@ }; } - // (676:2) {#if range} + // (729:2) {#if range} function create_if_block_1$1(ctx) { let span; let span_style_value; @@ -1288,7 +1312,7 @@ }; } - // (682:2) {#if pips} + // (735:2) {#if pips} function create_if_block$1(ctx) { let rangepips; let current; @@ -1419,10 +1443,10 @@ listen(window, "mouseup", /*bodyMouseUp*/ ctx[35]), listen(window, "touchend", /*bodyTouchEnd*/ ctx[36]), listen(window, "keydown", /*bodyKeyDown*/ ctx[37]), - listen(div, "touchstart", prevent_default(/*sliderInteractStart*/ ctx[31])), listen(div, "mousedown", /*sliderInteractStart*/ ctx[31]), - listen(div, "touchend", prevent_default(/*sliderInteractEnd*/ ctx[32])), - listen(div, "mouseup", /*sliderInteractEnd*/ ctx[32]) + listen(div, "mouseup", /*sliderInteractEnd*/ ctx[32]), + listen(div, "touchstart", prevent_default(/*sliderInteractStart*/ ctx[31])), + listen(div, "touchend", prevent_default(/*sliderInteractEnd*/ ctx[32])) ]; mounted = true; @@ -1590,6 +1614,7 @@ let { handleFormatter = formatter } = $$props; let { precision = 2 } = $$props; let { springValues = { stiffness: 0.15, damping: 0.4 } } = $$props; + const dispatch = createEventDispatcher(); // dom references let slider; @@ -1601,6 +1626,8 @@ let handlePressed = false; let keyboardActive = false; let activeHandle = values.length - 1; + let startValue; + let previousValue; // save spring-tweened copies of the values for use // when changing values and animating the handle/range nicely @@ -1750,6 +1777,13 @@ // set the value for the handle, and align/clamp it $$invalidate(0, values[index] = value, values); + + // fire the change event when the handle moves, + // and store the previous value for the next time + if (previousValue !== alignValueToStep(value)) { + eChange(); + previousValue = alignValueToStep(value); + } } /** @@ -1859,6 +1893,11 @@ $$invalidate(21, handlePressed = true); $$invalidate(22, activeHandle = getClosestHandle(clientPos)); + // fire the start event + startValue = values[activeHandle]; + + eStart(); + // for touch devices we want the handle to instantly // move to the position touched for more responsive feeling if (e.type === "touchstart") { @@ -1872,6 +1911,11 @@ * @param {event} e the event from browser **/ function sliderInteractEnd(e) { + // fire the stop event for touch devices + if (e.type === "touchend") { + eStop(); + } + $$invalidate(21, handlePressed = false); } @@ -1911,12 +1955,18 @@ // this only works if a handle is active, which can // only happen if there was sliderInteractStart triggered // on the slider, already - if (handleActivated && (el === slider || slider.contains(el))) { - $$invalidate(20, focus = true); + if (handleActivated) { + if (el === slider || slider.contains(el)) { + $$invalidate(20, focus = true); - if (!targetIsHandle(el)) { - handleInteract(normalisedClient(e)); + if (!targetIsHandle(el)) { + handleInteract(normalisedClient(e)); + } } + + // fire the stop event for mouse device + // when the body is triggered with an active handle + eStop(); } handleActivated = false; @@ -1939,6 +1989,32 @@ } } + function eStart() { + dispatch("start", { + activeHandle, + value: alignValueToStep(startValue), + values: values.map(v => alignValueToStep(v)) + }); + } + + function eStop() { + dispatch("stop", { + activeHandle, + startValue: alignValueToStep(startValue), + value: alignValueToStep(values[activeHandle]), + values: values.map(v => alignValueToStep(v)) + }); + } + + function eChange() { + dispatch("change", { + activeHandle, + previousValue: alignValueToStep(previousValue) || alignValueToStep(startValue) || alignValueToStep(values[activeHandle]), + value: alignValueToStep(values[activeHandle]), + values: values.map(v => alignValueToStep(v)) + }); + } + function div_binding($$value) { binding_callbacks[$$value ? "unshift" : "push"](() => { slider = $$value; @@ -1983,20 +2059,20 @@ * @param {number} val the value to clamp * @return {number} the value after it's been clamped **/ - $$invalidate(45, clampValue = function (val) { + $$invalidate(47, clampValue = function (val) { // return the min/max if outside of that range return val <= min ? min : val >= max ? max : val; }); } - if ($$self.$$.dirty[0] & /*min, max, step*/ 28 | $$self.$$.dirty[1] & /*clampValue, precision*/ 16640) { + if ($$self.$$.dirty[0] & /*min, max, step*/ 28 | $$self.$$.dirty[1] & /*clampValue, precision*/ 65792) { /** * align the value with the steps so that it * always sits on the closest (above/below) step * @param {number} val the value to align * @return {number} the value after it's been aligned **/ - $$invalidate(44, alignValueToStep = function (val) { + $$invalidate(46, alignValueToStep = function (val) { // sanity check for performance if (val <= min) { return min; @@ -2025,7 +2101,7 @@ }); } - if ($$self.$$.dirty[0] & /*values*/ 1 | $$self.$$.dirty[1] & /*alignValueToStep*/ 8192) { + if ($$self.$$.dirty[0] & /*values*/ 1 | $$self.$$.dirty[1] & /*alignValueToStep*/ 32768) { // watch the values array, and trim / clamp the values to the steps // and boundaries set up in the slider on change $$invalidate(0, values = trimRange(values).map(v => alignValueToStep(v))); diff --git a/dist/svelte-range-slider-pips.mjs b/dist/svelte-range-slider-pips.mjs index 0ef4968..09b7d78 100644 --- a/dist/svelte-range-slider-pips.mjs +++ b/dist/svelte-range-slider-pips.mjs @@ -1,7 +1,7 @@ /** - * svelte-range-slider-pips ~ 1.5.3 + * svelte-range-slider-pips ~ 1.6.0 * Multi-Thumb, Accessible, Beautiful Range Slider with Pips - * © MPL-2.0 ~ Simon Goellner ~ 7/1/2021 + * © MPL-2.0 ~ Simon Goellner ~ 16/1/2021 */ function noop() { } function run(fn) { @@ -120,11 +120,35 @@ function set_data(text, data) { function toggle_class(element, name, toggle) { element.classList[toggle ? 'add' : 'remove'](name); } +function custom_event(type, detail) { + const e = document.createEvent('CustomEvent'); + e.initCustomEvent(type, false, false, detail); + return e; +} let current_component; function set_current_component(component) { current_component = component; } +function get_current_component() { + if (!current_component) + throw new Error(`Function called outside component initialization`); + return current_component; +} +function createEventDispatcher() { + const component = get_current_component(); + return (type, detail) => { + const callbacks = component.$$.callbacks[type]; + if (callbacks) { + // TODO are there situations where events could be dispatched + // in a server (non-DOM) environment? + const event = custom_event(type, detail); + callbacks.slice().forEach(fn => { + fn.call(component, event); + }); + } + }; +} const dirty_components = []; const binding_callbacks = []; @@ -1098,16 +1122,16 @@ function add_css$1() { function get_each_context$1(ctx, list, i) { const child_ctx = ctx.slice(); - child_ctx[52] = list[i]; - child_ctx[54] = i; + child_ctx[58] = list[i]; + child_ctx[60] = i; return child_ctx; } -// (671:6) {#if float} +// (724:6) {#if float} function create_if_block_2$1(ctx) { let span; let t0; - let t1_value = /*handleFormatter*/ ctx[18](/*value*/ ctx[52]) + ""; + let t1_value = /*handleFormatter*/ ctx[18](/*value*/ ctx[58]) + ""; let t1; let t2; @@ -1127,7 +1151,7 @@ function create_if_block_2$1(ctx) { }, p(ctx, dirty) { if (dirty[0] & /*prefix*/ 32768) set_data(t0, /*prefix*/ ctx[15]); - if (dirty[0] & /*handleFormatter, values*/ 262145 && t1_value !== (t1_value = /*handleFormatter*/ ctx[18](/*value*/ ctx[52]) + "")) set_data(t1, t1_value); + if (dirty[0] & /*handleFormatter, values*/ 262145 && t1_value !== (t1_value = /*handleFormatter*/ ctx[18](/*value*/ ctx[58]) + "")) set_data(t1, t1_value); if (dirty[0] & /*suffix*/ 65536) set_data(t2, /*suffix*/ ctx[16]); }, d(detaching) { @@ -1136,7 +1160,7 @@ function create_if_block_2$1(ctx) { }; } -// (653:2) {#each values as value, index} +// (706:2) {#each values as value, index} function create_each_block$1(ctx) { let span1; let span0; @@ -1161,22 +1185,22 @@ function create_each_block$1(ctx) { attr(span1, "role", "slider"); attr(span1, "class", "rangeHandle"); attr(span1, "tabindex", "0"); - 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) + ";")); + 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) + ";")); - attr(span1, "aria-valuemin", span1_aria_valuemin_value = /*range*/ ctx[1] === true && /*index*/ ctx[54] === 1 + attr(span1, "aria-valuemin", span1_aria_valuemin_value = /*range*/ ctx[1] === true && /*index*/ ctx[60] === 1 ? /*values*/ ctx[0][0] : /*min*/ ctx[2]); - attr(span1, "aria-valuemax", span1_aria_valuemax_value = /*range*/ ctx[1] === true && /*index*/ ctx[54] === 0 + attr(span1, "aria-valuemax", span1_aria_valuemax_value = /*range*/ ctx[1] === true && /*index*/ ctx[60] === 0 ? /*values*/ ctx[0][1] : /*max*/ ctx[3]); - attr(span1, "aria-valuenow", span1_aria_valuenow_value = /*value*/ ctx[52]); - attr(span1, "aria-valuetext", span1_aria_valuetext_value = "" + (/*prefix*/ ctx[15] + /*handleFormatter*/ ctx[18](/*value*/ ctx[52]) + /*suffix*/ ctx[16])); + attr(span1, "aria-valuenow", span1_aria_valuenow_value = /*value*/ ctx[58]); + attr(span1, "aria-valuetext", span1_aria_valuetext_value = "" + (/*prefix*/ ctx[15] + /*handleFormatter*/ ctx[18](/*value*/ ctx[58]) + /*suffix*/ ctx[16])); attr(span1, "aria-orientation", span1_aria_orientation_value = /*vertical*/ ctx[5] ? "vertical" : "horizontal"); toggle_class(span1, "hoverable", /*hover*/ ctx[7]); - toggle_class(span1, "active", /*focus*/ ctx[20] && /*activeHandle*/ ctx[22] === /*index*/ ctx[54]); - toggle_class(span1, "press", /*handlePressed*/ ctx[21] && /*activeHandle*/ ctx[22] === /*index*/ ctx[54]); + toggle_class(span1, "active", /*focus*/ ctx[20] && /*activeHandle*/ ctx[22] === /*index*/ ctx[60]); + toggle_class(span1, "press", /*handlePressed*/ ctx[21] && /*activeHandle*/ ctx[22] === /*index*/ ctx[60]); }, m(target, anchor) { insert(target, span1, anchor); @@ -1208,27 +1232,27 @@ function create_each_block$1(ctx) { if_block = null; } - 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) + ";"))) { + 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) + ";"))) { attr(span1, "style", span1_style_value); } - if (dirty[0] & /*range, values, min*/ 7 && span1_aria_valuemin_value !== (span1_aria_valuemin_value = /*range*/ ctx[1] === true && /*index*/ ctx[54] === 1 + if (dirty[0] & /*range, values, min*/ 7 && span1_aria_valuemin_value !== (span1_aria_valuemin_value = /*range*/ ctx[1] === true && /*index*/ ctx[60] === 1 ? /*values*/ ctx[0][0] : /*min*/ ctx[2])) { attr(span1, "aria-valuemin", span1_aria_valuemin_value); } - if (dirty[0] & /*range, values, max*/ 11 && span1_aria_valuemax_value !== (span1_aria_valuemax_value = /*range*/ ctx[1] === true && /*index*/ ctx[54] === 0 + if (dirty[0] & /*range, values, max*/ 11 && span1_aria_valuemax_value !== (span1_aria_valuemax_value = /*range*/ ctx[1] === true && /*index*/ ctx[60] === 0 ? /*values*/ ctx[0][1] : /*max*/ ctx[3])) { attr(span1, "aria-valuemax", span1_aria_valuemax_value); } - if (dirty[0] & /*values*/ 1 && span1_aria_valuenow_value !== (span1_aria_valuenow_value = /*value*/ ctx[52])) { + if (dirty[0] & /*values*/ 1 && span1_aria_valuenow_value !== (span1_aria_valuenow_value = /*value*/ ctx[58])) { attr(span1, "aria-valuenow", span1_aria_valuenow_value); } - 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]))) { + 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]))) { attr(span1, "aria-valuetext", span1_aria_valuetext_value); } @@ -1241,11 +1265,11 @@ function create_each_block$1(ctx) { } if (dirty[0] & /*focus, activeHandle*/ 5242880) { - toggle_class(span1, "active", /*focus*/ ctx[20] && /*activeHandle*/ ctx[22] === /*index*/ ctx[54]); + toggle_class(span1, "active", /*focus*/ ctx[20] && /*activeHandle*/ ctx[22] === /*index*/ ctx[60]); } if (dirty[0] & /*handlePressed, activeHandle*/ 6291456) { - toggle_class(span1, "press", /*handlePressed*/ ctx[21] && /*activeHandle*/ ctx[22] === /*index*/ ctx[54]); + toggle_class(span1, "press", /*handlePressed*/ ctx[21] && /*activeHandle*/ ctx[22] === /*index*/ ctx[60]); } }, d(detaching) { @@ -1257,7 +1281,7 @@ function create_each_block$1(ctx) { }; } -// (676:2) {#if range} +// (729:2) {#if range} function create_if_block_1$1(ctx) { let span; let span_style_value; @@ -1282,7 +1306,7 @@ function create_if_block_1$1(ctx) { }; } -// (682:2) {#if pips} +// (735:2) {#if pips} function create_if_block$1(ctx) { let rangepips; let current; @@ -1413,10 +1437,10 @@ function create_fragment$1(ctx) { listen(window, "mouseup", /*bodyMouseUp*/ ctx[35]), listen(window, "touchend", /*bodyTouchEnd*/ ctx[36]), listen(window, "keydown", /*bodyKeyDown*/ ctx[37]), - listen(div, "touchstart", prevent_default(/*sliderInteractStart*/ ctx[31])), listen(div, "mousedown", /*sliderInteractStart*/ ctx[31]), - listen(div, "touchend", prevent_default(/*sliderInteractEnd*/ ctx[32])), - listen(div, "mouseup", /*sliderInteractEnd*/ ctx[32]) + listen(div, "mouseup", /*sliderInteractEnd*/ ctx[32]), + listen(div, "touchstart", prevent_default(/*sliderInteractStart*/ ctx[31])), + listen(div, "touchend", prevent_default(/*sliderInteractEnd*/ ctx[32])) ]; mounted = true; @@ -1584,6 +1608,7 @@ function instance$1($$self, $$props, $$invalidate) { let { handleFormatter = formatter } = $$props; let { precision = 2 } = $$props; let { springValues = { stiffness: 0.15, damping: 0.4 } } = $$props; + const dispatch = createEventDispatcher(); // dom references let slider; @@ -1595,6 +1620,8 @@ function instance$1($$self, $$props, $$invalidate) { let handlePressed = false; let keyboardActive = false; let activeHandle = values.length - 1; + let startValue; + let previousValue; // save spring-tweened copies of the values for use // when changing values and animating the handle/range nicely @@ -1744,6 +1771,13 @@ function instance$1($$self, $$props, $$invalidate) { // set the value for the handle, and align/clamp it $$invalidate(0, values[index] = value, values); + + // fire the change event when the handle moves, + // and store the previous value for the next time + if (previousValue !== alignValueToStep(value)) { + eChange(); + previousValue = alignValueToStep(value); + } } /** @@ -1853,6 +1887,11 @@ function instance$1($$self, $$props, $$invalidate) { $$invalidate(21, handlePressed = true); $$invalidate(22, activeHandle = getClosestHandle(clientPos)); + // fire the start event + startValue = values[activeHandle]; + + eStart(); + // for touch devices we want the handle to instantly // move to the position touched for more responsive feeling if (e.type === "touchstart") { @@ -1866,6 +1905,11 @@ function instance$1($$self, $$props, $$invalidate) { * @param {event} e the event from browser **/ function sliderInteractEnd(e) { + // fire the stop event for touch devices + if (e.type === "touchend") { + eStop(); + } + $$invalidate(21, handlePressed = false); } @@ -1905,12 +1949,18 @@ function instance$1($$self, $$props, $$invalidate) { // this only works if a handle is active, which can // only happen if there was sliderInteractStart triggered // on the slider, already - if (handleActivated && (el === slider || slider.contains(el))) { - $$invalidate(20, focus = true); + if (handleActivated) { + if (el === slider || slider.contains(el)) { + $$invalidate(20, focus = true); - if (!targetIsHandle(el)) { - handleInteract(normalisedClient(e)); + if (!targetIsHandle(el)) { + handleInteract(normalisedClient(e)); + } } + + // fire the stop event for mouse device + // when the body is triggered with an active handle + eStop(); } handleActivated = false; @@ -1933,6 +1983,32 @@ function instance$1($$self, $$props, $$invalidate) { } } + function eStart() { + dispatch("start", { + activeHandle, + value: alignValueToStep(startValue), + values: values.map(v => alignValueToStep(v)) + }); + } + + function eStop() { + dispatch("stop", { + activeHandle, + startValue: alignValueToStep(startValue), + value: alignValueToStep(values[activeHandle]), + values: values.map(v => alignValueToStep(v)) + }); + } + + function eChange() { + dispatch("change", { + activeHandle, + previousValue: alignValueToStep(previousValue) || alignValueToStep(startValue) || alignValueToStep(values[activeHandle]), + value: alignValueToStep(values[activeHandle]), + values: values.map(v => alignValueToStep(v)) + }); + } + function div_binding($$value) { binding_callbacks[$$value ? "unshift" : "push"](() => { slider = $$value; @@ -1977,20 +2053,20 @@ function instance$1($$self, $$props, $$invalidate) { * @param {number} val the value to clamp * @return {number} the value after it's been clamped **/ - $$invalidate(45, clampValue = function (val) { + $$invalidate(47, clampValue = function (val) { // return the min/max if outside of that range return val <= min ? min : val >= max ? max : val; }); } - if ($$self.$$.dirty[0] & /*min, max, step*/ 28 | $$self.$$.dirty[1] & /*clampValue, precision*/ 16640) { + if ($$self.$$.dirty[0] & /*min, max, step*/ 28 | $$self.$$.dirty[1] & /*clampValue, precision*/ 65792) { /** * align the value with the steps so that it * always sits on the closest (above/below) step * @param {number} val the value to align * @return {number} the value after it's been aligned **/ - $$invalidate(44, alignValueToStep = function (val) { + $$invalidate(46, alignValueToStep = function (val) { // sanity check for performance if (val <= min) { return min; @@ -2019,7 +2095,7 @@ function instance$1($$self, $$props, $$invalidate) { }); } - if ($$self.$$.dirty[0] & /*values*/ 1 | $$self.$$.dirty[1] & /*alignValueToStep*/ 8192) { + if ($$self.$$.dirty[0] & /*values*/ 1 | $$self.$$.dirty[1] & /*alignValueToStep*/ 32768) { // watch the values array, and trim / clamp the values to the steps // and boundaries set up in the slider on change $$invalidate(0, values = trimRange(values).map(v => alignValueToStep(v))); diff --git a/package.json b/package.json index b06beb7..c160b43 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "svelte-range-slider-pips", - "version": "1.5.3", + "version": "1.6.0", "svelte": "src/index.js", "module": "dist/svelte-range-slider-pips.mjs", "main": "dist/svelte-range-slider-pips.js", diff --git a/src/RangeSlider.svelte b/src/RangeSlider.svelte index 68bd398..45534ed 100644 --- a/src/RangeSlider.svelte +++ b/src/RangeSlider.svelte @@ -1,5 +1,6 @@ \ No newline at end of file diff --git a/test/yarn.lock b/test/yarn.lock index 9c71dd4..7b75e6d 100644 --- a/test/yarn.lock +++ b/test/yarn.lock @@ -554,15 +554,10 @@ supports-color@^6.1.0: dependencies: has-flag "^3.0.0" -svelte-range-slider-pips@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/svelte-range-slider-pips/-/svelte-range-slider-pips-1.3.1.tgz#f7fdf7c5aaeb2ba39dd7eda7594299b8c894cefe" - integrity sha512-yIpLof5H46yQTydI9zQzOjKHHdsDnSzxqZVDBA3jl8hbT3B13N4Zp9lvp+SqF/nq0RL/NlmmUFQRyHobhncxpA== - -svelte@^3.0.0: - version "3.24.0" - resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.24.0.tgz#6565a42c9705796fa66c6abb4fedc09f4323a4a8" - integrity sha512-VFXom6EP2DK83kxy4ZlBbaZklSbZIrpNH3oNXlPYHJUuW4q1OuAr3ZoYbfIVTVYPDgrI7Yq0gQcOhDlAtO4qfw== +svelte@^3.31.2: + version "3.31.2" + resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.31.2.tgz#d2ddf6cacbb95e4cc3796207510b660a25586324" + integrity sha512-TxZGrXzX2ggFH3BIKY5fmbeMdJuZrMIMDYPMX6R9255bueuYIuVaBQSLUeY2oD7W4IdeqRZiAVGCjDw2POKBRA== terser@^4.6.2: version "4.8.0"