axiros-datetime-picker
Version:
Angular 13 compatible date time picker
1,145 lines (1,137 loc) • 257 kB
JavaScript
import * as i0 from '@angular/core';
import { Directive, Input, InjectionToken, Injectable, inject, LOCALE_ID, EventEmitter, Component, ChangeDetectionStrategy, Output, Optional, Inject, ViewChild, Pipe, TemplateRef, SkipSelf, forwardRef, NgModule } from '@angular/core';
import * as i1 from '@angular/common';
import { DOCUMENT, CommonModule } from '@angular/common';
import * as i1$1 from '@angular/cdk/a11y';
import { A11yModule } from '@angular/cdk/a11y';
import * as i1$2 from '@angular/cdk/overlay';
import { NoopScrollStrategy, Overlay, OverlayConfig, OverlayModule } from '@angular/cdk/overlay';
import { Subscription, of, merge, Subject, defer } from 'rxjs';
import * as i2 from '@angular/cdk/portal';
import { BasePortalOutlet, CdkPortalOutlet, ComponentPortal, PortalInjector, PortalModule } from '@angular/cdk/portal';
import { ENTER, PAGE_DOWN, PAGE_UP, END, HOME, DOWN_ARROW, UP_ARROW, RIGHT_ARROW, LEFT_ARROW, SPACE, ESCAPE } from '@angular/cdk/keycodes';
import { coerceNumberProperty, coerceBooleanProperty, coerceArray } from '@angular/cdk/coercion';
import { take, debounceTime, distinctUntilChanged, filter, startWith } from 'rxjs/operators';
import { trigger, state, style, transition, group, query, animateChild, animate, keyframes } from '@angular/animations';
import { NG_VALUE_ACCESSOR, NG_VALIDATORS, Validators } from '@angular/forms';
import * as i1$3 from '@angular/cdk/platform';
import { PlatformModule } from '@angular/cdk/platform';
class OwlDateTimeTriggerDirective {
constructor(changeDetector) {
this.changeDetector = changeDetector;
this.stateChanges = Subscription.EMPTY;
}
get disabled() {
return this._disabled === undefined ? this.dtPicker.disabled : !!this._disabled;
}
set disabled(value) {
this._disabled = value;
}
get owlDTTriggerDisabledClass() {
return this.disabled;
}
ngOnInit() {
}
ngOnChanges(changes) {
if (changes.datepicker) {
this.watchStateChanges();
}
}
ngAfterContentInit() {
this.watchStateChanges();
}
ngOnDestroy() {
this.stateChanges.unsubscribe();
}
handleClickOnHost(event) {
if (this.dtPicker) {
this.dtPicker.open();
event.stopPropagation();
}
}
watchStateChanges() {
this.stateChanges.unsubscribe();
const inputDisabled = this.dtPicker && this.dtPicker.dtInput ?
this.dtPicker.dtInput.disabledChange : of();
const pickerDisabled = this.dtPicker ?
this.dtPicker.disabledChange : of();
this.stateChanges = merge([pickerDisabled, inputDisabled])
.subscribe(() => {
this.changeDetector.markForCheck();
});
}
}
/** @nocollapse */ OwlDateTimeTriggerDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: OwlDateTimeTriggerDirective, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Directive });
/** @nocollapse */ OwlDateTimeTriggerDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "13.3.11", type: OwlDateTimeTriggerDirective, selector: "[owlDateTimeTrigger]", inputs: { dtPicker: ["owlDateTimeTrigger", "dtPicker"], disabled: "disabled" }, host: { listeners: { "click": "handleClickOnHost($event)" }, properties: { "class.owl-dt-trigger-disabled": "owlDTTriggerDisabledClass" } }, usesOnChanges: true, ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: OwlDateTimeTriggerDirective, decorators: [{
type: Directive,
args: [{
selector: '[owlDateTimeTrigger]',
host: {
'(click)': 'handleClickOnHost($event)',
'[class.owl-dt-trigger-disabled]': 'owlDTTriggerDisabledClass'
}
}]
}], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }]; }, propDecorators: { dtPicker: [{
type: Input,
args: ['owlDateTimeTrigger']
}], disabled: [{
type: Input
}] } });
/**
* date-time-format.class
*/
/** InjectionToken for date time picker that can be used to override default format. */
const OWL_DATE_TIME_FORMATS = new InjectionToken('OWL_DATE_TIME_FORMATS');
/**
* date-time-picker-intl.service
*/
class OwlDateTimeIntl {
constructor() {
/**
* Stream that emits whenever the labels here are changed. Use this to notify
* components if the labels have changed after initialization.
*/
this.changes = new Subject();
/** A label for the up second button (used by screen readers). */
this.upSecondLabel = 'Add a second';
/** A label for the down second button (used by screen readers). */
this.downSecondLabel = 'Minus a second';
/** A label for the up minute button (used by screen readers). */
this.upMinuteLabel = 'Add a minute';
/** A label for the down minute button (used by screen readers). */
this.downMinuteLabel = 'Minus a minute';
/** A label for the up hour button (used by screen readers). */
this.upHourLabel = 'Add a hour';
/** A label for the down hour button (used by screen readers). */
this.downHourLabel = 'Minus a hour';
/** A label for the previous month button (used by screen readers). */
this.prevMonthLabel = 'Previous month';
/** A label for the next month button (used by screen readers). */
this.nextMonthLabel = 'Next month';
/** A label for the previous year button (used by screen readers). */
this.prevYearLabel = 'Previous year';
/** A label for the next year button (used by screen readers). */
this.nextYearLabel = 'Next year';
/** A label for the previous multi-year button (used by screen readers). */
this.prevMultiYearLabel = 'Previous 21 years';
/** A label for the next multi-year button (used by screen readers). */
this.nextMultiYearLabel = 'Next 21 years';
/** A label for the 'switch to month view' button (used by screen readers). */
this.switchToMonthViewLabel = 'Change to month view';
/** A label for the 'switch to year view' button (used by screen readers). */
this.switchToMultiYearViewLabel = 'Choose month and year';
/** A label for the cancel button */
this.cancelBtnLabel = 'Cancel';
/** A label for the set button */
this.setBtnLabel = 'Set';
/** A label for the range 'from' in picker info */
this.rangeFromLabel = 'From';
/** A label for the range 'to' in picker info */
this.rangeToLabel = 'To';
/** A label for the hour12 button (AM) */
this.hour12AMLabel = 'AM';
/** A label for the hour12 button (PM) */
this.hour12PMLabel = 'PM';
}
}
/** @nocollapse */ OwlDateTimeIntl.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: OwlDateTimeIntl, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
/** @nocollapse */ OwlDateTimeIntl.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: OwlDateTimeIntl, providedIn: 'root' });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: OwlDateTimeIntl, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}] });
/**
* date-time-adapter.class
*/
/** InjectionToken for date time picker that can be used to override default locale code. */
const OWL_DATE_TIME_LOCALE = new InjectionToken('OWL_DATE_TIME_LOCALE', {
providedIn: 'root',
factory: OWL_DATE_TIME_LOCALE_FACTORY
});
/** @docs-private */
function OWL_DATE_TIME_LOCALE_FACTORY() {
return inject(LOCALE_ID);
}
/** Provider for OWL_DATE_TIME_LOCALE injection token. */
const OWL_DATE_TIME_LOCALE_PROVIDER = {
provide: OWL_DATE_TIME_LOCALE,
useExisting: LOCALE_ID
};
class DateTimeAdapter {
constructor() {
/** A stream that emits when the locale changes. */
this._localeChanges = new Subject();
/** total milliseconds in a day. */
this.millisecondsInDay = 86400000;
/** total milliseconds in a minute. */
this.millisecondsInMinute = 60000;
}
get localeChanges() {
return this._localeChanges;
}
/**
* Compare two given dates
* 1 if the first date is after the second,
* -1 if the first date is before the second
* 0 if dates are equal.
* */
compare(first, second) {
if (!this.isValid(first) || !this.isValid(second)) {
throw Error('JSNativeDate: Cannot compare invalid dates.');
}
const dateFirst = this.clone(first);
const dateSecond = this.clone(second);
const diff = this.getTime(dateFirst) - this.getTime(dateSecond);
if (diff < 0) {
return -1;
}
else if (diff > 0) {
return 1;
}
else {
// Return 0 if diff is 0; return NaN if diff is NaN
return diff;
}
}
/**
* Check if two given dates are in the same year
* 1 if the first date's year is after the second,
* -1 if the first date's year is before the second
* 0 if two given dates are in the same year
* */
compareYear(first, second) {
if (!this.isValid(first) || !this.isValid(second)) {
throw Error('JSNativeDate: Cannot compare invalid dates.');
}
const yearLeft = this.getYear(first);
const yearRight = this.getYear(second);
const diff = yearLeft - yearRight;
if (diff < 0) {
return -1;
}
else if (diff > 0) {
return 1;
}
else {
return 0;
}
}
/**
* Attempts to deserialize a value to a valid date object. This is different from parsing in that
* deserialize should only accept non-ambiguous, locale-independent formats (e.g. a ISO 8601
* string). The default implementation does not allow any deserialization, it simply checks that
* the given value is already a valid date object or null. The `<mat-datepicker>` will call this
* method on all of it's `@Input()` properties that accept dates. It is therefore possible to
* support passing values from your backend directly to these properties by overriding this method
* to also deserialize the format used by your backend.
*/
deserialize(value) {
if (value == null ||
(this.isDateInstance(value) && this.isValid(value))) {
return value;
}
return this.invalid();
}
/**
* Sets the locale used for all dates.
*/
setLocale(locale) {
this.locale = locale;
this._localeChanges.next();
}
/**
* Clamp the given date between min and max dates.
*/
clampDate(date, min, max) {
if (min && this.compare(date, min) < 0) {
return min;
}
if (max && this.compare(date, max) > 0) {
return max;
}
return date;
}
}
/**
* calendar-body.component
*/
class CalendarCell {
constructor(value, displayValue, ariaLabel, enabled, out = false, cellClass = '') {
this.value = value;
this.displayValue = displayValue;
this.ariaLabel = ariaLabel;
this.enabled = enabled;
this.out = out;
this.cellClass = cellClass;
}
}
class OwlCalendarBodyComponent {
constructor(elmRef, ngZone) {
this.elmRef = elmRef;
this.ngZone = ngZone;
/**
* The cell number of the active cell in the table.
*/
this.activeCell = 0;
/**
* The number of columns in the table.
* */
this.numCols = 7;
/**
* The ratio (width / height) to use for the cells in the table.
*/
this.cellRatio = 1;
/**
* Emit when a calendar cell is selected
* */
this.select = new EventEmitter();
}
get owlDTCalendarBodyClass() {
return true;
}
get isInSingleMode() {
return this.selectMode === 'single';
}
get isInRangeMode() {
return (this.selectMode === 'range' ||
this.selectMode === 'rangeFrom' ||
this.selectMode === 'rangeTo');
}
ngOnInit() { }
selectCell(cell) {
this.select.emit(cell);
}
isActiveCell(rowIndex, colIndex) {
const cellNumber = rowIndex * this.numCols + colIndex;
return cellNumber === this.activeCell;
}
/**
* Check if the cell is selected
*/
isSelected(value) {
if (!this.selectedValues || this.selectedValues.length === 0) {
return false;
}
if (this.isInSingleMode) {
return value === this.selectedValues[0];
}
if (this.isInRangeMode) {
const fromValue = this.selectedValues[0];
const toValue = this.selectedValues[1];
return value === fromValue || value === toValue;
}
}
/**
* Check if the cell in the range
* */
isInRange(value) {
if (this.isInRangeMode) {
const fromValue = this.selectedValues[0];
const toValue = this.selectedValues[1];
if (fromValue !== null && toValue !== null) {
return value >= fromValue && value <= toValue;
}
else {
return value === fromValue || value === toValue;
}
}
}
/**
* Check if the cell is the range from
* */
isRangeFrom(value) {
if (this.isInRangeMode) {
const fromValue = this.selectedValues[0];
return fromValue !== null && value === fromValue;
}
}
/**
* Check if the cell is the range to
* */
isRangeTo(value) {
if (this.isInRangeMode) {
const toValue = this.selectedValues[1];
return toValue !== null && value === toValue;
}
}
/**
* Focus to a active cell
* */
focusActiveCell() {
this.ngZone.runOutsideAngular(() => {
this.ngZone.onStable
.asObservable()
.pipe(take(1))
.subscribe(() => {
this.elmRef.nativeElement
.querySelector('.owl-dt-calendar-cell-active')
.focus();
});
});
}
}
/** @nocollapse */ OwlCalendarBodyComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: OwlCalendarBodyComponent, deps: [{ token: i0.ElementRef }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component });
/** @nocollapse */ OwlCalendarBodyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.3.11", type: OwlCalendarBodyComponent, selector: "[owl-date-time-calendar-body]", inputs: { activeCell: "activeCell", rows: "rows", numCols: "numCols", cellRatio: "cellRatio", todayValue: "todayValue", selectedValues: "selectedValues", selectMode: "selectMode" }, outputs: { select: "select" }, host: { properties: { "class.owl-dt-calendar-body": "owlDTCalendarBodyClass" } }, exportAs: ["owlDateTimeCalendarBody"], ngImport: i0, template: "<tr *ngFor=\"let row of rows; let rowIndex = index\" role=\"row\">\n <td *ngFor=\"let item of row; let colIndex = index\"\n class=\"owl-dt-calendar-cell {{item.cellClass}}\"\n [tabindex]=\"isActiveCell(rowIndex, colIndex) ? 0 : -1\"\n [class.owl-dt-calendar-cell-active]=\"isActiveCell(rowIndex, colIndex)\"\n [class.owl-dt-calendar-cell-disabled]=\"!item.enabled\"\n [class.owl-dt-calendar-cell-in-range]=\"isInRange(item.value)\"\n [class.owl-dt-calendar-cell-range-from]=\"isRangeFrom(item.value)\"\n [class.owl-dt-calendar-cell-range-to]=\"isRangeTo(item.value)\"\n [attr.aria-label]=\"item.ariaLabel\"\n [attr.aria-disabled]=\"!item.enabled || null\"\n [style.width.%]=\"100 / numCols\"\n [style.paddingTop.%]=\"50 * cellRatio / numCols\"\n [style.paddingBottom.%]=\"50 * cellRatio / numCols\"\n (click)=\"selectCell(item)\">\n <span class=\"owl-dt-calendar-cell-content\"\n [ngClass]=\"{\n 'owl-dt-calendar-cell-out': item.out,\n 'owl-dt-calendar-cell-today': item.value === todayValue,\n 'owl-dt-calendar-cell-selected': isSelected(item.value)\n }\">\n {{item.displayValue}}\n </span>\n </td>\n</tr>\n", styles: [""], directives: [{ type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: OwlCalendarBodyComponent, decorators: [{
type: Component,
args: [{ selector: '[owl-date-time-calendar-body]', exportAs: 'owlDateTimeCalendarBody', host: {
'[class.owl-dt-calendar-body]': 'owlDTCalendarBodyClass'
}, preserveWhitespaces: false, changeDetection: ChangeDetectionStrategy.OnPush, template: "<tr *ngFor=\"let row of rows; let rowIndex = index\" role=\"row\">\n <td *ngFor=\"let item of row; let colIndex = index\"\n class=\"owl-dt-calendar-cell {{item.cellClass}}\"\n [tabindex]=\"isActiveCell(rowIndex, colIndex) ? 0 : -1\"\n [class.owl-dt-calendar-cell-active]=\"isActiveCell(rowIndex, colIndex)\"\n [class.owl-dt-calendar-cell-disabled]=\"!item.enabled\"\n [class.owl-dt-calendar-cell-in-range]=\"isInRange(item.value)\"\n [class.owl-dt-calendar-cell-range-from]=\"isRangeFrom(item.value)\"\n [class.owl-dt-calendar-cell-range-to]=\"isRangeTo(item.value)\"\n [attr.aria-label]=\"item.ariaLabel\"\n [attr.aria-disabled]=\"!item.enabled || null\"\n [style.width.%]=\"100 / numCols\"\n [style.paddingTop.%]=\"50 * cellRatio / numCols\"\n [style.paddingBottom.%]=\"50 * cellRatio / numCols\"\n (click)=\"selectCell(item)\">\n <span class=\"owl-dt-calendar-cell-content\"\n [ngClass]=\"{\n 'owl-dt-calendar-cell-out': item.out,\n 'owl-dt-calendar-cell-today': item.value === todayValue,\n 'owl-dt-calendar-cell-selected': isSelected(item.value)\n }\">\n {{item.displayValue}}\n </span>\n </td>\n</tr>\n", styles: [""] }]
}], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.NgZone }]; }, propDecorators: { activeCell: [{
type: Input
}], rows: [{
type: Input
}], numCols: [{
type: Input
}], cellRatio: [{
type: Input
}], todayValue: [{
type: Input
}], selectedValues: [{
type: Input
}], selectMode: [{
type: Input
}], select: [{
type: Output
}] } });
/**
* calendar-month-view.component
*/
const DAYS_PER_WEEK = 7;
const WEEKS_PER_VIEW = 6;
class OwlMonthViewComponent {
constructor(cdRef, dateTimeAdapter, dateTimeFormats) {
this.cdRef = cdRef;
this.dateTimeAdapter = dateTimeAdapter;
this.dateTimeFormats = dateTimeFormats;
/**
* Whether to hide dates in other months at the start or end of the current month.
* */
this.hideOtherMonths = false;
/**
* Define the first day of a week
* Sunday: 0 ~ Saturday: 6
* */
this._firstDayOfWeek = 0;
/**
* The select mode of the picker;
* */
this._selectMode = 'single';
this._selecteds = [];
this.localeSub = Subscription.EMPTY;
this.initiated = false;
/**
* An array to hold all selectedDates' value
* the value is the day number in current month
* */
this.selectedDates = [];
/**
* Callback to invoke when a new date is selected
* */
this.selectedChange = new EventEmitter();
/**
* Callback to invoke when any date is selected.
* */
this.userSelection = new EventEmitter();
/** Emits when any date is activated. */
this.pickerMomentChange = new EventEmitter();
}
get firstDayOfWeek() {
return this._firstDayOfWeek;
}
set firstDayOfWeek(val) {
val = coerceNumberProperty(val);
if (val >= 0 && val <= 6 && val !== this._firstDayOfWeek) {
this._firstDayOfWeek = val;
if (this.initiated) {
this.generateWeekDays();
this.generateCalendar();
this.cdRef.markForCheck();
}
}
}
get selectMode() {
return this._selectMode;
}
set selectMode(val) {
this._selectMode = val;
if (this.initiated) {
this.generateCalendar();
this.cdRef.markForCheck();
}
}
get selected() {
return this._selected;
}
set selected(value) {
const oldSelected = this._selected;
value = this.dateTimeAdapter.deserialize(value);
this._selected = this.getValidDate(value);
if (!this.dateTimeAdapter.isSameDay(oldSelected, this._selected)) {
this.setSelectedDates();
}
}
get selecteds() {
return this._selecteds;
}
set selecteds(values) {
this._selecteds = values.map(v => {
v = this.dateTimeAdapter.deserialize(v);
return this.getValidDate(v);
});
this.setSelectedDates();
}
get pickerMoment() {
return this._pickerMoment;
}
set pickerMoment(value) {
const oldMoment = this._pickerMoment;
value = this.dateTimeAdapter.deserialize(value);
this._pickerMoment =
this.getValidDate(value) || this.dateTimeAdapter.now();
this.firstDateOfMonth = this.dateTimeAdapter.createDate(this.dateTimeAdapter.getYear(this._pickerMoment), this.dateTimeAdapter.getMonth(this._pickerMoment), 1);
if (!this.isSameMonth(oldMoment, this._pickerMoment) &&
this.initiated) {
this.generateCalendar();
}
}
get dateFilter() {
return this._dateFilter;
}
set dateFilter(filter) {
this._dateFilter = filter;
if (this.initiated) {
this.generateCalendar();
this.cdRef.markForCheck();
}
}
get minDate() {
return this._minDate;
}
set minDate(value) {
value = this.dateTimeAdapter.deserialize(value);
this._minDate = this.getValidDate(value);
if (this.initiated) {
this.generateCalendar();
this.cdRef.markForCheck();
}
}
get maxDate() {
return this._maxDate;
}
set maxDate(value) {
value = this.dateTimeAdapter.deserialize(value);
this._maxDate = this.getValidDate(value);
if (this.initiated) {
this.generateCalendar();
this.cdRef.markForCheck();
}
}
get weekdays() {
return this._weekdays;
}
get days() {
return this._days;
}
get activeCell() {
if (this.pickerMoment) {
return (this.dateTimeAdapter.getDate(this.pickerMoment) +
this.firstRowOffset -
1);
}
}
get isInSingleMode() {
return this.selectMode === 'single';
}
get isInRangeMode() {
return (this.selectMode === 'range' ||
this.selectMode === 'rangeFrom' ||
this.selectMode === 'rangeTo');
}
get owlDTCalendarView() {
return true;
}
ngOnInit() {
this.generateWeekDays();
this.localeSub = this.dateTimeAdapter.localeChanges.subscribe(() => {
this.generateWeekDays();
this.generateCalendar();
this.cdRef.markForCheck();
});
}
ngAfterContentInit() {
this.generateCalendar();
this.initiated = true;
}
ngOnDestroy() {
this.localeSub.unsubscribe();
}
/**
* Handle a calendarCell selected
*/
selectCalendarCell(cell) {
// Cases in which the date would not be selected
// 1, the calendar cell is NOT enabled (is NOT valid)
// 2, the selected date is NOT in current picker's month and the hideOtherMonths is enabled
if (!cell.enabled || (this.hideOtherMonths && cell.out)) {
return;
}
this.selectDate(cell.value);
}
/**
* Handle a new date selected
*/
selectDate(date) {
const daysDiff = date - 1;
const selected = this.dateTimeAdapter.addCalendarDays(this.firstDateOfMonth, daysDiff);
this.selectedChange.emit(selected);
this.userSelection.emit();
}
/**
* Handle keydown event on calendar body
*/
handleCalendarKeydown(event) {
let moment;
switch (event.keyCode) {
// minus 1 day
case LEFT_ARROW:
moment = this.dateTimeAdapter.addCalendarDays(this.pickerMoment, -1);
this.pickerMomentChange.emit(moment);
break;
// add 1 day
case RIGHT_ARROW:
moment = this.dateTimeAdapter.addCalendarDays(this.pickerMoment, 1);
this.pickerMomentChange.emit(moment);
break;
// minus 1 week
case UP_ARROW:
moment = this.dateTimeAdapter.addCalendarDays(this.pickerMoment, -7);
this.pickerMomentChange.emit(moment);
break;
// add 1 week
case DOWN_ARROW:
moment = this.dateTimeAdapter.addCalendarDays(this.pickerMoment, 7);
this.pickerMomentChange.emit(moment);
break;
// move to first day of current month
case HOME:
moment = this.dateTimeAdapter.addCalendarDays(this.pickerMoment, 1 - this.dateTimeAdapter.getDate(this.pickerMoment));
this.pickerMomentChange.emit(moment);
break;
// move to last day of current month
case END:
moment = this.dateTimeAdapter.addCalendarDays(this.pickerMoment, this.dateTimeAdapter.getNumDaysInMonth(this.pickerMoment) -
this.dateTimeAdapter.getDate(this.pickerMoment));
this.pickerMomentChange.emit(moment);
break;
// minus 1 month (or 1 year)
case PAGE_UP:
moment = event.altKey
? this.dateTimeAdapter.addCalendarYears(this.pickerMoment, -1)
: this.dateTimeAdapter.addCalendarMonths(this.pickerMoment, -1);
this.pickerMomentChange.emit(moment);
break;
// add 1 month (or 1 year)
case PAGE_DOWN:
moment = event.altKey
? this.dateTimeAdapter.addCalendarYears(this.pickerMoment, 1)
: this.dateTimeAdapter.addCalendarMonths(this.pickerMoment, 1);
this.pickerMomentChange.emit(moment);
break;
// select the pickerMoment
case ENTER:
if (!this.dateFilter || this.dateFilter(this.pickerMoment)) {
this.selectDate(this.dateTimeAdapter.getDate(this.pickerMoment));
}
break;
default:
return;
}
this.focusActiveCell();
event.preventDefault();
}
/**
* Generate the calendar weekdays array
* */
generateWeekDays() {
const longWeekdays = this.dateTimeAdapter.getDayOfWeekNames('long');
const shortWeekdays = this.dateTimeAdapter.getDayOfWeekNames('short');
const narrowWeekdays = this.dateTimeAdapter.getDayOfWeekNames('narrow');
const firstDayOfWeek = this.firstDayOfWeek;
const weekdays = longWeekdays.map((long, i) => {
return { long, short: shortWeekdays[i], narrow: narrowWeekdays[i] };
});
this._weekdays = weekdays
.slice(firstDayOfWeek)
.concat(weekdays.slice(0, firstDayOfWeek));
this.dateNames = this.dateTimeAdapter.getDateNames();
return;
}
/**
* Generate the calendar days array
* */
generateCalendar() {
if (!this.pickerMoment) {
return;
}
this.todayDate = null;
// the first weekday of the month
const startWeekdayOfMonth = this.dateTimeAdapter.getDay(this.firstDateOfMonth);
const firstDayOfWeek = this.firstDayOfWeek;
// the amount of days from the first date of the month
// if it is < 0, it means the date is in previous month
let daysDiff = 0 -
((startWeekdayOfMonth + (DAYS_PER_WEEK - firstDayOfWeek)) %
DAYS_PER_WEEK);
// the index of cell that contains the first date of the month
this.firstRowOffset = Math.abs(daysDiff);
this._days = [];
for (let i = 0; i < WEEKS_PER_VIEW; i++) {
const week = [];
for (let j = 0; j < DAYS_PER_WEEK; j++) {
const date = this.dateTimeAdapter.addCalendarDays(this.firstDateOfMonth, daysDiff);
const dateCell = this.createDateCell(date, daysDiff);
// check if the date is today
if (this.dateTimeAdapter.isSameDay(this.dateTimeAdapter.now(), date)) {
this.todayDate = daysDiff + 1;
}
week.push(dateCell);
daysDiff += 1;
}
this._days.push(week);
}
this.setSelectedDates();
}
/**
* Creates CalendarCell for days.
*/
createDateCell(date, daysDiff) {
// total days of the month
const daysInMonth = this.dateTimeAdapter.getNumDaysInMonth(this.pickerMoment);
const dateNum = this.dateTimeAdapter.getDate(date);
// const dateName = this.dateNames[dateNum - 1];
const dateName = dateNum.toString();
const ariaLabel = this.dateTimeAdapter.format(date, this.dateTimeFormats.dateA11yLabel);
// check if the date if selectable
const enabled = this.isDateEnabled(date);
// check if date is not in current month
const dayValue = daysDiff + 1;
const out = dayValue < 1 || dayValue > daysInMonth;
const cellClass = 'owl-dt-day-' + this.dateTimeAdapter.getDay(date);
return new CalendarCell(dayValue, dateName, ariaLabel, enabled, out, cellClass);
}
/**
* Check if the date is valid
*/
isDateEnabled(date) {
return (!!date &&
(!this.dateFilter || this.dateFilter(date)) &&
(!this.minDate ||
this.dateTimeAdapter.compare(date, this.minDate) >= 0) &&
(!this.maxDate ||
this.dateTimeAdapter.compare(date, this.maxDate) <= 0));
}
/**
* Get a valid date object
*/
getValidDate(obj) {
return this.dateTimeAdapter.isDateInstance(obj) &&
this.dateTimeAdapter.isValid(obj)
? obj
: null;
}
/**
* Check if the give dates are none-null and in the same month
*/
isSameMonth(dateLeft, dateRight) {
return !!(dateLeft &&
dateRight &&
this.dateTimeAdapter.isValid(dateLeft) &&
this.dateTimeAdapter.isValid(dateRight) &&
this.dateTimeAdapter.getYear(dateLeft) ===
this.dateTimeAdapter.getYear(dateRight) &&
this.dateTimeAdapter.getMonth(dateLeft) ===
this.dateTimeAdapter.getMonth(dateRight));
}
/**
* Set the selectedDates value.
* In single mode, it has only one value which represent the selected date
* In range mode, it would has two values, one for the fromValue and the other for the toValue
* */
setSelectedDates() {
this.selectedDates = [];
if (!this.firstDateOfMonth) {
return;
}
if (this.isInSingleMode && this.selected) {
const dayDiff = this.dateTimeAdapter.differenceInCalendarDays(this.selected, this.firstDateOfMonth);
this.selectedDates[0] = dayDiff + 1;
return;
}
if (this.isInRangeMode && this.selecteds) {
this.selectedDates = this.selecteds.map(selected => {
if (this.dateTimeAdapter.isValid(selected)) {
const dayDiff = this.dateTimeAdapter.differenceInCalendarDays(selected, this.firstDateOfMonth);
return dayDiff + 1;
}
else {
return null;
}
});
}
}
focusActiveCell() {
this.calendarBodyElm.focusActiveCell();
}
}
/** @nocollapse */ OwlMonthViewComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: OwlMonthViewComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: DateTimeAdapter, optional: true }, { token: OWL_DATE_TIME_FORMATS, optional: true }], target: i0.ɵɵFactoryTarget.Component });
/** @nocollapse */ OwlMonthViewComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.3.11", type: OwlMonthViewComponent, selector: "owl-date-time-month-view", inputs: { hideOtherMonths: "hideOtherMonths", firstDayOfWeek: "firstDayOfWeek", selectMode: "selectMode", selected: "selected", selecteds: "selecteds", pickerMoment: "pickerMoment", dateFilter: "dateFilter", minDate: "minDate", maxDate: "maxDate" }, outputs: { selectedChange: "selectedChange", userSelection: "userSelection", pickerMomentChange: "pickerMomentChange" }, host: { properties: { "class.owl-dt-calendar-view": "owlDTCalendarView" } }, viewQueries: [{ propertyName: "calendarBodyElm", first: true, predicate: OwlCalendarBodyComponent, descendants: true, static: true }], exportAs: ["owlYearView"], ngImport: i0, template: "<table class=\"owl-dt-calendar-table owl-dt-calendar-month-table\"\n [class.owl-dt-calendar-only-current-month]=\"hideOtherMonths\">\n <thead class=\"owl-dt-calendar-header\">\n <tr class=\"owl-dt-weekdays\">\n <th *ngFor=\"let weekday of weekdays\"\n [attr.aria-label]=\"weekday.long\"\n class=\"owl-dt-weekday\" scope=\"col\">\n <span>{{weekday.short}}</span>\n </th>\n </tr>\n <tr>\n <th class=\"owl-dt-calendar-table-divider\" aria-hidden=\"true\" colspan=\"7\"></th>\n </tr>\n </thead>\n <tbody owl-date-time-calendar-body role=\"grid\"\n [rows]=\"days\" [todayValue]=\"todayDate\"\n [selectedValues]=\"selectedDates\"\n [selectMode]=\"selectMode\"\n [activeCell]=\"activeCell\"\n (keydown)=\"handleCalendarKeydown($event)\"\n (select)=\"selectCalendarCell($event)\">\n </tbody>\n</table>\n", styles: [""], components: [{ type: OwlCalendarBodyComponent, selector: "[owl-date-time-calendar-body]", inputs: ["activeCell", "rows", "numCols", "cellRatio", "todayValue", "selectedValues", "selectMode"], outputs: ["select"], exportAs: ["owlDateTimeCalendarBody"] }], directives: [{ type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: OwlMonthViewComponent, decorators: [{
type: Component,
args: [{ selector: 'owl-date-time-month-view', exportAs: 'owlYearView', host: {
'[class.owl-dt-calendar-view]': 'owlDTCalendarView'
}, preserveWhitespaces: false, changeDetection: ChangeDetectionStrategy.OnPush, template: "<table class=\"owl-dt-calendar-table owl-dt-calendar-month-table\"\n [class.owl-dt-calendar-only-current-month]=\"hideOtherMonths\">\n <thead class=\"owl-dt-calendar-header\">\n <tr class=\"owl-dt-weekdays\">\n <th *ngFor=\"let weekday of weekdays\"\n [attr.aria-label]=\"weekday.long\"\n class=\"owl-dt-weekday\" scope=\"col\">\n <span>{{weekday.short}}</span>\n </th>\n </tr>\n <tr>\n <th class=\"owl-dt-calendar-table-divider\" aria-hidden=\"true\" colspan=\"7\"></th>\n </tr>\n </thead>\n <tbody owl-date-time-calendar-body role=\"grid\"\n [rows]=\"days\" [todayValue]=\"todayDate\"\n [selectedValues]=\"selectedDates\"\n [selectMode]=\"selectMode\"\n [activeCell]=\"activeCell\"\n (keydown)=\"handleCalendarKeydown($event)\"\n (select)=\"selectCalendarCell($event)\">\n </tbody>\n</table>\n", styles: [""] }]
}], ctorParameters: function () {
return [{ type: i0.ChangeDetectorRef }, { type: DateTimeAdapter, decorators: [{
type: Optional
}] }, { type: undefined, decorators: [{
type: Optional
}, {
type: Inject,
args: [OWL_DATE_TIME_FORMATS]
}] }];
}, propDecorators: { hideOtherMonths: [{
type: Input
}], firstDayOfWeek: [{
type: Input
}], selectMode: [{
type: Input
}], selected: [{
type: Input
}], selecteds: [{
type: Input
}], pickerMoment: [{
type: Input
}], dateFilter: [{
type: Input
}], minDate: [{
type: Input
}], maxDate: [{
type: Input
}], selectedChange: [{
type: Output
}], userSelection: [{
type: Output
}], pickerMomentChange: [{
type: Output
}], calendarBodyElm: [{
type: ViewChild,
args: [OwlCalendarBodyComponent, { static: true }]
}] } });
/**
* calendar-year-view.component
*/
const MONTHS_PER_YEAR = 12;
const MONTHS_PER_ROW = 3;
class OwlYearViewComponent {
constructor(cdRef, dateTimeAdapter, dateTimeFormats) {
this.cdRef = cdRef;
this.dateTimeAdapter = dateTimeAdapter;
this.dateTimeFormats = dateTimeFormats;
/**
* The select mode of the picker;
* */
this._selectMode = 'single';
this._selecteds = [];
this.localeSub = Subscription.EMPTY;
this.initiated = false;
/**
* An array to hold all selectedDates' month value
* the value is the month number in current year
* */
this.selectedMonths = [];
/**
* Callback to invoke when a new month is selected
* */
this.change = new EventEmitter();
/**
* Emits the selected year. This doesn't imply a change on the selected date
* */
this.monthSelected = new EventEmitter();
/** Emits when any date is activated. */
this.pickerMomentChange = new EventEmitter();
/** Emits when use keyboard enter to select a calendar cell */
this.keyboardEnter = new EventEmitter();
this.monthNames = this.dateTimeAdapter.getMonthNames('short');
}
get selectMode() {
return this._selectMode;
}
set selectMode(val) {
this._selectMode = val;
if (this.initiated) {
this.generateMonthList();
this.cdRef.markForCheck();
}
}
get selected() {
return this._selected;
}
set selected(value) {
value = this.dateTimeAdapter.deserialize(value);
this._selected = this.getValidDate(value);
this.setSelectedMonths();
}
get selecteds() {
return this._selecteds;
}
set selecteds(values) {
this._selecteds = [];
for (let i = 0; i < values.length; i++) {
const value = this.dateTimeAdapter.deserialize(values[i]);
this._selecteds.push(this.getValidDate(value));
}
this.setSelectedMonths();
}
get pickerMoment() {
return this._pickerMoment;
}
set pickerMoment(value) {
const oldMoment = this._pickerMoment;
value = this.dateTimeAdapter.deserialize(value);
this._pickerMoment =
this.getValidDate(value) || this.dateTimeAdapter.now();
if (!this.hasSameYear(oldMoment, this._pickerMoment) &&
this.initiated) {
this.generateMonthList();
}
}
get dateFilter() {
return this._dateFilter;
}
set dateFilter(filter) {
this._dateFilter = filter;
if (this.initiated) {
this.generateMonthList();
}
}
get minDate() {
return this._minDate;
}
set minDate(value) {
value = this.dateTimeAdapter.deserialize(value);
this._minDate = this.getValidDate(value);
if (this.initiated) {
this.generateMonthList();
}
}
get maxDate() {
return this._maxDate;
}
set maxDate(value) {
value = this.dateTimeAdapter.deserialize(value);
this._maxDate = this.getValidDate(value);
if (this.initiated) {
this.generateMonthList();
}
}
get months() {
return this._months;
}
get activeCell() {
if (this._pickerMoment) {
return this.dateTimeAdapter.getMonth(this._pickerMoment);
}
}
get isInSingleMode() {
return this.selectMode === 'single';
}
get isInRangeMode() {
return (this.selectMode === 'range' ||
this.selectMode === 'rangeFrom' ||
this.selectMode === 'rangeTo');
}
get owlDTCalendarView() {
return true;
}
ngOnInit() {
this.localeSub = this.dateTimeAdapter.localeChanges.subscribe(() => {
this.generateMonthList();
this.cdRef.markForCheck();
});
}
ngAfterContentInit() {
this.generateMonthList();
this.initiated = true;
}
ngOnDestroy() {
this.localeSub.unsubscribe();
}
/**
* Handle a calendarCell selected
*/
selectCalendarCell(cell) {
this.selectMonth(cell.value);
}
/**
* Handle a new month selected
*/
selectMonth(month) {
const firstDateOfMonth = this.dateTimeAdapter.createDate(this.dateTimeAdapter.getYear(this.pickerMoment), month, 1);
this.monthSelected.emit(firstDateOfMonth);
const daysInMonth = this.dateTimeAdapter.getNumDaysInMonth(firstDateOfMonth);
const result = this.dateTimeAdapter.createDate(this.dateTimeAdapter.getYear(this.pickerMoment), month, Math.min(daysInMonth, this.dateTimeAdapter.getDate(this.pickerMoment)), this.dateTimeAdapter.getHours(this.pickerMoment), this.dateTimeAdapter.getMinutes(this.pickerMoment), this.dateTimeAdapter.getSeconds(this.pickerMoment));
this.change.emit(result);
}
/**
* Handle keydown event on calendar body
*/
handleCalendarKeydown(event) {
let moment;
switch (event.keyCode) {
// minus 1 month
case LEFT_ARROW:
moment = this.dateTimeAdapter.addCalendarMonths(this.pickerMoment, -1);
this.pickerMomentChange.emit(moment);
break;
// add 1 month
case RIGHT_ARROW:
moment = this.dateTimeAdapter.addCalendarMonths(this.pickerMoment, 1);
this.pickerMomentChange.emit(moment);
break;
// minus 3 months
case UP_ARROW:
moment = this.dateTimeAdapter.addCalendarMonths(this.pickerMoment, -3);
this.pickerMomentChange.emit(moment);
break;
// add 3 months
case DOWN_ARROW:
moment = this.dateTimeAdapter.addCalendarMonths(this.pickerMoment, 3);
this.pickerMomentChange.emit(moment);
break;
// move to first month of current year
case HOME:
moment = this.dateTimeAdapter.addCalendarMonths(this.pickerMoment, -this.dateTimeAdapter.getMonth(this.pickerMoment));
this.pickerMomentChange.emit(moment);
break;
// move to last month of current year
case END:
moment = this.dateTimeAdapter.addCalendarMonths(this.pickerMoment, 11 - this.dateTimeAdapter.getMonth(this.pickerMoment));
this.pickerMomentChange.emit(moment);
break;
// minus 1 year (or 10 year)
case PAGE_UP:
moment = this.dateTimeAdapter.addCalendarYears(this.pickerMoment, event.altKey ? -10 : -1);
this.pickerMomentChange.emit(moment);
break;
// add 1 year (or 10 year)
case PAGE_DOWN:
moment = this.dateTimeAdapter.addCalendarYears(this.pickerMoment, event.altKey ? 10 : 1);
this.pickerMomentChange.emit(moment);
break;
// Select current month
case ENTER:
this.selectMonth(this.dateTimeAdapter.getMonth(this.pickerMoment));
this.keyboardEnter.emit();
break;
default:
return;
}
this.focusActiveCell();
event.preventDefault();
}
/**
* Generate the calendar month list
* */
generateMonthList() {
if (!this.pickerMoment) {
return;
}
this.setSelectedMonths();
this.todayMonth = this.getMonthInCurrentYear(this.dateTimeAdapter.now());
this._months = [];
for (let i = 0; i < MONTHS_PER_YEAR / MONTHS_PER_ROW; i++) {
const row = [];
for (let j = 0; j < MONTHS_PER_ROW; j++) {
const month = j + i * MONTHS_PER_ROW;
const monthCell = this.createMonthCell(month);
row.push(monthCell);
}
this._months.push(row);
}
return;
}
/**
* Creates an CalendarCell for the given month.
*/
createMonthCell(month) {
const startDateOfMonth = this.dateTimeAdapter.createDate(this.dateTimeAdapter.getYear(this.pickerMoment), month, 1);
const ariaLabel = this.dateTimeAdapter.format(startDateOfMonth, this.dateTimeFormats.monthYearA11yLabel);
const cellClass = 'owl-dt-month-' + month;
return new CalendarCell(month, this.monthNames[month], ariaLabel, this.isMonthEnabled(month), false, cellClass);
}
/**
* Check if the given month is enable
*/
isMonthEnabled(month) {
const firstDateOfMonth = this.dateTimeAdapter.createDate(this.dateTimeAdapter.getYear(this.pickerMoment), month, 1);
// If any date in the month is selectable,
// we count the month as enable
for (let date = firstDateOfMonth; this.dateTimeAdapter.getMonth(date) === month; date = this.dateTimeAdapter.addCalendarDays(date, 1)) {
if (!!date &&
(!this.dateFilter || this.dateFilter(date)) &&
(!this.minDate ||
this.dateTimeAdapter.compare(date, this.minDate) >= 0) &&
(!this.maxDate ||
this.dateTimeAdapter.compare(date, this.maxDate) <= 0)) {
return true;
}
}
return false;
}
/**
* Gets the month in this year that the given Date falls on.
* Returns null if the given Date is in another year.
*/
getMonthInCurrentYear(date) {
if (this.getValidDate(date) && this.getValidDate(this._pickerMoment)) {
const result = this.dateTimeAdapter.compareYear(date, this._pickerMoment);
// < 0 : the given date's year is before pickerMoment's year, we return -1 as selected month value.
// > 0 : the given date's year is after pickerMoment's year, we return 12 as selected month value.
// 0 : the give date's year is same as the pickerMoment's year, we return the actual month value.
if (result < 0) {
return -1;
}
else if (result > 0) {
return 12;
}
else {
return this.dateTimeAdapter.getMonth(date);
}
}
else {
return null;
}
}
/**
* Set the selectedMonths value
* In single mode, it has only one value which represent the month the selected date in
* In range mode, it would has two values, one for the month the fromValue in and the other for the month the toValue in
* */
setSelectedMonths() {
this.selectedMonths = [];
if (this.isInSingleMode && this.selected) {
this.selectedMonths[0] = this.getMonthInCurrentYear(this.selected);
}
if (this.isInRangeMode && this.selecteds) {
this.selectedMonths[0] = this.getMonthInCurrentYear(this.selecteds[0]);
this.selectedMonths[1] = this.getMonthInCurrentYear(this.selecteds[1]);
}
}
/**
* Check the given dates are in the same year
*/
hasSameYear(dateLeft, dateRight) {
return !!(dateLeft &&
dateRight &&