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

Commit 30a00a0

Browse files
bekospkozlowski-opensource
authored andcommitted
feat(datepicker): add datepicker directive
Closes #366
1 parent f009b23 commit 30a00a0

File tree

6 files changed

+1058
-0
lines changed

6 files changed

+1058
-0
lines changed

src/datepicker/datepicker.js

+214
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
angular.module('ui.bootstrap.datepicker', [])
2+
3+
.constant('datepickerConfig', {
4+
dayFormat: 'dd',
5+
monthFormat: 'MMMM',
6+
yearFormat: 'yyyy',
7+
dayHeaderFormat: 'EEE',
8+
dayTitleFormat: 'MMMM yyyy',
9+
monthTitleFormat: 'yyyy',
10+
showWeeks: true,
11+
startingDay: 0,
12+
yearRange: 20
13+
})
14+
15+
.directive( 'datepicker', ['$filter', '$parse', 'datepickerConfig', function ($filter, $parse, datepickerConfig) {
16+
return {
17+
restrict: 'EA',
18+
replace: true,
19+
scope: {
20+
model: '=ngModel',
21+
dateDisabled: '&'
22+
},
23+
templateUrl: 'template/datepicker/datepicker.html',
24+
link: function(scope, element, attrs) {
25+
scope.mode = 'day'; // Initial mode
26+
27+
// Configuration parameters
28+
var selected = new Date(), showWeeks, minDate, maxDate, format = {};
29+
format.day = angular.isDefined(attrs.dayFormat) ? scope.$eval(attrs.dayFormat) : datepickerConfig.dayFormat;
30+
format.month = angular.isDefined(attrs.monthFormat) ? scope.$eval(attrs.monthFormat) : datepickerConfig.monthFormat;
31+
format.year = angular.isDefined(attrs.yearFormat) ? scope.$eval(attrs.yearFormat) : datepickerConfig.yearFormat;
32+
format.dayHeader = angular.isDefined(attrs.dayHeaderFormat) ? scope.$eval(attrs.dayHeaderFormat) : datepickerConfig.dayHeaderFormat;
33+
format.dayTitle = angular.isDefined(attrs.dayTitleFormat) ? scope.$eval(attrs.dayTitleFormat) : datepickerConfig.dayTitleFormat;
34+
format.monthTitle = angular.isDefined(attrs.monthTitleFormat) ? scope.$eval(attrs.monthTitleFormat) : datepickerConfig.monthTitleFormat;
35+
var startingDay = angular.isDefined(attrs.startingDay) ? scope.$eval(attrs.startingDay) : datepickerConfig.startingDay;
36+
var yearRange = angular.isDefined(attrs.yearRange) ? scope.$eval(attrs.yearRange) : datepickerConfig.yearRange;
37+
38+
if (attrs.showWeeks) {
39+
scope.$parent.$watch($parse(attrs.showWeeks), function(value) {
40+
showWeeks = !! value;
41+
updateShowWeekNumbers();
42+
});
43+
} else {
44+
showWeeks = datepickerConfig.showWeeks;
45+
updateShowWeekNumbers();
46+
}
47+
48+
if (attrs.min) {
49+
scope.$parent.$watch($parse(attrs.min), function(value) {
50+
minDate = new Date(value);
51+
refill();
52+
});
53+
}
54+
if (attrs.max) {
55+
scope.$parent.$watch($parse(attrs.max), function(value) {
56+
maxDate = new Date(value);
57+
refill();
58+
});
59+
}
60+
61+
function updateCalendar (rows, labels, title) {
62+
scope.rows = rows;
63+
scope.labels = labels;
64+
scope.title = title;
65+
}
66+
67+
// Define whether the week number are visible
68+
function updateShowWeekNumbers() {
69+
scope.showWeekNumbers = ( scope.mode === 'day' && showWeeks );
70+
}
71+
72+
function compare( date1, date2 ) {
73+
if ( scope.mode === 'year') {
74+
return date2.getFullYear() - date1.getFullYear();
75+
} else if ( scope.mode === 'month' ) {
76+
return new Date( date2.getFullYear(), date2.getMonth() ) - new Date( date1.getFullYear(), date1.getMonth() );
77+
} else if ( scope.mode === 'day' ) {
78+
return (new Date( date2.getFullYear(), date2.getMonth(), date2.getDate() ) - new Date( date1.getFullYear(), date1.getMonth(), date1.getDate() ) );
79+
}
80+
}
81+
82+
function isDisabled(date) {
83+
return ((minDate && compare(date, minDate) > 0) || (maxDate && compare(date, maxDate) < 0) || (scope.dateDisabled && scope.dateDisabled({ date: date, mode: scope.mode })));
84+
}
85+
86+
// Split array into smaller arrays
87+
var split = function(a, size) {
88+
var arrays = [];
89+
while (a.length > 0) {
90+
arrays.push(a.splice(0, size));
91+
}
92+
return arrays;
93+
};
94+
var getDaysInMonth = function( year, month ) {
95+
return new Date(year, month + 1, 0).getDate();
96+
};
97+
98+
var fill = {
99+
day: function() {
100+
var days = [], labels = [], lastDate = null;
101+
102+
function addDays( dt, n, isCurrentMonth ) {
103+
for (var i =0; i < n; i ++) {
104+
days.push( {date: new Date(dt), isCurrent: isCurrentMonth, isSelected: isSelected(dt), label: $filter('date')(dt, format.day), disabled: isDisabled(dt) } );
105+
dt.setDate( dt.getDate() + 1 );
106+
}
107+
lastDate = dt;
108+
}
109+
110+
var d = new Date(selected);
111+
d.setDate(1);
112+
113+
var difference = startingDay - d.getDay();
114+
var numDisplayedFromPreviousMonth = (difference > 0) ? 7 - difference : - difference;
115+
116+
if ( numDisplayedFromPreviousMonth > 0 ) {
117+
d.setDate( - numDisplayedFromPreviousMonth + 1 );
118+
addDays(d, numDisplayedFromPreviousMonth, false);
119+
}
120+
addDays(lastDate || d, getDaysInMonth(selected.getFullYear(), selected.getMonth()), true);
121+
addDays(lastDate, (7 - days.length % 7) % 7, false);
122+
123+
// Day labels
124+
for (i = 0; i < 7; i++) {
125+
labels.push( $filter('date')(days[i].date, format.dayHeader) );
126+
}
127+
updateCalendar( split( days, 7 ), labels, $filter('date')(selected, format.dayTitle) );
128+
},
129+
month: function() {
130+
var months = [], i = 0, year = selected.getFullYear();
131+
while ( i < 12 ) {
132+
var dt = new Date(year, i++, 1);
133+
months.push( {date: dt, isCurrent: true, isSelected: isSelected(dt), label: $filter('date')(dt, format.month), disabled: isDisabled(dt)} );
134+
}
135+
updateCalendar( split( months, 3 ), [], $filter('date')(selected, format.monthTitle) );
136+
},
137+
year: function() {
138+
var years = [], year = parseInt((selected.getFullYear() - 1) / yearRange, 10) * yearRange + 1;
139+
for ( var i = 0; i < yearRange; i++ ) {
140+
var dt = new Date(year + i, 0, 1);
141+
years.push( {date: dt, isCurrent: true, isSelected: isSelected(dt), label: $filter('date')(dt, format.year), disabled: isDisabled(dt)} );
142+
}
143+
var title = years[0].label + ' - ' + years[years.length - 1].label;
144+
updateCalendar( split( years, 5 ), [], title );
145+
}
146+
};
147+
var refill = function() {
148+
fill[scope.mode]();
149+
};
150+
var isSelected = function( dt ) {
151+
if ( scope.model && scope.model.getFullYear() === dt.getFullYear() ) {
152+
if ( scope.mode === 'year' ) {
153+
return true;
154+
}
155+
if ( scope.model.getMonth() === dt.getMonth() ) {
156+
return ( scope.mode === 'month' || (scope.mode === 'day' && scope.model.getDate() === dt.getDate()) );
157+
}
158+
}
159+
return false;
160+
};
161+
162+
scope.$watch('model', function ( dt, olddt ) {
163+
if ( angular.isDate(dt) ) {
164+
selected = angular.copy(dt);
165+
}
166+
167+
if ( ! angular.equals(dt, olddt) ) {
168+
refill();
169+
}
170+
});
171+
scope.$watch('mode', function() {
172+
updateShowWeekNumbers();
173+
refill();
174+
});
175+
176+
scope.select = function( dt ) {
177+
selected = new Date(dt);
178+
179+
if ( scope.mode === 'year' ) {
180+
scope.mode = 'month';
181+
selected.setFullYear( dt.getFullYear() );
182+
} else if ( scope.mode === 'month' ) {
183+
scope.mode = 'day';
184+
selected.setMonth( dt.getMonth() );
185+
} else if ( scope.mode === 'day' ) {
186+
scope.model = new Date(selected);
187+
}
188+
};
189+
scope.move = function(step) {
190+
if (scope.mode === 'day') {
191+
selected.setMonth( selected.getMonth() + step );
192+
} else if (scope.mode === 'month') {
193+
selected.setFullYear( selected.getFullYear() + step );
194+
} else if (scope.mode === 'year') {
195+
selected.setFullYear( selected.getFullYear() + step * yearRange );
196+
}
197+
refill();
198+
};
199+
scope.toggleMode = function() {
200+
scope.mode = ( scope.mode === 'day' ) ? 'month' : ( scope.mode === 'month' ) ? 'year' : 'day';
201+
};
202+
scope.getWeekNumber = function(row) {
203+
if ( scope.mode !== 'day' || ! scope.showWeekNumbers || row.length !== 7 ) {
204+
return;
205+
}
206+
207+
var index = ( startingDay > 4 ) ? 11 - startingDay : 4 - startingDay; // Thursday
208+
var d = new Date( row[ index ].date );
209+
d.setHours(0, 0, 0);
210+
return Math.ceil((((d - new Date(d.getFullYear(), 0, 1)) / 86400000) + 1) / 7); // 86400000 = 1000*60*60*24;
211+
};
212+
}
213+
};
214+
}]);

