Skip to content
This repository has been archived by the owner on May 29, 2019. It is now read-only.

feat(timepicker): added support for seconds #2467

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/timepicker/test/timepicker.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@ describe('timepicker directive', function () {
return e;
}

it('contains three row & three input elements', function() {
it('contains three row & four input elements', function() {
expect(element.find('tr').length).toBe(3);
expect(element.find('input').length).toBe(2);
expect(element.find('input').length).toBe(3);
expect(element.find('button').length).toBe(1);
});

Expand Down Expand Up @@ -549,7 +549,7 @@ describe('timepicker directive', function () {
});

function getMeridianTd() {
return element.find('tr').eq(1).find('td').eq(3);
return element.find('tr').eq(1).find('td').eq(5);
}

it('initially displays correct time when `show-meridian` is false', function() {
Expand Down
95 changes: 77 additions & 18 deletions src/timepicker/timepicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ angular.module('ui.bootstrap.timepicker', [])
.constant('timepickerConfig', {
hourStep: 1,
minuteStep: 1,
secondStep: 1,
showMeridian: true,
showSeconds: false,
meridians: null,
readonlyInput: false,
mousewheel: true
Expand All @@ -19,15 +21,16 @@ angular.module('ui.bootstrap.timepicker', [])
ngModelCtrl.$render = this.render;

var hoursInputEl = inputs.eq(0),
minutesInputEl = inputs.eq(1);
minutesInputEl = inputs.eq(1),
secondsInputEl = inputs.eq(2);

var mousewheel = angular.isDefined($attrs.mousewheel) ? $scope.$parent.$eval($attrs.mousewheel) : timepickerConfig.mousewheel;
if ( mousewheel ) {
this.setupMousewheelEvents( hoursInputEl, minutesInputEl );
this.setupMousewheelEvents( hoursInputEl, minutesInputEl, secondsInputEl );
}

$scope.readonlyInput = angular.isDefined($attrs.readonlyInput) ? $scope.$parent.$eval($attrs.readonlyInput) : timepickerConfig.readonlyInput;
this.setupInputEvents( hoursInputEl, minutesInputEl );
this.setupInputEvents( hoursInputEl, minutesInputEl, secondsInputEl );
};

var hourStep = timepickerConfig.hourStep;
Expand All @@ -43,6 +46,13 @@ angular.module('ui.bootstrap.timepicker', [])
minuteStep = parseInt(value, 10);
});
}

var secondStep = timepickerConfig.secondStep;
if ($attrs.secondStep) {
$scope.$parent.$watch($parse($attrs.secondStep), function(value) {
secondStep = parseInt(value, 10);
});
}

// 12H / 24H mode
$scope.showMeridian = timepickerConfig.showMeridian;
Expand All @@ -52,16 +62,24 @@ angular.module('ui.bootstrap.timepicker', [])

if ( ngModelCtrl.$error.time ) {
// Evaluate from template
var hours = getHoursFromTemplate(), minutes = getMinutesFromTemplate();
if (angular.isDefined( hours ) && angular.isDefined( minutes )) {
selected.setHours( hours );
var hours = getHoursFromTemplate(),
minutes = getMinutesFromTemplate(),
seconds = getSecondsFromTemplate();
if (angular.isDefined( hours ) && angular.isDefined( minutes ) && angular.isDefined( seconds )) {
selected.setHours( hours, minutes, seconds );
refresh();
}
} else {
updateTemplate();
}
});
}

// Show seconds?
$scope.showSeconds = timepickerConfig.showSeconds;
if ($attrs.showSeconds) {
$scope.showSeconds = !!$attrs.showSeconds;
}

// Get $scope.hours in 24H mode if valid
function getHoursFromTemplate ( ) {
Expand All @@ -86,13 +104,18 @@ angular.module('ui.bootstrap.timepicker', [])
var minutes = parseInt($scope.minutes, 10);
return ( minutes >= 0 && minutes < 60 ) ? minutes : undefined;
}

function getSecondsFromTemplate() {
var seconds = parseInt($scope.seconds, 10);
return ( seconds >= 0 && seconds < 60 ) ? seconds : undefined;
}

function pad( value ) {
return ( angular.isDefined(value) && value.toString().length < 2 ) ? '0' + value : value;
}

// Respond on mousewheel spin
this.setupMousewheelEvents = function( hoursInputEl, minutesInputEl ) {
this.setupMousewheelEvents = function( hoursInputEl, minutesInputEl, secondsInputEl ) {
var isScrollingUp = function(e) {
if (e.originalEvent) {
e = e.originalEvent;
Expand All @@ -111,17 +134,23 @@ angular.module('ui.bootstrap.timepicker', [])
$scope.$apply( (isScrollingUp(e)) ? $scope.incrementMinutes() : $scope.decrementMinutes() );
e.preventDefault();
});

secondsInputEl.bind('mousewheel wheel', function(e) {
$scope.$apply( (isScrollingUp(e)) ? $scope.incrementSeconds() : $scope.decrementSeconds() );
e.preventDefault();
});

};

this.setupInputEvents = function( hoursInputEl, minutesInputEl ) {
this.setupInputEvents = function( hoursInputEl, minutesInputEl, secondsInputEl ) {
if ( $scope.readonlyInput ) {
$scope.updateHours = angular.noop;
$scope.updateMinutes = angular.noop;
$scope.updateSeconds = angular.noop;
return;
}

var invalidate = function(invalidHours, invalidMinutes) {
var invalidate = function(invalidHours, invalidMinutes, invalidSeconds) {
ngModelCtrl.$setViewValue( null );
ngModelCtrl.$setValidity('time', false);
if (angular.isDefined(invalidHours)) {
Expand All @@ -130,6 +159,9 @@ angular.module('ui.bootstrap.timepicker', [])
if (angular.isDefined(invalidMinutes)) {
$scope.invalidMinutes = invalidMinutes;
}
if (angular.isDefined(invalidSeconds)) {
$scope.invalidSeconds = invalidSeconds;
}
};

$scope.updateHours = function() {
Expand Down Expand Up @@ -169,6 +201,25 @@ angular.module('ui.bootstrap.timepicker', [])
});
}
});

$scope.updateSeconds = function() {
var seconds = getSecondsFromTemplate();

if ( angular.isDefined(seconds) ) {
selected.setSeconds( seconds );
refresh( 's' );
} else {
invalidate(undefined, undefined, true);
}
};

secondsInputEl.bind('blur', function(e) {
if ( !$scope.invalidSeconds && $scope.seconds < 10 ) {
$scope.$apply( function() {
$scope.seconds = pad( $scope.seconds );
});
}
});

};

Expand Down Expand Up @@ -198,40 +249,48 @@ angular.module('ui.bootstrap.timepicker', [])
ngModelCtrl.$setValidity('time', true);
$scope.invalidHours = false;
$scope.invalidMinutes = false;
$scope.invalidSeconds = false;
}

