2018-11-12 10:31:58 +00:00
|
|
|
import ngModule from '../../module';
|
|
|
|
import Component from '../../lib/component';
|
|
|
|
import './style.scss';
|
|
|
|
|
|
|
|
/**
|
2019-04-29 09:49:43 +00:00
|
|
|
* Flat calendar.
|
2018-11-12 10:31:58 +00:00
|
|
|
*
|
2019-09-25 18:06:42 +00:00
|
|
|
* @property {Array} data Array of events
|
|
|
|
* @property {Function} hasEvents Determines if an events exists for a day
|
|
|
|
* @property {Function} getClass Class to apply to specific day
|
2018-11-12 10:31:58 +00:00
|
|
|
*/
|
|
|
|
export default class Calendar extends Component {
|
|
|
|
constructor($element, $scope) {
|
|
|
|
super($element, $scope);
|
2019-01-21 10:45:53 +00:00
|
|
|
this.events = [];
|
2018-11-12 10:31:58 +00:00
|
|
|
this.defaultDate = new Date();
|
2019-01-21 10:45:53 +00:00
|
|
|
this.displayControls = true;
|
2019-06-27 09:48:33 +00:00
|
|
|
this.disabled = false;
|
2019-01-21 10:45:53 +00:00
|
|
|
this.skip = 1;
|
2019-06-26 11:35:38 +00:00
|
|
|
|
|
|
|
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');
|
2018-11-12 10:31:58 +00:00
|
|
|
}
|
|
|
|
|
2019-04-03 09:34:58 +00:00
|
|
|
/**
|
|
|
|
* Returns the initial date
|
|
|
|
*
|
|
|
|
* @return {Date} - Default date
|
|
|
|
*/
|
2018-11-12 10:31:58 +00:00
|
|
|
get defaultDate() {
|
|
|
|
return this._defaultDate;
|
|
|
|
}
|
|
|
|
|
2019-04-03 09:34:58 +00:00
|
|
|
/**
|
|
|
|
* Sets a new initial date
|
|
|
|
*
|
|
|
|
* @param {Date} value - New default date
|
|
|
|
*/
|
2018-11-12 10:31:58 +00:00
|
|
|
set defaultDate(value) {
|
2019-09-25 18:06:42 +00:00
|
|
|
if (value) {
|
|
|
|
value = new Date(value);
|
|
|
|
value.setHours(0, 0, 0, 0);
|
|
|
|
value.setDate(1);
|
|
|
|
}
|
2019-01-21 10:45:53 +00:00
|
|
|
|
2019-09-25 18:06:42 +00:00
|
|
|
this._defaultDate = value;
|
2019-01-21 10:45:53 +00:00
|
|
|
this.repaint();
|
|
|
|
}
|
|
|
|
|
2019-04-03 09:34:58 +00:00
|
|
|
/**
|
2019-09-25 18:06:42 +00:00
|
|
|
* Sets events
|
2019-04-03 09:34:58 +00:00
|
|
|
*
|
|
|
|
* @param {Array} value - Array of events
|
2019-09-25 18:06:42 +00:00
|
|
|
* @param {Date} event.dated - Day to add event
|
|
|
|
* @param {String} event.name - Tooltip description
|
|
|
|
* @param {String} event.className - ClassName style
|
|
|
|
* @param {Object} event.style - Style properties
|
2019-04-03 09:34:58 +00:00
|
|
|
*/
|
|
|
|
set data(value) {
|
2019-01-21 10:45:53 +00:00
|
|
|
if (!value) return;
|
2018-11-12 10:31:58 +00:00
|
|
|
|
2019-04-03 09:34:58 +00:00
|
|
|
this.events = [];
|
2019-01-21 10:45:53 +00:00
|
|
|
|
2019-04-03 09:34:58 +00:00
|
|
|
value.forEach(event => {
|
2019-09-25 18:06:42 +00:00
|
|
|
event.dated = new Date(event.dated);
|
|
|
|
event.dated.setHours(0, 0, 0, 0);
|
|
|
|
this.events.push(event);
|
2019-04-03 09:34:58 +00:00
|
|
|
});
|
2019-01-21 10:45:53 +00:00
|
|
|
|
2019-09-04 06:42:01 +00:00
|
|
|
if (this.defaultDate) {
|
2019-01-21 10:45:53 +00:00
|
|
|
this.repaint();
|
2019-06-26 11:35:38 +00:00
|
|
|
this.checkSize();
|
|
|
|
}
|
2019-01-21 10:45:53 +00:00
|
|
|
}
|
|
|
|
|
2019-04-03 09:34:58 +00:00
|
|
|
/**
|
|
|
|
* Gets current month date
|
|
|
|
*/
|
|
|
|
|
|
|
|
get currentMonth() {
|
|
|
|
return this.defaultDate;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets next month date
|
|
|
|
*
|
|
|
|
* @return {Date}
|
|
|
|
*/
|
2019-01-21 10:45:53 +00:00
|
|
|
get nextMonth() {
|
|
|
|
const newDate = new Date(this.currentMonth);
|
|
|
|
newDate.setMonth(this.currentMonth.getMonth() + 1);
|
|
|
|
|
|
|
|
return newDate;
|
2018-11-12 10:31:58 +00:00
|
|
|
}
|
|
|
|
|
2019-04-03 09:34:58 +00:00
|
|
|
/**
|
|
|
|
* Gets previous month date
|
|
|
|
*
|
|
|
|
* @return {Date}
|
|
|
|
*/
|
2019-01-21 10:45:53 +00:00
|
|
|
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
|
2018-11-12 10:31:58 +00:00
|
|
|
|
2019-01-21 10:45:53 +00:00
|
|
|
let weekdayOffset = firstWeekday > 0 ? firstWeekday : 7;
|
|
|
|
let dayPrevious = previousLastDay - (weekdayOffset - 2);
|
|
|
|
let dayCurrent = 1;
|
|
|
|
let dayNext = 1;
|
2018-11-12 10:31:58 +00:00
|
|
|
|
|
|
|
this.days = [];
|
2019-01-21 10:45:53 +00:00
|
|
|
|
2019-04-03 09:34:58 +00:00
|
|
|
for (let fieldIndex = 1; fieldIndex < maxFields; fieldIndex++) {
|
|
|
|
// Insert previous month days
|
2019-01-21 10:45:53 +00:00
|
|
|
if (fieldIndex < weekdayOffset) {
|
2019-04-03 09:34:58 +00:00
|
|
|
const dated = new Date(
|
|
|
|
this.previousMonth.getFullYear(),
|
|
|
|
this.previousMonth.getMonth(), dayPrevious);
|
|
|
|
|
|
|
|
this.insertDay(dated, 'gray');
|
2019-01-21 10:45:53 +00:00
|
|
|
dayPrevious++;
|
2019-04-03 09:34:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Insert current month days
|
|
|
|
if (fieldIndex >= weekdayOffset && dayCurrent <= currentLastDay) {
|
|
|
|
const dated = new Date(
|
|
|
|
this.currentMonth.getFullYear(),
|
|
|
|
this.currentMonth.getMonth(), dayCurrent);
|
|
|
|
|
|
|
|
this.insertDay(dated);
|
2019-01-21 10:45:53 +00:00
|
|
|
dayCurrent++;
|
2019-04-03 09:34:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Insert next month days
|
|
|
|
if (fieldIndex >= weekdayOffset && dayCurrent > currentLastDay) {
|
|
|
|
const dated = new Date(
|
|
|
|
this.nextMonth.getFullYear(),
|
|
|
|
this.nextMonth.getMonth(), dayNext);
|
|
|
|
|
|
|
|
this.insertDay(dated, 'gray');
|
2019-01-21 10:45:53 +00:00
|
|
|
dayNext++;
|
2018-11-12 10:31:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-03 09:34:58 +00:00
|
|
|
/**
|
|
|
|
* Inserts a date on an array of month days
|
|
|
|
*
|
|
|
|
* @param {Date} dated - Date of month
|
|
|
|
* @param {String} className - Default class style
|
|
|
|
*/
|
2019-06-26 11:35:38 +00:00
|
|
|
insertDay(dated) {
|
|
|
|
let events = this.events.filter(event => {
|
2019-04-03 09:34:58 +00:00
|
|
|
return event.dated >= dated && event.dated <= dated;
|
2019-01-21 10:45:53 +00:00
|
|
|
});
|
2019-02-25 10:55:59 +00:00
|
|
|
|
2019-07-31 07:50:47 +00:00
|
|
|
const params = {dated: dated, events: events, style: {}};
|
2019-06-26 11:35:38 +00:00
|
|
|
const isSaturday = dated.getDay() === 6;
|
|
|
|
const isSunday = dated.getDay() === 0;
|
|
|
|
const isCurrentMonth = dated.getMonth() === this.currentMonth.getMonth();
|
|
|
|
const hasEvents = events.length > 0;
|
2019-03-22 07:28:57 +00:00
|
|
|
|
2019-06-26 11:35:38 +00:00
|
|
|
if (isCurrentMonth && isSunday && !hasEvents)
|
2019-09-25 18:06:42 +00:00
|
|
|
params.style = {color: '#999'};
|
2019-06-26 11:35:38 +00:00
|
|
|
|
|
|
|
if (isCurrentMonth && isSaturday && !hasEvents)
|
2019-09-25 18:06:42 +00:00
|
|
|
params.style = {color: '#999'};
|
2019-06-26 11:35:38 +00:00
|
|
|
|
2019-07-31 07:50:47 +00:00
|
|
|
if (!isCurrentMonth)
|
|
|
|
params.style = {opacity: '0.5'};
|
|
|
|
|
|
|
|
if (events.length > 0) {
|
|
|
|
const eventStyle = events[0].style;
|
|
|
|
const eventName = events[0].description || events[0].name;
|
|
|
|
if (eventStyle)
|
|
|
|
Object.assign(params.style, eventStyle);
|
|
|
|
if (eventName)
|
|
|
|
params.eventName = eventName;
|
|
|
|
}
|
2019-06-26 11:35:38 +00:00
|
|
|
|
|
|
|
this.days.push(params);
|
2019-01-21 10:45:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-04-03 09:34:58 +00:00
|
|
|
* Moves to next month(s)
|
2019-01-21 10:45:53 +00:00
|
|
|
*
|
|
|
|
* @param {Integer} skip - Months to skip at once
|
|
|
|
*/
|
|
|
|
moveNext(skip = 1) {
|
|
|
|
let next = this.defaultDate.getMonth() + skip;
|
2018-11-12 10:31:58 +00:00
|
|
|
this.defaultDate.setMonth(next);
|
2019-01-21 10:45:53 +00:00
|
|
|
this.repaint();
|
|
|
|
this.emit('moveNext');
|
2018-11-12 10:31:58 +00:00
|
|
|
}
|
|
|
|
|
2019-01-21 10:45:53 +00:00
|
|
|
/**
|
2019-04-03 09:34:58 +00:00
|
|
|
* Moves to previous month(s)
|
2019-01-21 10:45:53 +00:00
|
|
|
*
|
|
|
|
* @param {Integer} skip - Months to skip at once
|
|
|
|
*/
|
|
|
|
movePrevious(skip = 1) {
|
|
|
|
let previous = this.defaultDate.getMonth() - skip;
|
2018-11-12 10:31:58 +00:00
|
|
|
this.defaultDate.setMonth(previous);
|
2019-01-21 10:45:53 +00:00
|
|
|
this.repaint();
|
|
|
|
this.emit('movePrevious');
|
2018-11-12 10:31:58 +00:00
|
|
|
}
|
|
|
|
|
2019-01-21 10:45:53 +00:00
|
|
|
/**
|
|
|
|
* Day selection event
|
|
|
|
*
|
|
|
|
* @param {Integer} index - Index from days array
|
|
|
|
*/
|
2018-11-12 10:31:58 +00:00
|
|
|
select(index) {
|
2019-06-27 09:48:33 +00:00
|
|
|
if (this.disabled) return;
|
2019-01-21 10:45:53 +00:00
|
|
|
let day = this.days[index];
|
|
|
|
day.index = index;
|
|
|
|
|
|
|
|
this.emit('selection', {values: [day]});
|
2018-11-12 10:31:58 +00:00
|
|
|
}
|
|
|
|
|
2019-04-03 09:34:58 +00:00
|
|
|
/**
|
|
|
|
* WeekDay selection event
|
|
|
|
*
|
|
|
|
* @param {Integer} weekday - weekday index
|
|
|
|
*/
|
2019-01-21 10:45:53 +00:00
|
|
|
selectAll(weekday) {
|
2019-06-27 09:48:33 +00:00
|
|
|
if (this.disabled) return;
|
|
|
|
|
2019-01-21 10:45:53 +00:00
|
|
|
let selected = [];
|
|
|
|
for (let i in this.days) {
|
|
|
|
const day = this.days[i];
|
2019-04-03 09:34:58 +00:00
|
|
|
const date = day.dated;
|
2019-01-21 10:45:53 +00:00
|
|
|
day.index = i;
|
|
|
|
if (date.getDay() === weekday && date.getMonth() == this.defaultDate.getMonth())
|
|
|
|
selected.push(day);
|
|
|
|
}
|
|
|
|
this.emit('selection', {values: selected});
|
|
|
|
}
|
2019-06-26 11:35:38 +00:00
|
|
|
|
|
|
|
renderStyle(style) {
|
2019-09-26 08:43:06 +00:00
|
|
|
const normalizedStyle = {};
|
|
|
|
|
|
|
|
const properties = Object.keys(style);
|
|
|
|
properties.forEach(attribute => {
|
|
|
|
const attrName = attribute.split(/(?=[A-Z])/).
|
|
|
|
join('-').toLowerCase();
|
|
|
|
|
|
|
|
normalizedStyle[attrName] = style[attribute];
|
|
|
|
});
|
|
|
|
|
|
|
|
return normalizedStyle;
|
2019-06-26 11:35:38 +00:00
|
|
|
}
|
2019-09-25 18:06:42 +00:00
|
|
|
|
|
|
|
hasEvents() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
getClass() {
|
|
|
|
return '';
|
|
|
|
}
|
2018-11-12 10:31:58 +00:00
|
|
|
}
|
|
|
|
|
2019-04-03 09:34:58 +00:00
|
|
|
Calendar.$inject = ['$element', '$scope'];
|
2018-11-12 10:31:58 +00:00
|
|
|
|
|
|
|
ngModule.component('vnCalendar', {
|
|
|
|
template: require('./index.html'),
|
|
|
|
controller: Calendar,
|
|
|
|
bindings: {
|
|
|
|
model: '<',
|
2019-04-03 09:34:58 +00:00
|
|
|
data: '<?',
|
2019-05-17 11:27:51 +00:00
|
|
|
defaultDate: '=?',
|
2019-01-21 10:45:53 +00:00
|
|
|
onSelection: '&?',
|
|
|
|
onMoveNext: '&?',
|
|
|
|
onMovePrevious: '&?',
|
2019-09-25 18:06:42 +00:00
|
|
|
hasEvents: '&?',
|
|
|
|
getClass: '&?',
|
2019-01-21 10:45:53 +00:00
|
|
|
displayControls: '<?',
|
2019-06-27 09:48:33 +00:00
|
|
|
disabled: '<?',
|
2019-01-21 10:45:53 +00:00
|
|
|
skip: '<?'
|
2018-11-12 10:31:58 +00:00
|
|
|
}
|
|
|
|
});
|