ng-pick-datetime-ex
Version:
Angular Date Time Picker
1,036 lines (1,028 loc) • 268 kB
JavaScript
import * as i0 from '@angular/core';
import { Directive, Input, InjectionToken, Injectable, inject, LOCALE_ID, EventEmitter, Component, ChangeDetectionStrategy, Output, Optional, ViewChild, Inject, TemplateRef, SkipSelf, forwardRef, Pipe, NgModule } from '@angular/core';
import * as i3 from '@angular/common';
import { DOCUMENT, CommonModule } from '@angular/common';
import * as i4 from '@angular/cdk/a11y';
import { A11yModule } from '@angular/cdk/a11y';
import * as i1 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, 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$1 from '@angular/cdk/platform';
import { PlatformModule } from '@angular/cdk/platform';
import _dayjs from 'dayjs';
import localeData from 'dayjs/plugin/localeData';
import objectSupport from 'dayjs/plugin/objectSupport';
import localizedFormat from 'dayjs/plugin/localizedFormat';
import utc from 'dayjs/plugin/utc';
/**
* date-time-picker-trigger.directive
*/
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();
});
}
}
OwlDateTimeTriggerDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.4", ngImport: i0, type: OwlDateTimeTriggerDirective, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Directive });
OwlDateTimeTriggerDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.0.4", 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: "15.0.4", 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';
}
}
OwlDateTimeIntl.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.4", ngImport: i0, type: OwlDateTimeIntl, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
OwlDateTimeIntl.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.0.4", ngImport: i0, type: OwlDateTimeIntl, providedIn: 'root' });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.4", 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.milliseondsInMinute = 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();
});
});
}
}
OwlCalendarBodyComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.4", ngImport: i0, type: OwlCalendarBodyComponent, deps: [{ token: i0.ElementRef }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component });
OwlCalendarBodyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.0.4", 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: [""], dependencies: [{ kind: "directive", type: i3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.4", 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" }]
}], 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-multi-year-view.component
*/
const YEARS_PER_ROW = 3;
const YEAR_ROWS = 7;
class OwlMultiYearViewComponent {
constructor(cdRef, pickerIntl, dateTimeAdapter) {
this.cdRef = cdRef;
this.pickerIntl = pickerIntl;
this.dateTimeAdapter = dateTimeAdapter;
/**
* The select mode of the picker;
* */
this._selectMode = 'single';
this._selecteds = [];
this.initiated = false;
/**
* 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.yearSelected = new EventEmitter();
/** Emits when any date is activated. */
this.pickerDayjsChange = new EventEmitter();
/** Emits when use keyboard enter to select a calendar cell */
this.keyboardEnter = new EventEmitter();
}
get selectMode() {
return this._selectMode;
}
set selectMode(val) {
this._selectMode = val;
if (this.initiated) {
this.setSelectedYears();
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.setSelectedYears();
}
}
get selecteds() {
return this._selecteds;
}
set selecteds(values) {
this._selecteds = values.map((v) => {
v = this.dateTimeAdapter.deserialize(v);
return this.getValidDate(v);
});
this.setSelectedYears();
}
get pickerDayjs() {
return this._pickerDayjs;
}
set pickerDayjs(value) {
const oldDayjs = this._pickerDayjs;
value = this.dateTimeAdapter.deserialize(value);
this._pickerDayjs = this.getValidDate(value) || this.dateTimeAdapter.now();
if (oldDayjs && this._pickerDayjs &&
!this.isSameYearList(oldDayjs, this._pickerDayjs)) {
this.generateYearList();
}
}
get dateFilter() {
return this._dateFilter;
}
set dateFilter(filter) {
this._dateFilter = filter;
if (this.initiated) {
this.generateYearList();
}
}
get minDate() {
return this._minDate;
}
set minDate(value) {
value = this.dateTimeAdapter.deserialize(value);
this._minDate = this.getValidDate(value);
if (this.initiated) {
this.generateYearList();
}
}
get maxDate() {
return this._maxDate;
}
set maxDate(value) {
value = this.dateTimeAdapter.deserialize(value);
this._maxDate = this.getValidDate(value);
if (this.initiated) {
this.generateYearList();
}
}
get todayYear() {
return this._todayYear;
}
get years() {
return this._years;
}
get selectedYears() {
return this._selectedYears;
}
get isInSingleMode() {
return this.selectMode === 'single';
}
get isInRangeMode() {
return this.selectMode === 'range' || this.selectMode === 'rangeFrom'
|| this.selectMode === 'rangeTo';
}
get activeCell() {
if (this._pickerDayjs) {
return this.dateTimeAdapter.getYear(this._pickerDayjs) % (YEARS_PER_ROW * YEAR_ROWS);
}
}
get tableHeader() {
if (this._years && this._years.length > 0) {
return `${this._years[0][0].displayValue} ~ ${this._years[YEAR_ROWS - 1][YEARS_PER_ROW - 1].displayValue}`;
}
}
get prevButtonLabel() {
return this.pickerIntl.prevMultiYearLabel;
}
get nextButtonLabel() {
return this.pickerIntl.nextMultiYearLabel;
}
get owlDTCalendarView() {
return true;
}
get owlDTCalendarMultiYearView() {
return true;
}
ngOnInit() {
}
ngAfterContentInit() {
this._todayYear = this.dateTimeAdapter.getYear(this.dateTimeAdapter.now());
this.generateYearList();
this.initiated = true;
}
/**
* Handle a calendarCell selected
*/
selectCalendarCell(cell) {
this.selectYear(cell.value);
}
selectYear(year) {
this.yearSelected.emit(this.dateTimeAdapter.createDate(year, 0, 1));
const firstDateOfMonth = this.dateTimeAdapter.createDate(year, this.dateTimeAdapter.getMonth(this.pickerDayjs), 1);
const daysInMonth = this.dateTimeAdapter.getNumDaysInMonth(firstDateOfMonth);
const selected = this.dateTimeAdapter.createDate(year, this.dateTimeAdapter.getMonth(this.pickerDayjs), Math.min(daysInMonth, this.dateTimeAdapter.getDate(this.pickerDayjs)), this.dateTimeAdapter.getHours(this.pickerDayjs), this.dateTimeAdapter.getMinutes(this.pickerDayjs), this.dateTimeAdapter.getSeconds(this.pickerDayjs));
this.change.emit(selected);
}
/**
* Generate the previous year list
* */
prevYearList(event) {
this._pickerDayjs = this.dateTimeAdapter.addCalendarYears(this.pickerDayjs, -1 * YEAR_ROWS * YEARS_PER_ROW);
this.generateYearList();
event.preventDefault();
}
/**
* Generate the next year list
* */
nextYearList(event) {
this._pickerDayjs = this.dateTimeAdapter.addCalendarYears(this.pickerDayjs, YEAR_ROWS * YEARS_PER_ROW);
this.generateYearList();
event.preventDefault();
}
generateYearList() {
this._years = [];
const pickerDayjsYear = this.dateTimeAdapter.getYear(this._pickerDayjs);
const offset = pickerDayjsYear % (YEARS_PER_ROW * YEAR_ROWS);
for (let i = 0; i < YEAR_ROWS; i++) {
const row = [];
for (let j = 0; j < YEARS_PER_ROW; j++) {
const year = pickerDayjsYear - offset + (j + i * YEARS_PER_ROW);
const yearCell = this.createYearCell(year);
row.push(yearCell);
}
this._years.push(row);
}
return;
}
/** Whether the previous period button is enabled. */
previousEnabled() {
if (!this.minDate) {
return true;
}
return !this.minDate || !this.isSameYearList(this._pickerDayjs, this.minDate);
}
/** Whether the next period button is enabled. */
nextEnabled() {
return !this.maxDate || !this.isSameYearList(this._pickerDayjs, this.maxDate);
}
handleCalendarKeydown(event) {
let dayjs;
switch (event.keyCode) {
// minus 1 year
case LEFT_ARROW:
dayjs = this.dateTimeAdapter.addCalendarYears(this._pickerDayjs, -1);
this.pickerDayjsChange.emit(dayjs);
break;
// add 1 year
case RIGHT_ARROW:
dayjs = this.dateTimeAdapter.addCalendarYears(this._pickerDayjs, 1);
this.pickerDayjsChange.emit(dayjs);
break;
// minus 3 years
case UP_ARROW:
dayjs = this.dateTimeAdapter.addCalendarYears(this._pickerDayjs, -1 * YEARS_PER_ROW);
this.pickerDayjsChange.emit(dayjs);
break;
// add 3 years
case DOWN_ARROW:
dayjs = this.dateTimeAdapter.addCalendarYears(this._pickerDayjs, YEARS_PER_ROW);
this.pickerDayjsChange.emit(dayjs);
break;
// go to the first year of the year page
case HOME:
dayjs = this.dateTimeAdapter.addCalendarYears(this._pickerDayjs, -this.dateTimeAdapter.getYear(this._pickerDayjs) % (YEARS_PER_ROW * YEAR_ROWS));
this.pickerDayjsChange.emit(dayjs);
break;
// go to the last year of the year page
case END:
dayjs = this.dateTimeAdapter.addCalendarYears(this._pickerDayjs, (YEARS_PER_ROW * YEAR_ROWS) - this.dateTimeAdapter.getYear(this._pickerDayjs) % (YEARS_PER_ROW * YEAR_ROWS) - 1);
this.pickerDayjsChange.emit(dayjs);
break;
// minus 1 year page (or 10 year pages)
case PAGE_UP:
dayjs = this.dateTimeAdapter.addCalendarYears(this.pickerDayjs, event.altKey ? -10 * (YEARS_PER_ROW * YEAR_ROWS) : -1 * (YEARS_PER_ROW * YEAR_ROWS));
this.pickerDayjsChange.emit(dayjs);
break;
// add 1 year page (or 10 year pages)
case PAGE_DOWN:
dayjs = this.dateTimeAdapter.addCalendarYears(this.pickerDayjs, event.altKey ? 10 * (YEARS_PER_ROW * YEAR_ROWS) : (YEARS_PER_ROW * YEAR_ROWS));
this.pickerDayjsChange.emit(dayjs);
break;
case ENTER:
this.selectYear(this.dateTimeAdapter.getYear(this._pickerDayjs));
this.keyboardEnter.emit();
break;
default:
return;
}
this.focusActiveCell();
event.preventDefault();
}
/**
* Creates an CalendarCell for the given year.
*/
createYearCell(year) {
const startDateOfYear = this.dateTimeAdapter.createDate(year, 0, 1);
const ariaLabel = this.dateTimeAdapter.getYearName(startDateOfYear);
const cellClass = 'owl-dt-year-' + year;
return new CalendarCell(year, year.toString(), ariaLabel, this.isYearEnabled(year), false, cellClass);
}
setSelectedYears() {
this._selectedYears = [];
if (this.isInSingleMode && this.selected) {
this._selectedYears[0] = this.dateTimeAdapter.getYear(this.selected);
}
if (this.isInRangeMode && this.selecteds) {
this._selectedYears = this.selecteds.map((selected) => {
if (this.dateTimeAdapter.isValid(selected)) {
return this.dateTimeAdapter.getYear(selected);
}
else {
return null;
}
});
}
}
/** Whether the given year is enabled. */
isYearEnabled(year) {
// disable if the year is greater than maxDate lower than minDate
if (year === undefined || year === null ||
(this.maxDate && year > this.dateTimeAdapter.getYear(this.maxDate)) ||
(this.minDate && year < this.dateTimeAdapter.getYear(this.minDate))) {
return false;
}
// enable if it reaches here and there's no filter defined
if (!this.dateFilter) {
return true;
}
const firstOfYear = this.dateTimeAdapter.createDate(year, 0, 1);
// If any date in the year is enabled count the year as enabled.
for (let date = firstOfYear; this.dateTimeAdapter.getYear(date) == year; date = this.dateTimeAdapter.addCalendarDays(date, 1)) {
if (this.dateFilter(date)) {
return true;
}
}
return false;
}
isSameYearList(date1, date2) {
return Math.floor(this.dateTimeAdapter.getYear(date1) / (YEARS_PER_ROW * YEAR_ROWS)) ===
Math.floor(this.dateTimeAdapter.getYear(date2) / (YEARS_PER_ROW * YEAR_ROWS));
}
/**
* Get a valid date object
*/
getValidDate(obj) {
return (this.dateTimeAdapter.isDateInstance(obj) && this.dateTimeAdapter.isValid(obj)) ? obj : null;
}
focusActiveCell() {
this.calendarBodyElm.focusActiveCell();
}
}
OwlMultiYearViewComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.4", ngImport: i0, type: OwlMultiYearViewComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: OwlDateTimeIntl }, { token: DateTimeAdapter, optional: true }], target: i0.ɵɵFactoryTarget.Component });
OwlMultiYearViewComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.0.4", type: OwlMultiYearViewComponent, selector: "owl-date-time-multi-year-view", inputs: { selectMode: "selectMode", selected: "selected", selecteds: "selecteds", pickerDayjs: "pickerDayjs", dateFilter: "dateFilter", minDate: "minDate", maxDate: "maxDate" }, outputs: { change: "change", yearSelected: "yearSelected", pickerDayjsChange: "pickerDayjsChange", keyboardEnter: "keyboardEnter" }, host: { properties: { "class.owl-dt-calendar-view": "owlDTCalendarView", "class.owl-dt-calendar-multi-year-view": "owlDTCalendarMultiYearView" } }, viewQueries: [{ propertyName: "calendarBodyElm", first: true, predicate: OwlCalendarBodyComponent, descendants: true, static: true }], ngImport: i0, template: "<button class=\"owl-dt-control-button owl-dt-control-arrow-button\"\n [disabled]=\"!previousEnabled()\" [attr.aria-label]=\"prevButtonLabel\"\n type=\"button\" tabindex=\"0\" (click)=\"prevYearList($event)\">\n <span class=\"owl-dt-control-button-content\" tabindex=\"-1\">\n <!-- <editor-fold desc=\"SVG Arrow Left\"> -->\n <svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n version=\"1.1\" x=\"0px\" y=\"0px\" viewBox=\"0 0 250.738 250.738\"\n style=\"enable-background:new 0 0 250.738 250.738;\" xml:space=\"preserve\"\n width=\"100%\" height=\"100%\">\n <path style=\"fill-rule: evenodd; clip-rule: evenodd;\" d=\"M96.633,125.369l95.053-94.533c7.101-7.055,7.101-18.492,0-25.546 c-7.1-7.054-18.613-7.054-25.714,0L58.989,111.689c-3.784,3.759-5.487,8.759-5.238,13.68c-0.249,4.922,1.454,9.921,5.238,13.681 l106.983,106.398c7.101,7.055,18.613,7.055,25.714,0c7.101-7.054,7.101-18.491,0-25.544L96.633,125.369z\"/>\n </svg>\n <!-- </editor-fold> -->\n </span>\n</button>\n<table class=\"owl-dt-calendar-table owl-dt-calendar-multi-year-table\">\n <thead class=\"owl-dt-calendar-header\">\n <tr>\n <th colspan=\"3\">{{tableHeader}}</th>\n </tr>\n </thead>\n <tbody owl-date-time-calendar-body role=\"grid\"\n [rows]=\"years\" [numCols]=\"3\" [cellRatio]=\"3 / 7\"\n [activeCell]=\"activeCell\"\n [todayValue]=\"todayYear\"\n [selectedValues]=\"selectedYears\"\n [selectMode]=\"selectMode\"\n (keydown)=\"handleCalendarKeydown($event)\"\n (select)=\"selectCalendarCell($event)\"></tbody>\n</table>\n<button class=\"owl-dt-control-button owl-dt-control-arrow-button\"\n [disabled]=\"!nextEnabled()\" [attr.aria-label]=\"nextButtonLabel\"\n type=\"button\" tabindex=\"0\" (click)=\"nextYearList($event)\">\n <span class=\"owl-dt-control-button-content\" tabindex=\"-1\">\n <!-- <editor-fold desc=\"SVG Arrow Right\"> -->\n <svg version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n viewBox=\"0 0 250.738 250.738\" style=\"enable-background:new 0 0 250.738 250.738;\" xml:space=\"preserve\">\n <path style=\"fill-rule:evenodd;clip-rule:evenodd;\" d=\"M191.75,111.689L84.766,5.291c-7.1-7.055-18.613-7.055-25.713,0\n c-7.101,7.054-7.101,18.49,0,25.544l95.053,94.534l-95.053,94.533c-7.101,7.054-7.101,18.491,0,25.545\n c7.1,7.054,18.613,7.054,25.713,0L191.75,139.05c3.784-3.759,5.487-8.759,5.238-13.681\n C197.237,120.447,195.534,115.448,191.75,111.689z\"/>\n </svg>\n <!-- </editor-fold> -->\n </span>\n</button>\n", styles: [""], dependencies: [{ kind: "component", type: OwlCalendarBodyComponent, selector: "[owl-date-time-calendar-body]", inputs: ["activeCell", "rows", "numCols", "cellRatio", "todayValue", "selectedValues", "selectMode"], outputs: ["select"], exportAs: ["owlDateTimeCalendarBody"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.4", ngImport: i0, type: OwlMultiYearViewComponent, decorators: [{
type: Component,
args: [{ selector: 'owl-date-time-multi-year-view', host: {
'[class.owl-dt-calendar-view]': 'owlDTCalendarView',
'[class.owl-dt-calendar-multi-year-view]': 'owlDTCalendarMultiYearView'
}, preserveWhitespaces: false, changeDetection: ChangeDetectionStrategy.OnPush, template: "<button class=\"owl-dt-control-button owl-dt-control-arrow-button\"\n [disabled]=\"!previousEnabled()\" [attr.aria-label]=\"prevButtonLabel\"\n type=\"button\" tabindex=\"0\" (click)=\"prevYearList($event)\">\n <span class=\"owl-dt-control-button-content\" tabindex=\"-1\">\n <!-- <editor-fold desc=\"SVG Arrow Left\"> -->\n <svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n version=\"1.1\" x=\"0px\" y=\"0px\" viewBox=\"0 0 250.738 250.738\"\n style=\"enable-background:new 0 0 250.738 250.738;\" xml:space=\"preserve\"\n width=\"100%\" height=\"100%\">\n <path style=\"fill-rule: evenodd; clip-rule: evenodd;\" d=\"M96.633,125.369l95.053-94.533c7.101-7.055,7.101-18.492,0-25.546 c-7.1-7.054-18.613-7.054-25.714,0L58.989,111.689c-3.784,3.759-5.487,8.759-5.238,13.68c-0.249,4.922,1.454,9.921,5.238,13.681 l106.983,106.398c7.101,7.055,18.613,7.055,25.714,0c7.101-7.054,7.101-18.491,0-25.544L96.633,125.369z\"/>\n </svg>\n <!-- </editor-fold> -->\n </span>\n</button>\n<table class=\"owl-dt-calendar-table owl-dt-calendar-multi-year-table\">\n <thead class=\"owl-dt-calendar-header\">\n <tr>\n <th colspan=\"3\">{{tableHeader}}</th>\n </tr>\n </thead>\n <tbody owl-date-time-calendar-body role=\"grid\"\n [rows]=\"years\" [numCols]=\"3\" [cellRatio]=\"3 / 7\"\n [activeCell]=\"activeCell\"\n [todayValue]=\"todayYear\"\n [selectedValues]=\"selectedYears\"\n [selectMode]=\"selectMode\"\n (keydown)=\"handleCalendarKeydown($event)\"\n (select)=\"selectCalendarCell($event)\"></tbody>\n</table>\n<button class=\"owl-dt-control-button owl-dt-control-arrow-button\"\n [disabled]=\"!nextEnabled()\" [attr.aria-label]=\"nextButtonLabel\"\n type=\"button\" tabindex=\"0\" (click)=\"nextYearList($event)\">\n <span class=\"owl-dt-control-button-content\" tabindex=\"-1\">\n <!-- <editor-fold desc=\"SVG Arrow Right\"> -->\n <svg version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n viewBox=\"0 0 250.738 250.738\" style=\"enable-background:new 0 0 250.738 250.738;\" xml:space=\"preserve\">\n <path style=\"fill-rule:evenodd;clip-rule:evenodd;\" d=\"M191.75,111.689L84.766,5.291c-7.1-7.055-18.613-7.055-25.713,0\n c-7.101,7.054-7.101,18.49,0,25.544l95.053,94.534l-95.053,94.533c-7.101,7.054-7.101,18.491,0,25.545\n c7.1,7.054,18.613,7.054,25.713,0L191.75,139.05c3.784-3.759,5.487-8.759,5.238-13.681\n C197.237,120.447,195.534,115.448,191.75,111.689z\"/>\n </svg>\n <!-- </editor-fold> -->\n </span>\n</button>\n" }]
}], ctorParameters: function () {
return [{ type: i0.ChangeDetectorRef }, { type: OwlDateTimeIntl }, { type: DateTimeAdapter, decorators: [{
type: Optional
}] }];
}, propDecorators: { selectMode: [{
type: Input
}], selected: [{
type: Input
}], selecteds: [{
type: Input
}], pickerDayjs: [{
type: Input
}], dateFilter: [{
type: Input
}], minDate: [{
type: Input
}], maxDate: [{
type: Input
}], change: [{
type: Output
}], yearSelected: [{
type: Output
}], pickerDayjsChange: [{
type: Output
}], keyboardEnter: [{
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.pickerDayjsChange = 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 pickerDayjs() {
return this._pickerDayjs;
}
set pickerDayjs(value) {
const oldDayjs = this._pickerDayjs;
value = this.dateTimeAdapter.deserialize(value);
this._pickerDayjs =
this.getValidDate(value) || this.dateTimeAdapter.now();
if (!this.hasSameYear(oldDayjs, this._pickerDayjs) &&
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._pickerDayjs) {
return this.dateTimeAdapter.getMonth(this._pickerDayjs);
}
}
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.pickerDayjs), month, 1);
this.monthSelected.emit(firstDateOfMonth);
const daysInMonth = this.dateTimeAdapter.getNumDaysInMonth(firstDateOfMonth);
const result = this.dateTimeAdapter.createDate(this.dateTimeAdapter.getYear(this.pickerDayjs), month, Math.min(daysInMonth, this.dateTimeAdapter.getDate(this.pickerDayjs)), this.dateTimeAdapter.getHours(this.pickerDayjs), this.dateTimeAdapter.getMinutes(this.pickerDayjs), this.dateTimeAdapter.getSeconds(this.pickerDayjs));
this.change.emit(result);
}
/**
* Handle keydown event on calendar body
*/
handleCalendarKeydown(event) {
let dayjs;
switch (event.keyCode) {
// minus 1 month
case LEFT_ARROW:
dayjs = this.dateTimeAdapter.addCalendarMonths(this.pickerDayjs, -1);
this.pickerDayjsChange.emit(dayjs);
break;
// add 1 month
case RIGHT_ARROW:
dayjs = this.dateTimeAdapter.addCalendarMonths(this.pickerDayjs, 1);
this.pickerDayjsChange.emit(dayjs);
break;
// minus 3 months
case UP_ARROW:
dayjs = this.dateTimeAdapter.addCalendarMonths(this.pickerDayjs, -3);
this.pickerDayjsChange.emit(dayjs);
break;
// add 3 months
case DOWN_ARROW:
dayjs = this.dateTimeAdapter.addCalendarMonths(this.pickerDayjs, 3);
this.pickerDayjsChange.emit(dayjs);
break;
// move to first month of current year
case HOME:
dayjs = this.dateTimeAdapter.addCalendarMonths(this.pickerDayjs, -this.dateTimeAdapter.getMonth(this.pickerDayjs));
this.pickerDayjsChange.emit(dayjs);
break;
// move to last month of current year
case END:
dayjs = this.dateTimeAdapter.addCalendarMonths(this.pickerDayjs, 11 - this.dateTimeAdapter.getMonth(this.pickerDayjs));
this.pickerDayjsChange.emit(dayjs);
break;
// minus 1 year (or 10 year)
case PAGE_UP:
dayjs = this.dateTimeAdapter.addCalendarYears(this.pickerDayjs, event.altKey ? -10 : -1);
this.pickerDayjsChange.emit(dayjs);
break;
// add 1 year (or 10 year)
case PAGE_DOWN:
dayjs = this.dateTimeAdapter.addCalendarYears(this.pickerDayjs, event.altKey ? 10 : 1);
this.pickerDayjsChange.emit(dayjs);
break;
// Select current month
case ENTER:
this.selectMonth(this.dateTimeAdapter.getMonth(this.pickerDayjs));
this.keyboardEnter.emit();
break;
default:
return;
}
this.focusActiveCell();
event.preventDefault();
}
/**
* Generate the calendar month list
* */
generateMonthList() {
if (!this.pickerDayjs) {
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.pickerDayjs), 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.pickerDayjs), 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._pickerDayjs)) {
const result = this.dateTimeAdapter.compareYear(date, this._pickerDayjs);
// < 0 : the given date's year is before pickerDayjs's year, we return -1 as selected month value.
// > 0 : the given date's year is after pickerDayjs's year, we return 12 as selected month value.
// 0 : the give date's year is same as the pickerDayjs's year, we return the actual month value.