Commit b725812d authored by Valentin Hervieu's avatar Valentin Hervieu Committed by GitHub

feat(range): Add a pushRange option (#341)

As described in #311
parent 6d479afd
# 5.1.0 (2016-07-02)
## Features
- Add a `pushRange` option (#341).
# 5.0.1 (2016-07-01)
## Fix
- Switch from using opacity to visibility to show/hide elements (#362).
......
......@@ -190,6 +190,7 @@ The default options are:
maxLimit: null,
minRange: null,
maxRange: null,
pushRange: false,
id: null,
translate: null,
getLegend: null,
......@@ -243,6 +244,8 @@ The default options are:
**maxRange** - _Number (defaults to null)_: The maximum range authorized on the slider. *Applies to range slider only.*
**pushRange** - _Boolean (defaults to false)_: Set to true to have a push behavior. When the min handle goes above the max, the max is moved as well (and vice-versa). The range between min and max is defined by the `step` option (defaults to 1) and can also be override by the `minRange` option. *Applies to range slider only.*
**translate** - _Function(value, sliderId, label)_: Custom translate function. Use this if you want to translate values displayed on the slider.
`sliderId` can be used to determine the slider for which we are translating the value. `label` is a string that can take the following values:
- *'model'*: the model label
......
......@@ -54,6 +54,18 @@ app.controller('MainCtrl', function($scope, $rootScope, $timeout, $modal) {
}
};
//Range slider with minRange and pushRange config
$scope.minPushRangeSlider = {
minValue: 40,
maxValue: 60,
options: {
floor: 0,
ceil: 100,
minRange: 10,
pushRange: true
}
};
//Slider with selection bar
$scope.slider_visible_bar = {
value: 10,
......
......@@ -62,6 +62,15 @@
></rzslider>
</article>
<article>
<h2>Range slider with minimum range of 10 and pushRange option</h2>
<rzslider
rz-slider-model="minPushRangeSlider.minValue"
rz-slider-high="minPushRangeSlider.maxValue"
rz-slider-options="minPushRangeSlider.options"
></rzslider>
</article>
<article>
<h2>Slider with visible selection bar</h2>
<rzslider
......
/*! angularjs-slider - v5.0.1 -
(c) Rafal Zajac <rzajac@gmail.com>, Valentin Hervieu <valentin@hervieu.me>, Jussi Saarivirta <jusasi@gmail.com>, Angelin Sirbu <angelin.sirbu@gmail.com> -
https://github.com/angular-slider/angularjs-slider -
2016-07-01 */
2016-07-02 */
.rzslider {
position: relative;
display: inline-block;
......
/*! angularjs-slider - v5.0.1 -
(c) Rafal Zajac <rzajac@gmail.com>, Valentin Hervieu <valentin@hervieu.me>, Jussi Saarivirta <jusasi@gmail.com>, Angelin Sirbu <angelin.sirbu@gmail.com> -
https://github.com/angular-slider/angularjs-slider -
2016-07-01 */
2016-07-02 */
/*jslint unparam: true */
/*global angular: false, console: false, define, module */
(function(root, factory) {
......@@ -33,6 +33,7 @@
precision: 0,
minRange: null,
maxRange: null,
pushRange: false,
minLimit: null,
maxLimit: null,
id: null,
......@@ -1906,40 +1907,47 @@
newValue = this.applyMinMaxLimit(newValue);
if (this.range) {
newValue = this.applyMinMaxRange(newValue);
/* This is to check if we need to switch the min and max handles */
if (this.tracking === 'lowValue' && newValue > this.highValue) {
if (this.options.noSwitching && this.highValue !== this.minValue) {
newValue = this.applyMinMaxRange(this.highValue);
}
else {
this.lowValue = this.highValue;
this.applyLowValue();
this.updateHandles(this.tracking, this.maxH.rzsp);
this.updateAriaAttributes();
this.tracking = 'highValue';
this.minH.removeClass('rz-active');
this.maxH.addClass('rz-active');
if (this.options.keyboardSupport)
this.focusElement(this.maxH);
}
if (this.options.pushRange) {
newValue = this.applyPushRange(newValue);
valueChanged = true;
} else if (this.tracking === 'highValue' && newValue < this.lowValue) {
if (this.options.noSwitching && this.lowValue !== this.maxValue) {
newValue = this.applyMinMaxRange(this.lowValue);
}
else {
newValue = this.applyMinMaxRange(newValue);
/* This is to check if we need to switch the min and max handles */
if (this.tracking === 'lowValue' && newValue > this.highValue) {
if (this.options.noSwitching && this.highValue !== this.minValue) {
newValue = this.applyMinMaxRange(this.highValue);
}
else {
this.lowValue = this.highValue;
this.applyLowValue();
this.updateHandles(this.tracking, this.maxH.rzsp);
this.updateAriaAttributes();
this.tracking = 'highValue';
this.minH.removeClass('rz-active');
this.maxH.addClass('rz-active');
if (this.options.keyboardSupport)
this.focusElement(this.maxH);
}
valueChanged = true;
}
else {
this.highValue = this.lowValue;
this.applyHighValue();
this.updateHandles(this.tracking, this.minH.rzsp);
this.updateAriaAttributes();
this.tracking = 'lowValue';
this.maxH.removeClass('rz-active');
this.minH.addClass('rz-active');
if (this.options.keyboardSupport)
this.focusElement(this.minH);
else if (this.tracking === 'highValue' && newValue < this.lowValue) {
if (this.options.noSwitching && this.lowValue !== this.maxValue) {
newValue = this.applyMinMaxRange(this.lowValue);
}
else {
this.highValue = this.lowValue;
this.applyHighValue();
this.updateHandles(this.tracking, this.minH.rzsp);
this.updateAriaAttributes();
this.tracking = 'lowValue';
this.maxH.removeClass('rz-active');
this.minH.addClass('rz-active');
if (this.options.keyboardSupport)
this.focusElement(this.minH);
}
valueChanged = true;
}
valueChanged = true;
}
}
......@@ -1988,6 +1996,27 @@
return newValue;
},
applyPushRange: function(newValue) {
var difference = this.tracking === 'lowValue' ? this.highValue - newValue : newValue - this.lowValue,
range = this.options.minRange !== null ? this.options.minRange : this.options.step;
if (difference < range) {
if (this.tracking === 'lowValue') {
this.highValue = Math.min(newValue + range, this.maxValue);
newValue = this.highValue - range;
this.applyHighValue();
this.updateHandles('highValue', this.valueToOffset(this.highValue));
}
else {
this.lowValue = Math.max(newValue - range, this.minValue);
newValue = this.lowValue + range;
this.applyLowValue();
this.updateHandles('lowValue', this.valueToOffset(this.lowValue));
}
this.updateAriaAttributes();
}
return newValue;
},
/**
* Apply the model values using scope.$apply.
* We wrap it with the internalChange flag to avoid the watchers to be called
......
/*! angularjs-slider - v5.0.1 - (c) Rafal Zajac <rzajac@gmail.com>, Valentin Hervieu <valentin@hervieu.me>, Jussi Saarivirta <jusasi@gmail.com>, Angelin Sirbu <angelin.sirbu@gmail.com> - https://github.com/angular-slider/angularjs-slider - 2016-07-01 */
/*! angularjs-slider - v5.0.1 - (c) Rafal Zajac <rzajac@gmail.com>, Valentin Hervieu <valentin@hervieu.me>, Jussi Saarivirta <jusasi@gmail.com>, Angelin Sirbu <angelin.sirbu@gmail.com> - https://github.com/angular-slider/angularjs-slider - 2016-07-02 */
.rzslider{position:relative;display:inline-block;width:100%;height:4px;margin:35px 0 15px 0;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.rzslider.with-legend{margin-bottom:40px}.rzslider[disabled]{cursor:not-allowed}.rzslider[disabled] .rz-pointer{cursor:not-allowed;background-color:#d8e0f3}.rzslider span{position:absolute;display:inline-block;white-space:nowrap}.rzslider .rz-base{width:100%;height:100%;padding:0}.rzslider .rz-bar-wrapper{left:0;z-index:1;width:100%;height:32px;padding-top:16px;margin-top:-16px;box-sizing:border-box}.rzslider .rz-bar-wrapper.rz-draggable{cursor:move}.rzslider .rz-bar{left:0;z-index:1;width:100%;height:4px;background:#d8e0f3;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px}.rzslider .rz-bar.rz-selection{z-index:2;background:#0db9f0;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px}.rzslider .rz-pointer{top:-14px;z-index:3;width:32px;height:32px;cursor:pointer;background-color:#0db9f0;-webkit-border-radius:16px;-moz-border-radius:16px;border-radius:16px}.rzslider .rz-pointer:after{position:absolute;top:12px;left:12px;width:8px;height:8px;background:#fff;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;content:''}.rzslider .rz-pointer:hover:after{background-color:#fff}.rzslider .rz-pointer.rz-active{z-index:4}.rzslider .rz-pointer.rz-active:after{background-color:#451aff}.rzslider .rz-bubble{bottom:16px;padding:1px 3px;color:#55637d;cursor:default}.rzslider .rz-bubble.rz-selection{top:16px}.rzslider .rz-bubble.rz-limit{color:#55637d}.rzslider .rz-ticks{position:absolute;top:-3px;left:0;z-index:1;display:-webkit-flex;display:-ms-flexbox;display:flex;width:100%;height:0;padding:0 11px;margin:0;list-style:none;box-sizing:border-box;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between}.rzslider .rz-ticks .rz-tick{width:10px;height:10px;text-align:center;cursor:pointer;background:#d8e0f3;border-radius:50%}.rzslider .rz-ticks .rz-tick.rz-selected{background:#0db9f0}.rzslider .rz-ticks .rz-tick .rz-tick-value{position:absolute;top:-30px;transform:translate(-50%,0)}.rzslider .rz-ticks .rz-tick .rz-tick-legend{position:absolute;top:24px;max-width:50px;white-space:normal;transform:translate(-50%,0)}.rzslider .rz-ticks.rz-ticks-values-under .rz-tick-value{top:initial;bottom:-40px}.rzslider.rz-vertical{position:relative;width:4px;height:100%;padding:0;margin:0 20px;vertical-align:baseline}.rzslider.rz-vertical .rz-base{width:100%;height:100%;padding:0}.rzslider.rz-vertical .rz-bar-wrapper{top:auto;left:0;width:32px;height:100%;padding:0 0 0 16px;margin:0 0 0 -16px}.rzslider.rz-vertical .rz-bar{bottom:0;left:auto;width:4px;height:100%}.rzslider.rz-vertical .rz-pointer{top:auto;bottom:0;left:-14px!important}.rzslider.rz-vertical .rz-bubble{bottom:0;left:16px!important;margin-left:3px}.rzslider.rz-vertical .rz-bubble.rz-selection{top:auto;left:16px!important}.rzslider.rz-vertical .rz-ticks{top:0;left:-3px;z-index:1;width:0;height:100%;padding:11px 0;-webkit-flex-direction:column-reverse;-ms-flex-direction:column-reverse;flex-direction:column-reverse}.rzslider.rz-vertical .rz-ticks .rz-tick{vertical-align:middle}.rzslider.rz-vertical .rz-ticks .rz-tick .rz-tick-value{top:initial;left:22px;transform:translate(0,-28%)}.rzslider.rz-vertical .rz-ticks .rz-tick .rz-tick-legend{top:initial;right:24px;max-width:none;white-space:nowrap;transform:translate(0,-28%)}.rzslider.rz-vertical .rz-ticks.rz-ticks-values-under .rz-tick-value{right:12px;bottom:initial;left:initial}
\ No newline at end of file
This diff is collapsed.
......@@ -37,6 +37,7 @@
precision: 0,
minRange: null,
maxRange: null,
pushRange: false,
minLimit: null,
maxLimit: null,
id: null,
......@@ -1910,40 +1911,47 @@
newValue = this.applyMinMaxLimit(newValue);
if (this.range) {
newValue = this.applyMinMaxRange(newValue);
/* This is to check if we need to switch the min and max handles */
if (this.tracking === 'lowValue' && newValue > this.highValue) {
if (this.options.noSwitching && this.highValue !== this.minValue) {
newValue = this.applyMinMaxRange(this.highValue);
}
else {
this.lowValue = this.highValue;
this.applyLowValue();
this.updateHandles(this.tracking, this.maxH.rzsp);
this.updateAriaAttributes();
this.tracking = 'highValue';
this.minH.removeClass('rz-active');
this.maxH.addClass('rz-active');
if (this.options.keyboardSupport)
this.focusElement(this.maxH);
}
if (this.options.pushRange) {
newValue = this.applyPushRange(newValue);
valueChanged = true;
} else if (this.tracking === 'highValue' && newValue < this.lowValue) {
if (this.options.noSwitching && this.lowValue !== this.maxValue) {
newValue = this.applyMinMaxRange(this.lowValue);
}
else {
newValue = this.applyMinMaxRange(newValue);
/* This is to check if we need to switch the min and max handles */
if (this.tracking === 'lowValue' && newValue > this.highValue) {
if (this.options.noSwitching && this.highValue !== this.minValue) {
newValue = this.applyMinMaxRange(this.highValue);
}
else {
this.lowValue = this.highValue;
this.applyLowValue();
this.updateHandles(this.tracking, this.maxH.rzsp);
this.updateAriaAttributes();
this.tracking = 'highValue';
this.minH.removeClass('rz-active');
this.maxH.addClass('rz-active');
if (this.options.keyboardSupport)
this.focusElement(this.maxH);
}
valueChanged = true;
}
else {
this.highValue = this.lowValue;
this.applyHighValue();
this.updateHandles(this.tracking, this.minH.rzsp);
this.updateAriaAttributes();
this.tracking = 'lowValue';
this.maxH.removeClass('rz-active');
this.minH.addClass('rz-active');
if (this.options.keyboardSupport)
this.focusElement(this.minH);
else if (this.tracking === 'highValue' && newValue < this.lowValue) {
if (this.options.noSwitching && this.lowValue !== this.maxValue) {
newValue = this.applyMinMaxRange(this.lowValue);
}
else {
this.highValue = this.lowValue;
this.applyHighValue();
this.updateHandles(this.tracking, this.minH.rzsp);
this.updateAriaAttributes();
this.tracking = 'lowValue';
this.maxH.removeClass('rz-active');
this.minH.addClass('rz-active');
if (this.options.keyboardSupport)
this.focusElement(this.minH);
}
valueChanged = true;
}
valueChanged = true;
}
}
......@@ -1992,6 +2000,27 @@
return newValue;
},
applyPushRange: function(newValue) {
var difference = this.tracking === 'lowValue' ? this.highValue - newValue : newValue - this.lowValue,
range = this.options.minRange !== null ? this.options.minRange : this.options.step;
if (difference < range) {
if (this.tracking === 'lowValue') {
this.highValue = Math.min(newValue + range, this.maxValue);
newValue = this.highValue - range;
this.applyHighValue();
this.updateHandles('highValue', this.valueToOffset(this.highValue));
}
else {
this.lowValue = Math.max(newValue - range, this.minValue);
newValue = this.lowValue + range;
this.applyLowValue();
this.updateHandles('lowValue', this.valueToOffset(this.lowValue));
}
this.updateAriaAttributes();
}
return newValue;
},
/**
* Apply the model values using scope.$apply.
* We wrap it with the internalChange flag to avoid the watchers to be called
......
......@@ -108,6 +108,11 @@
$timeout.flush();
};
h.mouseMoveToValue = function(value) {
var offset = h.slider.valueToOffset(value) + h.slider.handleHalfDim + h.slider.sliderElem.rzsp;
h.fireMousemove(offset);
};
return h;
});
}());
(function() {
"use strict";
describe('Mouse controls - pushRange Range Horizontal', function() {
var helper,
RzSliderOptions,
$rootScope,
$timeout;
beforeEach(module('test-helper'));
beforeEach(inject(function(TestHelper, _RzSliderOptions_, _$rootScope_, _$timeout_) {
helper = TestHelper;
RzSliderOptions = _RzSliderOptions_;
$rootScope = _$rootScope_;
$timeout = _$timeout_;
}));
afterEach(function() {
helper.clean();
});
beforeEach(function() {
var sliderConf = {
min: 45,
max: 55,
options: {
floor: 0,
ceil: 100,
pushRange: true
}
};
helper.createRangeSlider(sliderConf);
});
afterEach(function() {
// to clean document listener
helper.fireMouseup();
});
it('should push maxH when moving minH above it', function() {
helper.fireMousedown(helper.slider.minH, 0);
helper.mouseMoveToValue(60);
expect(helper.scope.slider.min).to.equal(60);
expect(helper.scope.slider.max).to.equal(61);
});
it('should push minH when moving maxH below it', function() {
helper.fireMousedown(helper.slider.maxH, 0);
helper.mouseMoveToValue(40);
expect(helper.scope.slider.min).to.equal(39);
expect(helper.scope.slider.max).to.equal(40);
});
it('should not move maxH above ceil when moving minH to ceil', function() {
helper.fireMousedown(helper.slider.minH, 0);
helper.mouseMoveToValue(100);
expect(helper.scope.slider.min).to.equal(99);
expect(helper.scope.slider.max).to.equal(100);
});
it('should not move minH below floor when moving maxH to floor', function() {
helper.fireMousedown(helper.slider.maxH, 0);
helper.mouseMoveToValue(0);
expect(helper.scope.slider.min).to.equal(0);
expect(helper.scope.slider.max).to.equal(1);
});
it('should push maxH according to step', function() {
helper.scope.slider.options.step = 5;
helper.scope.$digest();
helper.fireMousedown(helper.slider.minH, 0);
helper.mouseMoveToValue(60);
expect(helper.scope.slider.min).to.equal(60);
expect(helper.scope.slider.max).to.equal(65);
});
it('should push minH according to step', function() {
helper.scope.slider.options.step = 5;
helper.scope.$digest();
helper.fireMousedown(helper.slider.maxH, 0);
helper.mouseMoveToValue(40);
expect(helper.scope.slider.min).to.equal(35);
expect(helper.scope.slider.max).to.equal(40);
});
it('should push maxH according to minRange when both step and minRange are defined', function() {
helper.scope.slider.options.step = 5;
helper.scope.slider.options.minRange = 10;
helper.scope.$digest();
helper.fireMousedown(helper.slider.minH, 0);
helper.mouseMoveToValue(60);
expect(helper.scope.slider.min).to.equal(60);
expect(helper.scope.slider.max).to.equal(70);
});
it('should push minH according to minRange when both step and minRange are defined', function() {
helper.scope.slider.options.step = 5;
helper.scope.slider.options.minRange = 10;
helper.scope.$digest();
helper.fireMousedown(helper.slider.maxH, 0);
helper.mouseMoveToValue(40);
expect(helper.scope.slider.min).to.equal(30);
expect(helper.scope.slider.max).to.equal(40);
});
it('should push maxH according to minRange when minRange is 0', function() {
helper.scope.slider.options.step = 5;
helper.scope.slider.options.minRange = 0;
helper.scope.$digest();
helper.fireMousedown(helper.slider.minH, 0);
helper.mouseMoveToValue(60);
expect(helper.scope.slider.min).to.equal(60);
expect(helper.scope.slider.max).to.equal(60);
});
it('should push minH according to minRange when minRange is 0', function() {
helper.scope.slider.options.step = 5;
helper.scope.slider.options.minRange = 0;
helper.scope.$digest();
helper.fireMousedown(helper.slider.maxH, 0);
helper.mouseMoveToValue(40);
expect(helper.scope.slider.min).to.equal(40);
expect(helper.scope.slider.max).to.equal(40);
});
});
}());
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment