This repository has been archived by the owner on May 29, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 6.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(datepicker): add datepicker directive
Closes #366
- Loading branch information
1 parent
f009b23
commit 30a00a0
Showing
6 changed files
with
1,058 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,214 @@ | ||
angular.module('ui.bootstrap.datepicker', []) | ||
|
||
.constant('datepickerConfig', { | ||
dayFormat: 'dd', | ||
monthFormat: 'MMMM', | ||
yearFormat: 'yyyy', | ||
dayHeaderFormat: 'EEE', | ||
dayTitleFormat: 'MMMM yyyy', | ||
monthTitleFormat: 'yyyy', | ||
showWeeks: true, | ||
startingDay: 0, | ||
yearRange: 20 | ||
}) | ||
|
||
.directive( 'datepicker', ['$filter', '$parse', 'datepickerConfig', function ($filter, $parse, datepickerConfig) { | ||
return { | ||
restrict: 'EA', | ||
replace: true, | ||
scope: { | ||
model: '=ngModel', | ||
dateDisabled: '&' | ||
}, | ||
templateUrl: 'template/datepicker/datepicker.html', | ||
link: function(scope, element, attrs) { | ||
scope.mode = 'day'; // Initial mode | ||
|
||
// Configuration parameters | ||
var selected = new Date(), showWeeks, minDate, maxDate, format = {}; | ||
format.day = angular.isDefined(attrs.dayFormat) ? scope.$eval(attrs.dayFormat) : datepickerConfig.dayFormat; | ||
format.month = angular.isDefined(attrs.monthFormat) ? scope.$eval(attrs.monthFormat) : datepickerConfig.monthFormat; | ||
format.year = angular.isDefined(attrs.yearFormat) ? scope.$eval(attrs.yearFormat) : datepickerConfig.yearFormat; | ||
format.dayHeader = angular.isDefined(attrs.dayHeaderFormat) ? scope.$eval(attrs.dayHeaderFormat) : datepickerConfig.dayHeaderFormat; | ||
format.dayTitle = angular.isDefined(attrs.dayTitleFormat) ? scope.$eval(attrs.dayTitleFormat) : datepickerConfig.dayTitleFormat; | ||
format.monthTitle = angular.isDefined(attrs.monthTitleFormat) ? scope.$eval(attrs.monthTitleFormat) : datepickerConfig.monthTitleFormat; | ||
var startingDay = angular.isDefined(attrs.startingDay) ? scope.$eval(attrs.startingDay) : datepickerConfig.startingDay; | ||
var yearRange = angular.isDefined(attrs.yearRange) ? scope.$eval(attrs.yearRange) : datepickerConfig.yearRange; | ||
|
||
if (attrs.showWeeks) { | ||
scope.$parent.$watch($parse(attrs.showWeeks), function(value) { | ||
showWeeks = !! value; | ||
updateShowWeekNumbers(); | ||
}); | ||
} else { | ||
showWeeks = datepickerConfig.showWeeks; | ||
updateShowWeekNumbers(); | ||
} | ||
|
||
if (attrs.min) { | ||
scope.$parent.$watch($parse(attrs.min), function(value) { | ||
minDate = new Date(value); | ||
refill(); | ||
}); | ||
} | ||
if (attrs.max) { | ||
scope.$parent.$watch($parse(attrs.max), function(value) { | ||
maxDate = new Date(value); | ||
refill(); | ||
}); | ||
} | ||
|
||
function updateCalendar (rows, labels, title) { | ||
scope.rows = rows; | ||
scope.labels = labels; | ||
scope.title = title; | ||
} | ||
|
||
// Define whether the week number are visible | ||
function updateShowWeekNumbers() { | ||
scope.showWeekNumbers = ( scope.mode === 'day' && showWeeks ); | ||
} | ||
|
||
function compare( date1, date2 ) { | ||
if ( scope.mode === 'year') { | ||
return date2.getFullYear() - date1.getFullYear(); | ||
} else if ( scope.mode === 'month' ) { | ||
return new Date( date2.getFullYear(), date2.getMonth() ) - new Date( date1.getFullYear(), date1.getMonth() ); | ||
} else if ( scope.mode === 'day' ) { | ||
return (new Date( date2.getFullYear(), date2.getMonth(), date2.getDate() ) - new Date( date1.getFullYear(), date1.getMonth(), date1.getDate() ) ); | ||
} | ||
} | ||
|
||
function isDisabled(date) { | ||
return ((minDate && compare(date, minDate) > 0) || (maxDate && compare(date, maxDate) < 0) || (scope.dateDisabled && scope.dateDisabled({ date: date, mode: scope.mode }))); | ||
} | ||
|
||
// Split array into smaller arrays | ||
var split = function(a, size) { | ||
var arrays = []; | ||
while (a.length > 0) { | ||
arrays.push(a.splice(0, size)); | ||
} | ||
return arrays; | ||
}; | ||
var getDaysInMonth = function( year, month ) { | ||
return new Date(year, month + 1, 0).getDate(); | ||
}; | ||
|
||
var fill = { | ||
day: function() { | ||
var days = [], labels = [], lastDate = null; | ||
|
||
function addDays( dt, n, isCurrentMonth ) { | ||
for (var i =0; i < n; i ++) { | ||
days.push( {date: new Date(dt), isCurrent: isCurrentMonth, isSelected: isSelected(dt), label: $filter('date')(dt, format.day), disabled: isDisabled(dt) } ); | ||
dt.setDate( dt.getDate() + 1 ); | ||
} | ||
lastDate = dt; | ||
} | ||
|
||
var d = new Date(selected); | ||
d.setDate(1); | ||
|
||
var difference = startingDay - d.getDay(); | ||
var numDisplayedFromPreviousMonth = (difference > 0) ? 7 - difference : - difference; | ||
|
||
if ( numDisplayedFromPreviousMonth > 0 ) { | ||
d.setDate( - numDisplayedFromPreviousMonth + 1 ); | ||
addDays(d, numDisplayedFromPreviousMonth, false); | ||
} | ||
addDays(lastDate || d, getDaysInMonth(selected.getFullYear(), selected.getMonth()), true); | ||
addDays(lastDate, (7 - days.length % 7) % 7, false); | ||
|
||
// Day labels | ||
for (i = 0; i < 7; i++) { | ||
labels.push( $filter('date')(days[i].date, format.dayHeader) ); | ||
} | ||
updateCalendar( split( days, 7 ), labels, $filter('date')(selected, format.dayTitle) ); | ||
}, | ||
month: function() { | ||
var months = [], i = 0, year = selected.getFullYear(); | ||
while ( i < 12 ) { | ||
var dt = new Date(year, i++, 1); | ||
months.push( {date: dt, isCurrent: true, isSelected: isSelected(dt), label: $filter('date')(dt, format.month), disabled: isDisabled(dt)} ); | ||
} | ||
updateCalendar( split( months, 3 ), [], $filter('date')(selected, format.monthTitle) ); | ||
}, | ||
year: function() { | ||
var years = [], year = parseInt((selected.getFullYear() - 1) / yearRange, 10) * yearRange + 1; | ||
for ( var i = 0; i < yearRange; i++ ) { | ||
var dt = new Date(year + i, 0, 1); | ||
years.push( {date: dt, isCurrent: true, isSelected: isSelected(dt), label: $filter('date')(dt, format.year), disabled: isDisabled(dt)} ); | ||
} | ||
var title = years[0].label + ' - ' + years[years.length - 1].label; | ||
updateCalendar( split( years, 5 ), [], title ); | ||
} | ||
}; | ||
var refill = function() { | ||
fill[scope.mode](); | ||
}; | ||
var isSelected = function( dt ) { | ||
if ( scope.model && scope.model.getFullYear() === dt.getFullYear() ) { | ||
if ( scope.mode === 'year' ) { | ||
return true; | ||
} | ||
if ( scope.model.getMonth() === dt.getMonth() ) { | ||
return ( scope.mode === 'month' || (scope.mode === 'day' && scope.model.getDate() === dt.getDate()) ); | ||
} | ||
} | ||
return false; | ||
}; | ||
|
||
scope.$watch('model', function ( dt, olddt ) { | ||
if ( angular.isDate(dt) ) { | ||
selected = angular.copy(dt); | ||
} | ||
|
||
if ( ! angular.equals(dt, olddt) ) { | ||
refill(); | ||
} | ||
}); | ||
scope.$watch('mode', function() { | ||
updateShowWeekNumbers(); | ||
refill(); | ||
}); | ||
|
||
scope.select = function( dt ) { | ||
selected = new Date(dt); | ||
|
||
if ( scope.mode === 'year' ) { | ||
scope.mode = 'month'; | ||
selected.setFullYear( dt.getFullYear() ); | ||
} else if ( scope.mode === 'month' ) { | ||
scope.mode = 'day'; | ||
selected.setMonth( dt.getMonth() ); | ||
} else if ( scope.mode === 'day' ) { | ||
scope.model = new Date(selected); | ||
} | ||
}; | ||
scope.move = function(step) { | ||
if (scope.mode === 'day') { | ||
selected.setMonth( selected.getMonth() + step ); | ||
} else if (scope.mode === 'month') { | ||
selected.setFullYear( selected.getFullYear() + step ); | ||
} else if (scope.mode === 'year') { | ||
selected.setFullYear( selected.getFullYear() + step * yearRange ); | ||
} | ||
refill(); | ||
}; | ||
scope.toggleMode = function() { | ||
scope.mode = ( scope.mode === 'day' ) ? 'month' : ( scope.mode === 'month' ) ? 'year' : 'day'; | ||
}; | ||
scope.getWeekNumber = function(row) { | ||
if ( scope.mode !== 'day' || ! scope.showWeekNumbers || row.length !== 7 ) { | ||
return; | ||
} | ||
|
||
var index = ( startingDay > 4 ) ? 11 - startingDay : 4 - startingDay; // Thursday | ||
var d = new Date( row[ index ].date ); | ||
d.setHours(0, 0, 0); | ||
return Math.ceil((((d - new Date(d.getFullYear(), 0, 1)) / 86400000) + 1) / 7); // 86400000 = 1000*60*60*24; | ||
}; | ||
} | ||
}; | ||
}]); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
<div ng-controller="DatepickerDemoCtrl"> | ||
<datepicker ng-model="dt" show-weeks="showWeeks" starting-day="1" date-disabled="disabled(date, mode)" min="minDate" max="'2015-06-22'"></datepicker> | ||
<pre>Selected date is: <em>{{dt | date:'fullDate' }}</em></pre> | ||
|
||
<button class="btn btn-small btn-inverse" ng-click="today()">Today</button> | ||
<button class="btn btn-small btn-success" ng-click="toggleWeeks()">Toggle Weeks</button> | ||
<button class="btn btn-small btn-danger" ng-click="clear()">Clear</button> | ||
<button class="btn btn-small" ng-click="toggleMin()">After today restriction</button> | ||
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
var DatepickerDemoCtrl = function ($scope) { | ||
$scope.today = function() { | ||
$scope.dt = new Date(); | ||
}; | ||
$scope.today(); | ||
|
||
$scope.showWeeks = true; | ||
$scope.toggleWeeks = function () { | ||
$scope.showWeeks = ! $scope.showWeeks; | ||
}; | ||
|
||
$scope.clear = function () { | ||
$scope.dt = null; | ||
}; | ||
|
||
// Disable weekend selection | ||
$scope.disabled = function(date, mode) { | ||
return ( mode === 'day' && ( date.getDay() === 0 || date.getDay() === 6 ) ); | ||
}; | ||
|
||
$scope.toggleMin = function() { | ||
$scope.minDate = ( $scope.minDate ) ? null : new Date(); | ||
}; | ||
$scope.toggleMin(); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
A clean, flexible, and fully customizable date picker. | ||
|
||
User can navigate through months and years. | ||
The datepicker shows dates that come from other than the main month being displayed. These other dates are also selectable. | ||
|
||
Everything is formatted using the [date filter](http://docs.angularjs.org/api/ng.filter:date) and thus is also localized. | ||
|
||
### Settings ### | ||
|
||
All settings can be provided as attributes in the `<datepicker>` or globally configured through the `datepickerConfig`. | ||
|
||
* `ng-model` <i class="icon-eye-open"></i> | ||
: | ||
The date object. | ||
|
||
* `show-weeks` <i class="icon-eye-open"></i> | ||
_(Defaults: true)_ : | ||
Whether to display week numbers. | ||
|
||
* `starting-day` | ||
_(Defaults: 0)_ : | ||
Starting day of the week from 0-6 (0=Sunday, ..., 6=Saturday). | ||
|
||
* `min` <i class="icon-eye-open"></i> | ||
_(Default: null)_ : | ||
Defines the minimum available date. | ||
|
||
* `max` <i class="icon-eye-open"></i> | ||
_(Default: null)_ : | ||
Defines the maximum available date. | ||
|
||
* `date-disabled (date, mode)` | ||
_(Default: null)_ : | ||
An optional expression to disable visible options based on passing date and current mode _(day|month|year)_. | ||
|
||
* `day-format` | ||
_(Default: 'dd')_ : | ||
Format of day in month. | ||
|
||
* `month-format` | ||
_(Default: 'MMMM')_ : | ||
Format of month in year. | ||
|
||
* `year-format` | ||
_(Default: 'yyyy')_ : | ||
Format of year in year range. | ||
|
||
* `year-range` | ||
_(Default: 20)_ : | ||
Number of years displayed in year selection. | ||
|
||
* `day-header-format` | ||
_(Default: 'EEE')_ : | ||
Format of day in week header. | ||
|
||
* `day-title-format` | ||
_(Default: 'MMMM yyyy')_ : | ||
Format of title when selecting day. | ||
|
||
* `month-title-format` | ||
_(Default: 'yyyy')_ : | ||
Format of title when selecting month. |
Oops, something went wrong.