UNPKG

ng-pick-datetime-ex

Version:
1,036 lines (1,028 loc) 268 kB
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.