src/datepicker/docs/demo.html

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<div ng-controller="DatepickerDemoCtrl">
2+
<datepicker ng-model="dt" show-weeks="showWeeks" starting-day="1" date-disabled="disabled(date, mode)" min="minDate" max="'2015-06-22'"></datepicker>
3+
<pre>Selected date is: <em>{{dt | date:'fullDate' }}</em></pre>
4+
5+
<button class="btn btn-small btn-inverse" ng-click="today()">Today</button>
6+
<button class="btn btn-small btn-success" ng-click="toggleWeeks()">Toggle Weeks</button>
7+
<button class="btn btn-small btn-danger" ng-click="clear()">Clear</button>
8+
<button class="btn btn-small" ng-click="toggleMin()">After today restriction</button>
9+
</div>

src/datepicker/docs/demo.js

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
var DatepickerDemoCtrl = function ($scope) {
2+
$scope.today = function() {
3+
$scope.dt = new Date();
4+
};
5+
$scope.today();
6+
7+
$scope.showWeeks = true;
8+
$scope.toggleWeeks = function () {
9+
$scope.showWeeks = ! $scope.showWeeks;
10+
};
11+
12+
$scope.clear = function () {
13+
$scope.dt = null;
14+
};
15+
16+
// Disable weekend selection
17+
$scope.disabled = function(date, mode) {
18+
return ( mode === 'day' && ( date.getDay() === 0 || date.getDay() === 6 ) );
19+
};
20+
21+
$scope.toggleMin = function() {
22+
$scope.minDate = ( $scope.minDate ) ? null : new Date();
23+
};
24+
$scope.toggleMin();
25+
};

