This repository has been archived by the owner on Sep 5, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3.4k
/
calendarYear.js
237 lines (200 loc) · 7.82 KB
/
calendarYear.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
(function() {
'use strict';
angular.module('material.components.datepicker')
.directive('mdCalendarYear', calendarDirective);
/**
* Height of one calendar year tbody. This must be made known to the virtual-repeat and is
* subsequently used for scrolling to specific years.
*/
var TBODY_HEIGHT = 88;
/** Private component, representing a list of years in the calendar. */
function calendarDirective() {
return {
template:
'<div class="md-calendar-scroll-mask">' +
'<md-virtual-repeat-container class="md-calendar-scroll-container">' +
'<table role="grid" tabindex="0" class="md-calendar" aria-readonly="true">' +
'<tbody ' +
'md-calendar-year-body ' +
'role="rowgroup" ' +
'md-virtual-repeat="i in yearCtrl.items" ' +
'md-year-offset="$index" class="md-calendar-year" ' +
'md-start-index="yearCtrl.getFocusedYearIndex()" ' +
'md-item-size="' + TBODY_HEIGHT + '">' +
// The <tr> ensures that the <tbody> will have the proper
// height, even though it may be empty.
'<tr aria-hidden="true" md-force-height="\'' + TBODY_HEIGHT + 'px\'"></tr>' +
'</tbody>' +
'</table>' +
'</md-virtual-repeat-container>' +
'</div>',
require: ['^^mdCalendar', 'mdCalendarYear'],
controller: CalendarYearCtrl,
controllerAs: 'yearCtrl',
bindToController: true,
link: function(scope, element, attrs, controllers) {
var calendarCtrl = controllers[0];
var yearCtrl = controllers[1];
yearCtrl.initialize(calendarCtrl);
}
};
}
/**
* Controller for the mdCalendar component.
* @ngInject @constructor
*/
function CalendarYearCtrl($element, $scope, $animate, $q, $$mdDateUtil, $mdUtil) {
/** @final {!angular.JQLite} */
this.$element = $element;
/** @final {!angular.Scope} */
this.$scope = $scope;
/** @final {!angular.$animate} */
this.$animate = $animate;
/** @final {!angular.$q} */
this.$q = $q;
/** @final */
this.dateUtil = $$mdDateUtil;
/** @final {HTMLElement} */
this.calendarScroller = $element[0].querySelector('.md-virtual-repeat-scroller');
/** @type {boolean} */
this.isInitialized = false;
/** @type {boolean} */
this.isMonthTransitionInProgress = false;
/** @final */
this.$mdUtil = $mdUtil;
var self = this;
/**
* Handles a click event on a date cell.
* Created here so that every cell can use the same function instance.
* @this {HTMLTableCellElement} The cell that was clicked.
*/
this.cellClickHandler = function() {
self.onTimestampSelected($$mdDateUtil.getTimestampFromNode(this));
};
}
/**
* Initialize the controller by saving a reference to the calendar and
* setting up the object that will be iterated by the virtual repeater.
*/
CalendarYearCtrl.prototype.initialize = function(calendarCtrl) {
/**
* Dummy array-like object for virtual-repeat to iterate over. The length is the total
* number of years that can be viewed. We add 1 extra in order to include the current year.
*/
this.items = {
length: this.dateUtil.getYearDistance(
calendarCtrl.firstRenderableDate,
calendarCtrl.lastRenderableDate
) + 1
};
this.calendarCtrl = calendarCtrl;
this.attachScopeListeners();
calendarCtrl.updateVirtualRepeat();
// Fire the initial render, since we might have missed it the first time it fired.
calendarCtrl.ngModelCtrl && calendarCtrl.ngModelCtrl.$render();
};
/**
* Gets the "index" of the currently selected date as it would be in the virtual-repeat.
* @returns {number}
*/
CalendarYearCtrl.prototype.getFocusedYearIndex = function() {
var calendarCtrl = this.calendarCtrl;
return this.dateUtil.getYearDistance(
calendarCtrl.firstRenderableDate,
calendarCtrl.displayDate || calendarCtrl.selectedDate || calendarCtrl.today
);
};
/**
* Change the date that is highlighted in the calendar.
* @param {Date} date
*/
CalendarYearCtrl.prototype.changeDate = function(date) {
// Initialization is deferred until this function is called because we want to reflect
// the starting value of ngModel.
if (!this.isInitialized) {
this.calendarCtrl.hideVerticalScrollbar(this);
this.isInitialized = true;
return this.$q.when();
} else if (this.dateUtil.isValidDate(date) && !this.isMonthTransitionInProgress) {
var self = this;
var animationPromise = this.animateDateChange(date);
self.isMonthTransitionInProgress = true;
self.calendarCtrl.displayDate = date;
return animationPromise.then(function() {
self.isMonthTransitionInProgress = false;
});
}
};
/**
* Animates the transition from the calendar's current month to the given month.
* @param {Date} date
* @returns {angular.$q.Promise} The animation promise.
*/
CalendarYearCtrl.prototype.animateDateChange = function(date) {
if (this.dateUtil.isValidDate(date)) {
var monthDistance = this.dateUtil.getYearDistance(this.calendarCtrl.firstRenderableDate, date);
this.calendarScroller.scrollTop = monthDistance * TBODY_HEIGHT;
}
return this.$q.when();
};
/**
* Handles the year-view-specific keyboard interactions.
* @param {Object} event Scope event object passed by the calendar.
* @param {String} action Action, corresponding to the key that was pressed.
*/
CalendarYearCtrl.prototype.handleKeyEvent = function(event, action) {
var self = this;
var calendarCtrl = self.calendarCtrl;
var displayDate = calendarCtrl.displayDate;
if (action === 'select') {
self.changeDate(displayDate).then(function() {
self.onTimestampSelected(displayDate);
});
} else {
var date = null;
var dateUtil = self.dateUtil;
switch (action) {
case 'move-right': date = dateUtil.incrementMonths(displayDate, 1); break;
case 'move-left': date = dateUtil.incrementMonths(displayDate, -1); break;
case 'move-row-down': date = dateUtil.incrementMonths(displayDate, 6); break;
case 'move-row-up': date = dateUtil.incrementMonths(displayDate, -6); break;
}
if (date) {
var min = calendarCtrl.minDate ? dateUtil.getFirstDateOfMonth(calendarCtrl.minDate) : null;
var max = calendarCtrl.maxDate ? dateUtil.getFirstDateOfMonth(calendarCtrl.maxDate) : null;
date = dateUtil.getFirstDateOfMonth(self.dateUtil.clampDate(date, min, max));
self.changeDate(date).then(function() {
calendarCtrl.focusDate(date);
});
}
}
};
/**
* Attaches listeners for the scope events that are broadcast by the calendar.
*/
CalendarYearCtrl.prototype.attachScopeListeners = function() {
var self = this;
self.$scope.$on('md-calendar-parent-changed', function(event, value) {
self.calendarCtrl.changeSelectedDate(value ? self.dateUtil.getFirstDateOfMonth(value) : value);
self.changeDate(value);
});
self.$scope.$on('md-calendar-parent-action', angular.bind(self, self.handleKeyEvent));
};
/**
* Handles the behavior when a date is selected. Depending on the `mode`
* of the calendar, this can either switch back to the calendar view or
* set the model value.
* @param {number} timestamp The selected timestamp.
*/
CalendarYearCtrl.prototype.onTimestampSelected = function(timestamp) {
var calendarCtrl = this.calendarCtrl;
if (calendarCtrl.mode) {
this.$mdUtil.nextTick(function() {
// The timestamp has to be converted to a valid date.
calendarCtrl.setNgModelValue(new Date(timestamp));
});
} else {
calendarCtrl.setCurrentView('month', timestamp);
}
};
})();