import ngModule from '../../module'; import FormInput from '../form-input'; import './style.scss'; /** * Flat calendar. * * @property {Array} defaultDate Array of events * @property {Function} hasEvents Determines if an events exists for a day * @property {Function} getClass Class to apply to specific day * @event selection Emitted when day or weekday is selected * @event move Emitted when month changes */ export default class Calendar extends FormInput { constructor($element, $scope, vnWeekDays, moment) { super($element, $scope); this.weekDays = vnWeekDays.locales; this.displayControls = true; this.moment = moment; this.defaultDate = Date.vnNew(); } /** * The initial date * * @return {Date} - Default date */ get defaultDate() { return this._defaultDate; } set defaultDate(value) { if (value) { value = new Date(value); value.setHours(0, 0, 0, 0); value.setDate(1); } this._defaultDate = value; this.month = value.getMonth(); this.repaint(); } /** * Returns first day of month from a given date * * @param {Date} date - Origin date * @return {Integer} */ firstDay(date) { return new Date( date.getFullYear(), date.getMonth(), 1 ); } lastDay() { return new Date( this.defaultDate.getFullYear(), this.defaultDate.getMonth() + 1, 0 ).getDate(); } /** * Repaints the calendar. */ repaint() { const firstWeekday = this.firstDay(this.defaultDate).getDay() - 1; this.weekdayOffset = firstWeekday >= 0 ? firstWeekday : 6; let dayIndex = new Date(this.defaultDate.getTime()); dayIndex.setDate(1 - this.weekdayOffset); this.days = []; for (let i = 1; i <= 42; i++) { this.days.push(new Date(dayIndex.getTime())); dayIndex.setDate(dayIndex.getDate() + 1); } this.getWeekdays(); } getWeekdays() { if (!this.moment) return; const totalSlots = this.lastDay() + this.weekdayOffset; const weeks = Math.ceil(totalSlots / 7); const dated = this.moment(this.defaultDate); const firstWeekNumber = dated.set('date', 1).isoWeek(); const weekNumbers = []; for (let w = 0; w < weeks; w++) { let weekNumber = firstWeekNumber; if (dated.get('month') == 0 && firstWeekNumber > 1 && w > 0) weekNumber = 0; weekNumbers.push(weekNumber + w); } this.weekNumbers = weekNumbers; } /** * Gets CSS classes to apply to the specified day. * * @param {Date} date The date * @return {Object} The CSS classes to apply */ getDayClasses(date) { let day = date.getDate(); let wday = date.getDay(); let month = date.getMonth(); let year = date.getFullYear(); const currentDay = Date.vnNew().getDate(); const currentMonth = Date.vnNew().getMonth(); const currentYear = Date.vnNew().getFullYear(); let classes = { today: day === currentDay && month === currentMonth && year === currentYear, weekend: wday === 6 || wday === 0, previous: month < this.month, current: month == this.month, next: month > this.month, event: this.hasEvents({$day: date}) }; let userClass = this.getClass({$day: date}); if (userClass) classes[userClass] = true; return classes; } /** * Moves to next month(s) */ moveNext() { this.move(1); } /** * Moves to previous month(s) */ movePrevious() { this.move(-1); } /** * Moves @direction months backwards/forwards. * * @param {Number} direction Negative to move backwards, positive forwards */ move(direction) { let date = new Date(this.defaultDate.getTime()); date.setMonth(date.getMonth() + direction); this.defaultDate = date; this.repaint(); this.emit('move', {$date: date}); } /* * Day selection event */ select($event, day) { if (!this.editable) return; this.change(day); this.emit('selection', { $event: $event, $days: [day], $type: 'day' }); // Repaint only if 'selection' event is not listening if (!this.$events || this.$events && !this.$events['selection']) this.repaint(); } /* * WeekDay selection event */ selectWeekDay($event, weekday) { if (!this.editable) return; let days = []; for (let day of this.days) { if (day.getDay() === weekday && day.getMonth() == this.month) days.push(day); } this.field = days[0]; this.emit('selection', { $event: $event, $days: days, $type: 'weekday', $weekday: weekday }); this.repaint(); } hasEvents() { return false; } getClass() { return ''; } repeatLast() { if (this.formatDay) { const days = this.element.querySelectorAll('.days > .day'); for (let i = 0; i < days.length; i++) { this.formatDay({ $day: this.days[i], $element: days[i] }); } } if (this.formatWeek) { const weeks = this.element.querySelectorAll('.weeks > .day'); for (const week of weeks) { this.formatWeek({ $element: week }); } } } } Calendar.$inject = ['$element', '$scope', 'vnWeekDays', 'moment']; ngModule.vnComponent('vnCalendar', { template: require('./index.html'), controller: Calendar, bindings: { defaultDate: '=?', hasEvents: '&?', getClass: '&?', formatDay: '&?', formatWeek: '&?', displayControls: '