salix/front/core/components/calendar/index.js

338 lines
8.4 KiB
JavaScript

import ngModule from '../../module';
import Component from '../../lib/component';
import './style.scss';
/**
* Flat calendar.
*
* @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
*/
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) {
if (value) {
value = new Date(value);
value.setHours(0, 0, 0, 0);
value.setDate(1);
}
this._defaultDate = value;
this.repaint();
}
/**
* Sets events
*
* @param {Array} value - Array of events
* @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
*/
set data(value) {
if (!value) return;
this.events = [];
value.forEach(event => {
event.dated = new Date(event.dated);
event.dated.setHours(0, 0, 0, 0);
this.events.push(event);
});
if (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: dated, events: events /**/, style: {}};
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: '#999'};
if (isCurrentMonth && isSaturday && !hasEvents)
params.style = {color: '#999'};
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;
}
this.days.push(params);
}
/**
* Moves to next month(s)
*
* @param {Integer} skip - Months to skip at once
*/
moveNext(skip = 1) {
let next = this.defaultDate.getMonth() + skip;
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.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].dated;
this.emit('selection', {
$days: [day],
$type: 'day'
});
}
/**
* WeekDay selection event
*
* @param {Integer} weekday - weekday index
*/
selectAll(weekday) {
if (this.disabled) return;
let days = [];
for (let i in this.days) {
const day = this.days[i].dated;
if (day.getDay() === weekday && day.getMonth() == this.defaultDate.getMonth())
days.push(day);
}
this.emit('selection', {
$days: days,
$type: 'weekday',
$weekday: weekday
});
}
renderStyle(style) {
const normalizedStyle = {};
if (style) {
const properties = Object.keys(style);
properties.forEach(attribute => {
const attrName = attribute.split(/(?=[A-Z])/).
join('-').toLowerCase();
normalizedStyle[attrName] = style[attribute];
});
}
return normalizedStyle;
}
hasEvents() {
return false;
}
getClass() {
return '';
}
}
Calendar.$inject = ['$element', '$scope'];
ngModule.component('vnCalendar', {
template: require('./index.html'),
controller: Calendar,
bindings: {
model: '<',
data: '<?',
defaultDate: '=?',
onSelection: '&?',
onMoveNext: '&?',
onMovePrevious: '&?',
hasEvents: '&?',
getClass: '&?',
displayControls: '<?',
disabled: '<?',
skip: '<?'
}
});