projects/ng-datetime-picker/src/lib/datetime-picker/datetime-picker/datetime-picker.component.ts
import {
Component, forwardRef, OnInit, ViewEncapsulation,
Input, Output, EventEmitter, AfterViewInit,
OnChanges
} from '@angular/core';
import { InputCoreComponent } from '@sq-ui/ng-sq-common';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { CalendarDay, InCalendarPicker } from '../interfaces/calendar-entities';
import { CalendarPeriodRelativityEnum } from '../enums/calendar-period-relativity.enum';
import { DateRange } from '../interfaces/date-range';
import { CalendarPeriodTypeEnum } from '../enums/calendar-period-type.enum';
import { DateObjectType } from '../enums/date-object-type.enum';
import { TimepickerConfig } from '../interfaces/timepicker-config';
import { List } from 'immutable';
import { CalendarManagerService } from '../calendar-manager.service';
import moment from 'moment';
const CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DatetimePickerComponent),
multi: true
};
@Component({
selector: 'sq-datetime-picker',
templateUrl: './datetime-picker.component.html',
styleUrls: ['./datetime-picker.component.scss'],
encapsulation: ViewEncapsulation.None,
providers: [CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR]
})
export class DatetimePickerComponent extends InputCoreComponent implements OnInit, AfterViewInit, OnChanges {
@Input() locale = 'en';
@Input() maxDate: moment.Moment | Date;
@Input() minDate: moment.Moment | Date;
@Input() isMultipleSelect = false;
@Input() format: string;
@Input() isTimepickerEnabled = false;
@Input() dateObjectType: string = DateObjectType.Moment;
@Input() timepickerConfig: TimepickerConfig;
@Output() dateSelectionChange: EventEmitter<moment.Moment | Date> = new EventEmitter<moment.Moment | Date>();
weekdays: string[];
months: InCalendarPicker[];
yearsList: InCalendarPicker[];
calendar: Array<CalendarDay[]>;
currentMonth: moment.Moment;
isMonthsPickerEnabled = false;
isYearsPickerEnabled = false;
time: moment.Moment;
calendarPeriodRelativity = CalendarPeriodRelativityEnum;
period: CalendarPeriodTypeEnum = CalendarPeriodTypeEnum.Month;
private selectedDates: List<moment.Moment> = List<moment.Moment>();
private parsedSelectedDates: any;
constructor(private calendarManager: CalendarManagerService) {
super();
}
ngOnInit() {
moment.locale(this.locale);
this.calendarManager.setLocale(this.locale);
const now = moment().hours(0).minutes(0).locale(this.locale);
this.selectedDates = List([now.clone()]);
this.weekdays = this.calendarManager.getWeekdays();
this.calendar = this.getMonthCalendar(now.clone());
this.initializeAuthorValuesIfAny();
}
ngAfterViewInit() {
setTimeout(() => {
this.setValueResult();
});
}
ngOnChanges(changesObj) {
if (changesObj.timepickerConfig && changesObj.timepickerConfig.currentValue) {
this.setValueResult();
}
}
onDateClick(date: CalendarDay) {
switch (date.relativityToCurrentMonth) {
case CalendarPeriodRelativityEnum.After:
this.select(date);
this.next();
break;
case CalendarPeriodRelativityEnum.Before:
this.select(date);
this.previous();
break;
default:
this.select(date);
break;
}
}
select(date: CalendarDay) {
const month = this.currentMonth.clone();
if (date.relativityToCurrentMonth === CalendarPeriodRelativityEnum.Before) {
month.subtract(1, 'month');
}
if (date.relativityToCurrentMonth === CalendarPeriodRelativityEnum.After) {
month.add(1, 'month');
}
this.markDateAsSelected(date);
this.dateSelectionChange.emit(this.value);
}
next() {
if (this.period === CalendarPeriodTypeEnum.Month) {
const nextMonth = this.currentMonth.add(1, 'month');
this.calendar = this.getMonthCalendar(nextMonth);
}
if (this.period === CalendarPeriodTypeEnum.Year) {
const dateRange = {
minDate: moment(this.minDate),
maxDate: moment(this.maxDate)
};
this.yearsList = this.calendarManager.generateYearPickerCollection(null, 19, dateRange);
}
}
previous() {
if (this.period === CalendarPeriodTypeEnum.Month) {
const previousMonth = this.currentMonth.subtract(1, 'month');
this.calendar = this.getMonthCalendar(previousMonth);
}
if (this.period === CalendarPeriodTypeEnum.Year) {
const dateRange = {
minDate: moment(this.minDate),
maxDate: moment(this.maxDate)
};
this.yearsList = this.calendarManager.generateYearPickerCollection(null, -19, dateRange);
}
}
getMonthCalendar(startPeriod: moment.Moment): Array<CalendarDay[]> {
const selectedDates = this.selectedDates.toArray();
const dateRange: DateRange = {
minDate: this.minDate,
maxDate: this.maxDate
};
this.currentMonth = startPeriod.clone();
return this.calendarManager.generateCalendarForMonth(startPeriod, this.currentMonth, selectedDates, dateRange);
}
showMonthsPicker(year: number = this.currentMonth.year()) {
this.deselectAll();
this.isYearsPickerEnabled = false;
this.isMonthsPickerEnabled = true;
this.currentMonth.year(year);
const dateRange = {
minDate: this.minDate,
maxDate: this.maxDate
};
this.period = CalendarPeriodTypeEnum.Month;
this.months = this.calendarManager.generateMonthPickerCollection(year, dateRange);
}
showYearsPicker() {
this.deselectAll();
this.isMonthsPickerEnabled = false;
this.isYearsPickerEnabled = true;
const dateRange = {
minDate: this.minDate,
maxDate: this.maxDate
};
this.period = CalendarPeriodTypeEnum.Year;
this.yearsList = this.calendarManager.generateYearPickerCollection(this.currentMonth, 19, dateRange);
}
selectMonth(month) {
this.calendar = this.getMonthCalendar(month.momentObj);
this.isMonthsPickerEnabled = false;
}
selectYear(year) {
this.showMonthsPicker(year.momentObj.year());
}
onTimeChange() {
this.setValueResult();
this.dateSelectionChange.emit(this.value);
}
private initializeAuthorValuesIfAny() {
const subscription = this._modelToViewChange.subscribe((newValue) => {
if (this.selectedDates.size === 1 && this.selectedDates.get(0).isSame(moment(), 'day')) {
if (newValue) {
this.deselectAll();
if (Array.isArray(newValue)) {
newValue.forEach((date) => {
const convertedDate = this.calendarManager.findADateFromCalendar(moment(date), this.calendar);
this.markDateAsSelected(convertedDate);
});
} else {
const calendarDay = this.calendarManager.findADateFromCalendar(moment(newValue), this.calendar);
this.markDateAsSelected(calendarDay);
}
}
}
subscription.unsubscribe();
});
}
private markDateAsSelected(date: CalendarDay) {
const selectedMomentObj = moment(date.momentObj);
const selectedIndex = this.calendarManager.getSelectedItemIndex(selectedMomentObj, this.selectedDates.toArray());
if (this.isMultipleSelect) {
if (selectedIndex > -1) {
date.isSelected = false;
this.selectedDates = this.selectedDates.remove(selectedIndex);
} else {
this.selectedDates = this.selectedDates.push(selectedMomentObj);
date.isSelected = true;
}
} else {
const previousDate = this.calendarManager.findADateFromCalendar(this.selectedDates.get(0), this.calendar);
if (previousDate) {
previousDate.isSelected = false;
}
this.selectedDates = this.selectedDates.clear();
this.selectedDates = this.selectedDates.push(selectedMomentObj);
date.isSelected = true;
}
this.setValueResult();
}
private deselectAll() {
this.selectedDates.toArray().forEach((selectedDate) => {
const calendarDay = this.calendarManager.findADateFromCalendar(selectedDate, this.calendar);
// this handles the case when we have a selected date
// from the previous month but we haven't selected anything
// from the current
if (calendarDay) {
calendarDay.isSelected = false;
}
});
this.selectedDates = List([]);
this.setValueResult();
}
private setValueResult() {
this.parsedSelectedDates = this.selectedDates.toArray();
if (this.parsedSelectedDates.length > 0) {
this.setValueTimeIfNeeded();
this.sortValueIfNeeded();
this.toValueDateObjectTypeIfNeeded();
this.toValueFormatIfNeeded();
}
if (this.isMultipleSelect) {
this.value = this.parsedSelectedDates;
} else {
this.value = this.parsedSelectedDates[0];
}
}
private toValueDateObjectTypeIfNeeded() {
if (!this.format) {
switch (this.dateObjectType) {
case DateObjectType.Date:
this.parsedSelectedDates = this.parsedSelectedDates.map((momentObj) => {
return momentObj.toDate();
});
break;
case DateObjectType.Unix:
this.parsedSelectedDates = this.parsedSelectedDates.map((momentObj) => {
return momentObj.toDate().getTime();
});
break;
}
}
}
private toValueFormatIfNeeded() {
if (this.format) {
const formattedDates = this.parsedSelectedDates.map((date) => {
return moment(date).format(this.format);
});
this.parsedSelectedDates = formattedDates;
}
}
private setValueTimeIfNeeded() {
if (this.isTimepickerEnabled && this.time) {
const datesWithTime = this.parsedSelectedDates.map((momentObj) => {
return momentObj.hours(this.time.hours()).minutes(this.time.minutes());
});
this.parsedSelectedDates = datesWithTime;
}
}
private sortValueIfNeeded() {
if (this.isMultipleSelect) {
const sortedDates = this.calendarManager.sortDatesAsc(this.parsedSelectedDates);
this.parsedSelectedDates = sortedDates;
}
}
}