Commit 0df94dd4 authored by Valentin Hervieu's avatar Valentin Hervieu

feat(keyboardSupport): Consolidate onStart, onChange and onEnd for keyboard

Handle slideEnded event as well

Closes #319
parent 5d9c66a5
/*! angularjs-slider - v2.14.0 -
(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-05-22 */
2016-05-25 */
rzslider {
position: relative;
display: inline-block;
......
/*! angularjs-slider - v2.14.0 -
(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-05-22 */
2016-05-25 */
/*jslint unparam: true */
/*global angular: false, console: false, define, module */
(function(root, factory) {
......@@ -267,6 +267,13 @@
*/
this.initHasRun = false;
/**
* Used to call onStart on the first keydown event
*
* @type {boolean}
*/
this.firstKeyDown = false
/**
* Internal flag to prevent watchers to be called when the sliders value are modified internally.
* @type {boolean}
......@@ -1519,7 +1526,6 @@
this.dragging.active = false;
$document.off(moveEventName, ehMove);
this.scope.$emit('slideEnded');
this.callOnEnd();
},
......@@ -1531,11 +1537,19 @@
this.tracking = ref;
pointer.one('blur', angular.bind(this, this.onPointerBlur, pointer));
pointer.on('keydown', angular.bind(this, this.onKeyboardEvent));
pointer.on('keyup', angular.bind(this, this.onKeyUp));
this.firstKeyDown = true;
pointer.addClass('rz-active');
},
onKeyUp: function() {
this.firstKeyDown = true;
this.callOnEnd();
},
onPointerBlur: function(pointer) {
pointer.off('keydown');
pointer.off('keyup');
this.tracking = '';
pointer.removeClass('rz-active');
},
......@@ -1596,29 +1610,37 @@
if (action == null || this.tracking === '') return;
event.preventDefault();
var newValue = this.roundStep(this.sanitizeValue(action));
if (!this.options.draggableRangeOnly) {
this.positionTrackingHandle(newValue);
} else {
var difference = this.scope.rzSliderHigh - this.scope.rzSliderModel,
newMinValue, newMaxValue;
if (this.tracking === 'rzSliderModel') {
newMinValue = newValue;
newMaxValue = newValue + difference;
if (newMaxValue > this.maxValue) {
newMaxValue = this.maxValue;
newMinValue = newMaxValue - difference;
}
if (this.firstKeyDown) {
this.firstKeyDown = false;
this.callOnStart();
}
var self = this;
$timeout(function() {
var newValue = self.roundStep(self.sanitizeValue(action));
if (!self.options.draggableRangeOnly) {
self.positionTrackingHandle(newValue);
} else {
newMaxValue = newValue;
newMinValue = newValue - difference;
if (newMinValue < this.minValue) {
newMinValue = this.minValue;
newMaxValue = newMinValue + difference;
var difference = self.scope.rzSliderHigh - self.scope.rzSliderModel,
newMinValue, newMaxValue;
if (self.tracking === 'rzSliderModel') {
newMinValue = newValue;
newMaxValue = newValue + difference;
if (newMaxValue > self.maxValue) {
newMaxValue = self.maxValue;
newMinValue = newMaxValue - difference;
}
} else {
newMaxValue = newValue;
newMinValue = newValue - difference;
if (newMinValue < self.minValue) {
newMinValue = self.minValue;
newMaxValue = newMinValue + difference;
}
}
self.positionTrackingBar(newMinValue, newMaxValue);
}
this.positionTrackingBar(newMinValue, newMaxValue);
}
});
},
/**
......@@ -1889,6 +1911,7 @@
self.options.onEnd(self.options.id, self.scope.rzSliderModel, self.scope.rzSliderHigh);
});
}
this.scope.$emit('slideEnded');
}
};
......
/*! angularjs-slider - v2.14.0 - (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-05-22 */
/*! angularjs-slider - v2.14.0 - (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-05-25 */
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.
......@@ -271,6 +271,13 @@
*/
this.initHasRun = false;
/**
* Used to call onStart on the first keydown event
*
* @type {boolean}
*/
this.firstKeyDown = false
/**
* Internal flag to prevent watchers to be called when the sliders value are modified internally.
* @type {boolean}
......@@ -1523,7 +1530,6 @@
this.dragging.active = false;
$document.off(moveEventName, ehMove);
this.scope.$emit('slideEnded');
this.callOnEnd();
},
......@@ -1535,11 +1541,19 @@
this.tracking = ref;
pointer.one('blur', angular.bind(this, this.onPointerBlur, pointer));
pointer.on('keydown', angular.bind(this, this.onKeyboardEvent));
pointer.on('keyup', angular.bind(this, this.onKeyUp));
this.firstKeyDown = true;
pointer.addClass('rz-active');
},
onKeyUp: function() {
this.firstKeyDown = true;
this.callOnEnd();
},
onPointerBlur: function(pointer) {
pointer.off('keydown');
pointer.off('keyup');
this.tracking = '';
pointer.removeClass('rz-active');
},
......@@ -1600,29 +1614,37 @@
if (action == null || this.tracking === '') return;
event.preventDefault();
var newValue = this.roundStep(this.sanitizeValue(action));
if (!this.options.draggableRangeOnly) {
this.positionTrackingHandle(newValue);
} else {
var difference = this.scope.rzSliderHigh - this.scope.rzSliderModel,
newMinValue, newMaxValue;
if (this.tracking === 'rzSliderModel') {
newMinValue = newValue;
newMaxValue = newValue + difference;
if (newMaxValue > this.maxValue) {
newMaxValue = this.maxValue;
newMinValue = newMaxValue - difference;
}
if (this.firstKeyDown) {
this.firstKeyDown = false;
this.callOnStart();
}
var self = this;
$timeout(function() {
var newValue = self.roundStep(self.sanitizeValue(action));
if (!self.options.draggableRangeOnly) {
self.positionTrackingHandle(newValue);
} else {
newMaxValue = newValue;
newMinValue = newValue - difference;
if (newMinValue < this.minValue) {
newMinValue = this.minValue;
newMaxValue = newMinValue + difference;
var difference = self.scope.rzSliderHigh - self.scope.rzSliderModel,
newMinValue, newMaxValue;
if (self.tracking === 'rzSliderModel') {
newMinValue = newValue;
newMaxValue = newValue + difference;
if (newMaxValue > self.maxValue) {
newMaxValue = self.maxValue;
newMinValue = newMaxValue - difference;
}
} else {
newMaxValue = newValue;
newMinValue = newValue - difference;
if (newMinValue < self.minValue) {
newMinValue = self.minValue;
newMaxValue = newMinValue + difference;
}
}
self.positionTrackingBar(newMinValue, newMaxValue);
}
this.positionTrackingBar(newMinValue, newMaxValue);
}
});
},
/**
......@@ -1893,6 +1915,7 @@
self.options.onEnd(self.options.id, self.scope.rzSliderModel, self.scope.rzSliderHigh);
});
}
this.scope.$emit('slideEnded');
}
};
......
......@@ -14,7 +14,7 @@
var optionsExpression = sliderObj.optionsExpression || 'slider.options';
if (sliderObj.options || sliderObj.optionsExpression)
template = '<rzslider rz-slider-model="slider.value" rz-slider-options="' +
optionsExpression + '"></rzslider>';
optionsExpression + '"></rzslider>';
else
template = '<rzslider rz-slider-model="slider.value"></rzslider>';
h.initSlider(sliderObj, template);
......@@ -25,7 +25,7 @@
var optionsExpression = sliderObj.optionsExpression || 'slider.options';
if (sliderObj.options || sliderObj.optionsExpression)
template = '<rzslider rz-slider-model="slider.min" rz-slider-high="slider.max"' +
'rz-slider-options="' + optionsExpression + '"></rzslider>';
'rz-slider-options="' + optionsExpression + '"></rzslider>';
else
template = '<rzslider rz-slider-model="slider.min" rz-slider-high="slider.max"></rzslider>';
h.initSlider(sliderObj, template);
......@@ -82,7 +82,8 @@
$document.triggerHandler(event);
};
h.pressKeydown = function(element, key, oldAPI) {
h.pressKeydown = function(element, key, options) {
options = options || {};
key = key.toUpperCase();
var event = {
type: 'keydown'
......@@ -99,10 +100,12 @@
'SPACE': 32
};
var keyCode = keys[key];
if (oldAPI)
if (options.oldAPI)
event.which = keyCode;
else event.keyCode = keyCode;
element.triggerHandler(event);
if (options.timeout !== false)
$timeout.flush();
};
return h;
......
......@@ -136,7 +136,7 @@
helper.pressKeydown(helper.slider.minH, 'RIGHT');
expect(helper.scope.slider.min).to.equal(51);
helper.slider.minH.triggerHandler('blur');
helper.pressKeydown(helper.slider.minH, 'RIGHT');
helper.pressKeydown(helper.slider.minH, 'RIGHT', {timeout: false});
expect(helper.scope.slider.min).to.equal(51);
});
......@@ -145,7 +145,7 @@
helper.pressKeydown(helper.slider.maxH, 'RIGHT');
expect(helper.scope.slider.max).to.equal(101);
helper.slider.maxH.triggerHandler('blur');
helper.pressKeydown(helper.slider.maxH, 'RIGHT');
helper.pressKeydown(helper.slider.maxH, 'RIGHT', {timeout: false});
expect(helper.scope.slider.max).to.equal(101);
});
});
......@@ -273,7 +273,7 @@
helper.pressKeydown(helper.slider.minH, 'RIGHT');
expect(helper.scope.slider.min).to.equal(49);
helper.slider.minH.triggerHandler('blur');
helper.pressKeydown(helper.slider.minH, 'RIGHT');
helper.pressKeydown(helper.slider.minH, 'RIGHT', {timeout: false});
expect(helper.scope.slider.min).to.equal(49);
});
......@@ -282,7 +282,7 @@
helper.pressKeydown(helper.slider.maxH, 'RIGHT');
expect(helper.scope.slider.max).to.equal(99);
helper.slider.maxH.triggerHandler('blur');
helper.pressKeydown(helper.slider.maxH, 'RIGHT');
helper.pressKeydown(helper.slider.maxH, 'RIGHT', {timeout: false});
expect(helper.scope.slider.max).to.equal(99);
});
......
......@@ -25,12 +25,46 @@
value: 100,
options: {
floor: 0,
ceil: 200
ceil: 200,
onStart: sinon.spy(),
onChange: sinon.spy(),
onEnd: sinon.spy()
}
};
helper.createSlider(sliderConf);
});
it('should call onStart on the first keydown but not after', function() {
helper.slider.minH.triggerHandler('focus');
helper.pressKeydown(helper.slider.minH, 'RIGHT');
helper.scope.slider.options.onStart.callCount === 1;
helper.pressKeydown(helper.slider.minH, 'RIGHT');
helper.scope.slider.options.onStart.callCount === 1;
});
it('should call onChange on each keydown but after a timeout', function() {
helper.slider.minH.triggerHandler('focus');
helper.pressKeydown(helper.slider.minH, 'RIGHT', {timeout: false});
$timeout.flush();
helper.scope.slider.options.onChange.callCount === 1;
helper.pressKeydown(helper.slider.minH, 'RIGHT', {timeout: false});
$timeout.flush();
helper.scope.slider.options.onChange.callCount === 1;
});
it('should call onEnd on keyup and recall onStart if key is down again', function() {
helper.slider.minH.triggerHandler('focus');
helper.pressKeydown(helper.slider.minH, 'RIGHT');
helper.slider.minH.triggerHandler({type: 'keyup'});
helper.scope.slider.options.onStart.callCount === 1;
helper.scope.slider.options.onEnd.callCount === 1;
helper.pressKeydown(helper.slider.minH, 'RIGHT');
helper.slider.minH.triggerHandler({type: 'keyup'});
helper.scope.slider.options.onStart.callCount === 2;
helper.scope.slider.options.onEnd.callCount === 2;
});
it('should toggle active style when handle focused/blured', function() {
helper.slider.minH.triggerHandler('focus');
expect(helper.slider.minH.hasClass('rz-active')).to.be.true;
......@@ -46,7 +80,7 @@
it('should increment by 1 when RIGHT is pressed with oldAPI', function() {
helper.slider.minH.triggerHandler('focus');
helper.pressKeydown(helper.slider.minH, 'RIGHT', true);
helper.pressKeydown(helper.slider.minH, 'RIGHT', {oldAPI: true});
expect(helper.scope.slider.value).to.equal(101);
});
......@@ -103,7 +137,7 @@
helper.pressKeydown(helper.slider.minH, 'RIGHT');
expect(helper.scope.slider.value).to.equal(101);
helper.slider.minH.triggerHandler('blur');
helper.pressKeydown(helper.slider.minH, 'RIGHT');
helper.pressKeydown(helper.slider.minH, 'RIGHT', {timeout: false});
expect(helper.scope.slider.value).to.equal(101);
});
});
......@@ -211,7 +245,7 @@
helper.pressKeydown(helper.slider.minH, 'RIGHT');
expect(helper.scope.slider.value).to.equal(99);
helper.slider.minH.triggerHandler('blur');
helper.pressKeydown(helper.slider.minH, 'RIGHT');
helper.pressKeydown(helper.slider.minH, 'RIGHT', {timeout: false});
expect(helper.scope.slider.value).to.equal(99);
});
});
......
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