function updateTemplate( keyboardChange ) {
var hours = selected.getHours(), minutes = selected.getMinutes();
var hours = selected.getHours(), minutes = selected.getMinutes(), seconds = selected.getSeconds();

if ( $scope.showMeridian ) {
hours = ( hours === 0 || hours === 12 ) ? 12 : hours % 12; // Convert 24 to 12 hour system
}

$scope.hours = keyboardChange === 'h' ? hours : pad(hours);
$scope.minutes = keyboardChange === 'm' ? minutes : pad(minutes);
$scope.seconds = keyboardChange === 's' ? seconds : pad(seconds);
$scope.meridian = selected.getHours() < 12 ? meridians[0] : meridians[1];
}

function addMinutes( minutes ) {
var dt = new Date( selected.getTime() + minutes * 60000 );
selected.setHours( dt.getHours(), dt.getMinutes() );
function addSeconds( seconds ) {
var dt = new Date( selected.getTime() + seconds * 1000 );
selected.setHours( dt.getHours(), dt.getMinutes(), dt.getSeconds() );
refresh();
}

$scope.incrementHours = function() {
addMinutes( hourStep * 60 );
addSeconds( hourStep * 60 * 60 );
};
$scope.decrementHours = function() {
addMinutes( - hourStep * 60 );
addSeconds( - hourStep * 60 * 60 );
};
$scope.incrementMinutes = function() {
addMinutes( minuteStep );
addSeconds( minuteStep * 60 );
};
$scope.decrementMinutes = function() {
addMinutes( - minuteStep );
addSeconds( - minuteStep * 60 );
};
$scope.incrementSeconds = function() {
addSeconds( secondStep );
};
$scope.decrementSeconds = function() {
addSeconds( - secondStep );
};
$scope.toggleMeridian = function() {
addMinutes( 12 * 60 * (( selected.getHours() < 12 ) ? 1 : -1) );
addSeconds( 12 * 60 * 60 * (( selected.getHours() < 12 ) ? 1 : -1) );
};
}])

Expand Down
8 changes: 8 additions & 0 deletions template/timepicker/timepicker.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
<td><a ng-click="incrementHours()" class="btn btn-link"><span class="glyphicon glyphicon-chevron-up"></span></a></td>
<td>&nbsp;</td>
<td><a ng-click="incrementMinutes()" class="btn btn-link"><span class="glyphicon glyphicon-chevron-up"></span></a></td>
<td ng-show="showSeconds">&nbsp;</td>
<td ng-show="showSeconds"><a ng-click="incrementSeconds()" class="btn btn-link"><span class="glyphicon glyphicon-chevron-up"></span></a></td>
<td ng-show="showMeridian"></td>
</tr>
<tr>
Expand All @@ -14,12 +16,18 @@
<td style="width:50px;" class="form-group" ng-class="{'has-error': invalidMinutes}">
<input type="text" ng-model="minutes" ng-change="updateMinutes()" class="form-control text-center" ng-readonly="readonlyInput" maxlength="2">
</td>
<td ng-show="showSeconds">:</td>
<td style="width:50px;" class="form-group" ng-class="{'has-error': invalidSeconds}" ng-show="showSeconds">
<input type="text" ng-model="seconds" ng-change="updateSeconds()" class="form-control text-center" ng-readonly="readonlyInput" maxlength="2">
</td>
<td ng-show="showMeridian"><button type="button" class="btn btn-default text-center" ng-click="toggleMeridian()">{{meridian}}</button></td>
</tr>
<tr class="text-center">
<td><a ng-click="decrementHours()" class="btn btn-link"><span class="glyphicon glyphicon-chevron-down"></span></a></td>
<td>&nbsp;</td>
<td><a ng-click="decrementMinutes()" class="btn btn-link"><span class="glyphicon glyphicon-chevron-down"></span></a></td>
<td ng-show="showSeconds">&nbsp;</td>
<td ng-show="showSeconds"><a ng-click="decrementSeconds()" class="btn btn-link"><span class="glyphicon glyphicon-chevron-down"></span></a></td>
<td ng-show="showMeridian"></td>
</tr>
</tbody>
Expand Down