import ngModule from '../../module'; import Component from '../../lib/component'; import './style.scss'; /** * Flat calendar. * */ export default class Calendar extends Component { constructor($element, $scope) { super($element, $scope); this.events = []; this.defaultDate = new Date(); this.displayControls = true; this.disabled = false; this.skip = 1; this.window.addEventListener('resize', () => { this.checkSize(); }); } /** * Resizes the calendar * based on component height */ checkSize() { const height = this.$element[0].clientHeight; if (height < 530) this.$element.addClass('small'); else this.$element.removeClass('small'); } /** * Returns the initial date * * @return {Date} - Default date */ get defaultDate() { return this._defaultDate; } /** * Sets a new initial date * * @param {Date} value - New default date */ set defaultDate(value) { this._defaultDate = value; this.repaint(); } /** * Sets initial events * * @param {Array} value - Array of events */ set data(value) { if (!value) return; this.events = []; value.forEach(event => { this.addEvent(event); }); if (value.length && this.defaultDate) { this.repaint(); this.checkSize(); } } /** * Gets current month date */ get currentMonth() { return this.defaultDate; } /** * Gets next month date * * @return {Date} */ get nextMonth() { const newDate = new Date(this.currentMonth); newDate.setMonth(this.currentMonth.getMonth() + 1); return newDate; } /** * Gets previous month date * * @return {Date} */ get previousMonth() { const newDate = new Date(this.currentMonth); newDate.setMonth(this.currentMonth.getMonth() - 1); return newDate; } /** * Returns first day of month from a given date * * @param {Date} date - Origin date * @return {Integer} */ firstDay(date) { const newDate = new Date( date.getFullYear(), date.getMonth(), 1); return newDate; } /** * Returns last day of month from a given date * * @param {Date} date - Origin date * @return {Integer} */ lastDay(date) { const newDate = new Date( date.getFullYear(), date.getMonth() + 1, 0); return newDate; } repaint() { const firstWeekday = this.firstDay(this.currentMonth).getDay(); const previousLastDay = this.lastDay(this.previousMonth).getDate(); const currentLastDay = this.lastDay(this.currentMonth).getDate(); const maxFields = 42; // Max field limit let weekdayOffset = firstWeekday > 0 ? firstWeekday : 7; let dayPrevious = previousLastDay - (weekdayOffset - 2); let dayCurrent = 1; let dayNext = 1; this.days = []; for (let fieldIndex = 1; fieldIndex < maxFields; fieldIndex++) { // Insert previous month days if (fieldIndex < weekdayOffset) { const dated = new Date( this.previousMonth.getFullYear(), this.previousMonth.getMonth(), dayPrevious); this.insertDay(dated, 'gray'); dayPrevious++; } // Insert current month days if (fieldIndex >= weekdayOffset && dayCurrent <= currentLastDay) { const dated = new Date( this.currentMonth.getFullYear(), this.currentMonth.getMonth(), dayCurrent); this.insertDay(dated); dayCurrent++; } // Insert next month days if (fieldIndex >= weekdayOffset && dayCurrent > currentLastDay) { const dated = new Date( this.nextMonth.getFullYear(), this.nextMonth.getMonth(), dayNext); this.insertDay(dated, 'gray'); dayNext++; } } } /** * Inserts a date on an array of month days * * @param {Date} dated - Date of month * @param {String} className - Default class style */ insertDay(dated) { let events = this.events.filter(event => { return event.dated >= dated && event.dated <= dated; }); const params = {dated, events}; const isSaturday = dated.getDay() === 6; const isSunday = dated.getDay() === 0; const isCurrentMonth = dated.getMonth() === this.currentMonth.getMonth(); const hasEvents = events.length > 0; if (isCurrentMonth && isSunday && !hasEvents) params.style = {color: '#f42121'}; if (isCurrentMonth && isSaturday && !hasEvents) params.style = {color: '#666666'}; if (!isCurrentMonth && !hasEvents) params.style = {color: '#9b9b9b'}; this.days.push(params); } /** * Adds a new calendar event * * @param {Object} options - Event params * @param {Date} options.dated - Day to add event * @param {String} options.name - Tooltip description * @param {String} options.className - ClassName style * @param {Object} options.style - Style properties * @param {Boolean} options.isRemovable - True if is removable by users */ addEvent(options) { if (!Object.hasOwnProperty.call(options, 'isRemovable')) options.isRemovable = true; options.dated = new Date(options.dated); options.dated.setHours(0, 0, 0, 0); this.events.push(options); } /** * Removes an event from an array of events * @param {Object} dated - Dated event */ removeEvent(dated) { dated = new Date(dated); dated.setHours(0, 0, 0, 0); const event = this.events.findIndex(event => { return event.dated >= dated && event.dated <= dated; }); if (event > -1) this.events.splice(event, 1); } /** * Moves to next month(s) * * @param {Integer} skip - Months to skip at once */ moveNext(skip = 1) { let next = this.defaultDate.getMonth() + skip; this.defaultDate.setDate(1); this.defaultDate.setMonth(next); this.repaint(); this.emit('moveNext'); } /** * Moves to previous month(s) * * @param {Integer} skip - Months to skip at once */ movePrevious(skip = 1) { let previous = this.defaultDate.getMonth() - skip; this.defaultDate.setDate(1); this.defaultDate.setMonth(previous); this.repaint(); this.emit('movePrevious'); } /** * Day selection event * * @param {Integer} index - Index from days array */ select(index) { if (this.disabled) return; let day = this.days[index]; day.index = index; this.emit('selection', {values: [day]}); } /** * WeekDay selection event * * @param {Integer} weekday - weekday index */ selectAll(weekday) { if (this.disabled) return; let selected = []; for (let i in this.days) { const day = this.days[i]; const date = day.dated; day.index = i; if (date.getDay() === weekday && date.getMonth() == this.defaultDate.getMonth()) selected.push(day); } this.emit('selection', {values: selected}); } renderStyle(style) { if (style) { return { 'background-color': style.backgroundColor, 'font-weight': style.fontWeight, 'color': style.color }; } } } Calendar.$inject = ['$element', '$scope']; ngModule.component('vnCalendar', { template: require('./index.html'), controller: Calendar, bindings: { model: '<', data: '