src/datepicker/docs/readme.md

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
A clean, flexible, and fully customizable date picker.
2+
3+
User can navigate through months and years.
4+
The datepicker shows dates that come from other than the main month being displayed. These other dates are also selectable.
5+
6+
Everything is formatted using the [date filter](http://docs.angularjs.org/api/ng.filter:date) and thus is also localized.
7+
8+
### Settings ###
9+
10+
All settings can be provided as attributes in the `<datepicker>` or globally configured through the `datepickerConfig`.
11+
12+
* `ng-model` <i class="icon-eye-open"></i>
13+
:
14+
The date object.
15+
16+
* `show-weeks` <i class="icon-eye-open"></i>
17+
_(Defaults: true)_ :
18+
Whether to display week numbers.
19+
20+
* `starting-day`
21+
_(Defaults: 0)_ :
22+
Starting day of the week from 0-6 (0=Sunday, ..., 6=Saturday).
23+
24+
* `min` <i class="icon-eye-open"></i>
25+
_(Default: null)_ :
26+
Defines the minimum available date.
27+
28+
* `max` <i class="icon-eye-open"></i>
29+
_(Default: null)_ :
30+
Defines the maximum available date.
31+
32+
* `date-disabled (date, mode)`
33+
_(Default: null)_ :
34+
An optional expression to disable visible options based on passing date and current mode _(day|month|year)_.
35+
36+
* `day-format`
37+
_(Default: 'dd')_ :
38+
Format of day in month.
39+
40+
* `month-format`
41+
_(Default: 'MMMM')_ :
42+
Format of month in year.
43+
44+
* `year-format`
45+
_(Default: 'yyyy')_ :
46+
Format of year in year range.
47+
48+
* `year-range`
49+
_(Default: 20)_ :
50+
Number of years displayed in year selection.
51+
52+
* `day-header-format`
53+
_(Default: 'EEE')_ :
54+
Format of day in week header.
55+
56+
* `day-title-format`
57+
_(Default: 'MMMM yyyy')_ :
58+
Format of title when selecting day.
59+
60+
* `month-title-format`
61+
_(Default: 'yyyy')_ :
62+
Format of title when selecting month.

0 commit comments

Comments
 (0)