Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
AngularJS Hold Button Directive with feedback
app.directive('holdButton', function($parse, $interval) {
//The framerate of the progress bar, progression will be evaluated every 5ms.
var tickDelay = 10;
var minFill = 5;
return {
restrict: 'A',
scope: {
holdButtonSuccess: '&'
}
,link: function postLink(scope, element, attrs) {
var started, completed, stop;
var engage = function($event){
var holdDelay = attrs.holdButtonDelay ? ($parse(attrs.holdButtonDelay)(scope) || 400) : 400;
var counter = 0;
var nbTick = holdDelay / tickDelay;
element.removeClass('hold-success', 'hold-failure');
element.addClass('holding');
completed = false;
var fillColor = !!attrs.holdButtonFillColor? attrs.holdButtonFillColor : '#A1A1A1';
element.css('background-image', 'linear-gradient(to right, ' + fillColor + ' ' + minFill + '%, transparent ' + minFill + '%)');
// Call the onTick function `nbTick` times every `tickDelay` ms.
// stop is the stopper function
stop = $interval(onTick, tickDelay, nbTick);
started = true;
function onTick() {
counter++;
var percentage = Math.max(Math.round(counter / nbTick * 100), minFill);
element.css('background-image', 'linear-gradient(to right, ' + fillColor + ' ' + percentage + '%, transparent ' + percentage + '%)');
// If we reach `nbTick` then we're done
if (counter === nbTick) {
if( !!scope.holdButtonSuccess ){
scope.holdButtonSuccess();
}
completed = true;
element.addClass('hold-success');
element.css('background-image', null);
}
}
};
var disengage = function($event){
// Prevent standard events for all interactions
$event.stopPropagation();
$interval.cancel(stop);
element.removeClass('holding');
element.css('background-image', null);
if( !started || completed ){
return;
}
element.addClass('hold-failure');
// TODO add a failure callback?
};
element.on('mousedown', function($event) {
engage($event);
});
element.on('mouseup', function($event) {
disengage($event);
});
element.on('mouseleave', function($event) {
disengage($event);
});
// Touch events
element.on('touchstart', function($event) {
engage($event);
});
element.on('touchend', function($event) {
disengage($event);
});
}
};
});
<button
hold-button
hold-button-success="holdAction()" <!-- this should be a $scope function, optionally with parameters -->
hold-button-delay="" <!-- number of milliseconds required, default 400 -->
hold-button-fill-color="" <!-- any acceptable css color string -->
>Hold to Activate
</button>
describe('hold buttons', function(){
// You'll need to write the page definition yourself
var holdButton = page.getHoldButton();
it('should give feedback on interaction', function(){
browser.actions().mouseDown(holdButton).perform();
browser.sleep(50);
expect(holdButton.getAttribute('class') ).toMatch('holding');
browser.actions().mouseUp(holdButton).perform();
});
it('should not complete in under .5 seconds', function(){
browser.actions().mouseDown(holdButton).perform();
browser.sleep(50);
browser.actions().mouseUp(holdButton).perform();
expect(holdButton.getAttribute('class') ).toMatch('hold-failure');
});
it('should complete eventually', function(){
browser.actions().mouseDown(holdButton).perform();
browser.sleep(2000);
browser.actions().mouseUp(holdButton).perform();
expect(holdButton.getAttribute('class') ).toMatch('hold-success');
});
});
button.holding {
border-style: dashed;
}
button.hold-success {
background-color: #43AC6A;
border-color: #368a55;
color: #fff;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment