ng-pick-datetime-ex
Version:
Angular Date Time Picker
1 lines • 432 kB
Source Map (JSON)
{"version":3,"file":"ng-pick-datetime-ex.mjs","sources":["../../../projects/picker/src/lib/date-time/date-time-picker-trigger.directive.ts","../../../projects/picker/src/lib/date-time/adapter/date-time-format.class.ts","../../../projects/picker/src/lib/date-time/date-time-picker-intl.service.ts","../../../projects/picker/src/lib/date-time/adapter/date-time-adapter.class.ts","../../../projects/picker/src/lib/date-time/calendar-body.component.ts","../../../projects/picker/src/lib/date-time/calendar-body.component.html","../../../projects/picker/src/lib/date-time/calendar-multi-year-view.component.ts","../../../projects/picker/src/lib/date-time/calendar-multi-year-view.component.html","../../../projects/picker/src/lib/date-time/calendar-year-view.component.ts","../../../projects/picker/src/lib/date-time/calendar-year-view.component.html","../../../projects/picker/src/lib/date-time/calendar-month-view.component.ts","../../../projects/picker/src/lib/date-time/calendar-month-view.component.html","../../../projects/picker/src/lib/date-time/calendar.component.ts","../../../projects/picker/src/lib/date-time/calendar.component.html","../../../projects/picker/src/lib/date-time/timer-box.component.ts","../../../projects/picker/src/lib/date-time/timer-box.component.html","../../../projects/picker/src/lib/date-time/timer.component.ts","../../../projects/picker/src/lib/date-time/timer.component.html","../../../projects/picker/src/lib/date-time/date-time-picker.animations.ts","../../../projects/picker/src/lib/date-time/date-time-picker-container.component.ts","../../../projects/picker/src/lib/date-time/date-time-picker-container.component.html","../../../projects/picker/src/lib/date-time/date-time.class.ts","../../../projects/picker/src/lib/dialog/dialog-config.class.ts","../../../projects/picker/src/lib/dialog/dialog-ref.class.ts","../../../projects/picker/src/lib/dialog/dialog-container.component.ts","../../../projects/picker/src/lib/dialog/dialog-container.component.html","../../../projects/picker/src/lib/utils/object.utils.ts","../../../projects/picker/src/lib/utils/index.ts","../../../projects/picker/src/lib/dialog/dialog.service.ts","../../../projects/picker/src/lib/date-time/date-time-picker.component.ts","../../../projects/picker/src/lib/date-time/date-time-picker.component.html","../../../projects/picker/src/lib/date-time/date-time-picker-input.directive.ts","../../../projects/picker/src/lib/date-time/numberedFixLen.pipe.ts","../../../projects/picker/src/lib/date-time/date-time-inline.component.ts","../../../projects/picker/src/lib/date-time/date-time-inline.component.html","../../../projects/picker/src/lib/dialog/dialog.module.ts","../../../projects/picker/src/lib/date-time/date-time.module.ts","../../../projects/picker/src/lib/date-time/adapter/native-date-time-adapter.class.ts","../../../projects/picker/src/lib/date-time/adapter/native-date-time-format.class.ts","../../../projects/picker/src/lib/date-time/adapter/native-date-time.module.ts","../../../projects/picker/src/lib/date-time/adapter/dayjs-adapter/dayjs-date-time-adapter.class.ts","../../../projects/picker/src/lib/date-time/adapter/dayjs-adapter/dayjs-date-time-format.class.ts","../../../projects/picker/src/lib/date-time/adapter/dayjs-adapter/dayjs-date-time.module.ts","../../../projects/picker/src/public_api.ts","../../../projects/picker/src/ng-pick-datetime-ex.ts"],"sourcesContent":["/**\n * date-time-picker-trigger.directive\n */\n\n\nimport {\n AfterContentInit,\n ChangeDetectorRef,\n Directive,\n Input,\n OnChanges,\n OnDestroy,\n OnInit,\n SimpleChanges\n} from '@angular/core';\nimport { OwlDateTimeComponent } from './date-time-picker.component';\nimport { merge, of as observableOf, Subscription } from 'rxjs';\n\n@Directive({\n selector: '[owlDateTimeTrigger]',\n host: {\n '(click)': 'handleClickOnHost($event)',\n '[class.owl-dt-trigger-disabled]': 'owlDTTriggerDisabledClass'\n }\n})\nexport class OwlDateTimeTriggerDirective<T> implements OnInit, OnChanges, AfterContentInit, OnDestroy {\n\n @Input('owlDateTimeTrigger') dtPicker: OwlDateTimeComponent<T>;\n\n private _disabled: boolean;\n @Input()\n get disabled(): boolean {\n return this._disabled === undefined ? this.dtPicker.disabled : !!this._disabled;\n }\n\n set disabled( value: boolean ) {\n this._disabled = value;\n }\n\n get owlDTTriggerDisabledClass(): boolean {\n return this.disabled;\n }\n\n private stateChanges = Subscription.EMPTY;\n\n constructor( protected changeDetector: ChangeDetectorRef ) {\n }\n\n public ngOnInit(): void {\n }\n\n public ngOnChanges( changes: SimpleChanges ) {\n if (changes.datepicker) {\n this.watchStateChanges();\n }\n }\n\n public ngAfterContentInit() {\n this.watchStateChanges();\n }\n\n public ngOnDestroy(): void {\n this.stateChanges.unsubscribe();\n }\n\n public handleClickOnHost( event: Event ): void {\n if (this.dtPicker) {\n this.dtPicker.open();\n event.stopPropagation();\n }\n }\n\n private watchStateChanges(): void {\n this.stateChanges.unsubscribe();\n\n const inputDisabled = this.dtPicker && this.dtPicker.dtInput ?\n this.dtPicker.dtInput.disabledChange : observableOf();\n\n const pickerDisabled = this.dtPicker ?\n this.dtPicker.disabledChange : observableOf();\n\n this.stateChanges = merge(pickerDisabled, inputDisabled)\n .subscribe(() => {\n this.changeDetector.markForCheck();\n });\n }\n}\n","/**\n * date-time-format.class\n */\n\nimport { InjectionToken } from '@angular/core';\n\nexport type OwlDateTimeFormats = {\n parseInput: any,\n fullPickerInput: any,\n datePickerInput: any,\n timePickerInput: any,\n monthYearLabel: any,\n dateA11yLabel: any,\n monthYearA11yLabel: any,\n};\n\n/** InjectionToken for date time picker that can be used to override default format. */\nexport const OWL_DATE_TIME_FORMATS = new InjectionToken<OwlDateTimeFormats>('OWL_DATE_TIME_FORMATS');\n","/**\n * date-time-picker-intl.service\n */\n\nimport { Injectable } from '@angular/core';\nimport { Subject } from 'rxjs';\n\n@Injectable({providedIn: 'root'})\nexport class OwlDateTimeIntl {\n\n /**\n * Stream that emits whenever the labels here are changed. Use this to notify\n * components if the labels have changed after initialization.\n */\n readonly changes: Subject<void> = new Subject<void>();\n\n /** A label for the up second button (used by screen readers). */\n upSecondLabel = 'Add a second';\n\n /** A label for the down second button (used by screen readers). */\n downSecondLabel = 'Minus a second';\n\n /** A label for the up minute button (used by screen readers). */\n upMinuteLabel = 'Add a minute';\n\n /** A label for the down minute button (used by screen readers). */\n downMinuteLabel = 'Minus a minute';\n\n /** A label for the up hour button (used by screen readers). */\n upHourLabel = 'Add a hour';\n\n /** A label for the down hour button (used by screen readers). */\n downHourLabel = 'Minus a hour';\n\n /** A label for the previous month button (used by screen readers). */\n prevMonthLabel = 'Previous month';\n\n /** A label for the next month button (used by screen readers). */\n nextMonthLabel = 'Next month';\n\n /** A label for the previous year button (used by screen readers). */\n prevYearLabel = 'Previous year';\n\n /** A label for the next year button (used by screen readers). */\n nextYearLabel = 'Next year';\n\n /** A label for the previous multi-year button (used by screen readers). */\n prevMultiYearLabel: string = 'Previous 21 years';\n\n /** A label for the next multi-year button (used by screen readers). */\n nextMultiYearLabel: string = 'Next 21 years';\n\n /** A label for the 'switch to month view' button (used by screen readers). */\n switchToMonthViewLabel = 'Change to month view';\n\n /** A label for the 'switch to year view' button (used by screen readers). */\n switchToMultiYearViewLabel: string = 'Choose month and year';\n\n /** A label for the cancel button */\n cancelBtnLabel = 'Cancel';\n\n /** A label for the set button */\n setBtnLabel = 'Set';\n\n /** A label for the range 'from' in picker info */\n rangeFromLabel = 'From';\n\n /** A label for the range 'to' in picker info */\n rangeToLabel = 'To';\n\n /** A label for the hour12 button (AM) */\n hour12AMLabel = 'AM';\n\n /** A label for the hour12 button (PM) */\n hour12PMLabel = 'PM';\n}\n","/**\n * date-time-adapter.class\n */\nimport { Observable, Subject } from 'rxjs';\nimport { inject, InjectionToken, LOCALE_ID } from '@angular/core';\n\n/** InjectionToken for date time picker that can be used to override default locale code. */\nexport const OWL_DATE_TIME_LOCALE = new InjectionToken<string>(\n 'OWL_DATE_TIME_LOCALE',\n {\n providedIn: 'root',\n factory: OWL_DATE_TIME_LOCALE_FACTORY\n }\n);\n\n/** @docs-private */\nexport function OWL_DATE_TIME_LOCALE_FACTORY(): string {\n return inject(LOCALE_ID);\n}\n\n/** Provider for OWL_DATE_TIME_LOCALE injection token. */\nexport const OWL_DATE_TIME_LOCALE_PROVIDER = {\n provide: OWL_DATE_TIME_LOCALE,\n useExisting: LOCALE_ID\n};\n\nexport abstract class DateTimeAdapter<T> {\n /** The locale to use for all dates. */\n protected locale: any;\n\n /** A stream that emits when the locale changes. */\n protected _localeChanges = new Subject<void>();\n get localeChanges(): Observable<void> {\n return this._localeChanges;\n }\n\n /** total milliseconds in a day. */\n protected readonly millisecondsInDay = 86400000;\n\n /** total milliseconds in a minute. */\n protected readonly milliseondsInMinute = 60000;\n\n /**\n * Get the year of the given date\n */\n abstract getYear(date: T): number;\n\n /**\n * Get the month of the given date\n * 0 -- January\n * 11 -- December\n * */\n abstract getMonth(date: T): number;\n\n /**\n * Get the day of the week of the given date\n * 0 -- Sunday\n * 6 -- Saturday\n * */\n abstract getDay(date: T): number;\n\n /**\n * Get the day num of the given date\n */\n abstract getDate(date: T): number;\n\n /**\n * Get the hours of the given date\n */\n abstract getHours(date: T): number;\n\n /**\n * Get the minutes of the given date\n */\n abstract getMinutes(date: T): number;\n\n /**\n * Get the seconds of the given date\n */\n abstract getSeconds(date: T): number;\n\n /**\n * Get the milliseconds timestamp of the given date\n */\n abstract getTime(date: T): number;\n\n /**\n * Gets the number of days in the month of the given date.\n */\n abstract getNumDaysInMonth(date: T): number;\n\n /**\n * Get the number of calendar days between the given dates.\n * If dateLeft is before dateRight, it would return positive value\n * If dateLeft is after dateRight, it would return negative value\n */\n abstract differenceInCalendarDays(dateLeft: T, dateRight: T): number;\n\n /**\n * Gets the name for the year of the given date.\n */\n abstract getYearName(date: T): string;\n\n /**\n * Get a list of month names\n */\n abstract getMonthNames(style: 'long' | 'short' | 'narrow'): string[];\n\n /**\n * Get a list of week names\n */\n abstract getDayOfWeekNames(style: 'long' | 'short' | 'narrow'): string[];\n\n /**\n * Gets a list of names for the dates of the month.\n */\n abstract getDateNames(): string[];\n\n /**\n * Return a Date object as a string, using the ISO standard\n */\n abstract toIso8601(date: T): string;\n\n /**\n * Check if the give dates are equal\n */\n abstract isEqual(dateLeft: T, dateRight: T): boolean;\n\n /**\n * Check if the give dates are the same day\n */\n abstract isSameDay(dateLeft: T, dateRight: T): boolean;\n\n /**\n * Checks whether the given date is valid.\n */\n abstract isValid(date: T): boolean;\n\n /**\n * Gets date instance that is not valid.\n */\n abstract invalid(): T;\n\n /**\n * Checks whether the given object is considered a date instance by this DateTimeAdapter.\n */\n abstract isDateInstance(obj: any): boolean;\n\n /**\n * Add the specified number of years to the given date\n */\n abstract addCalendarYears(date: T, amount: number): T;\n\n /**\n * Add the specified number of months to the given date\n */\n abstract addCalendarMonths(date: T, amount: number): T;\n\n /**\n * Add the specified number of days to the given date\n */\n abstract addCalendarDays(date: T, amount: number): T;\n\n /**\n * Set the hours to the given date.\n */\n abstract setHours(date: T, amount: number): T;\n\n /**\n * Set the minutes to the given date.\n */\n abstract setMinutes(date: T, amount: number): T;\n\n /**\n * Set the seconds to the given date.\n */\n abstract setSeconds(date: T, amount: number): T;\n\n /**\n * Creates a date with the given year, month, date, hour, minute and second. Does not allow over/under-flow of the\n * month and date.\n */\n abstract createDate(year: number, month: number, date: number): T;\n abstract createDate(\n year: number,\n month: number,\n date: number,\n hours: number,\n minutes: number,\n seconds: number\n ): T;\n\n /**\n * Clone the given date\n */\n abstract clone(date: T): T;\n\n /**\n * Get a new dayjs\n * */\n abstract now(): T;\n\n /**\n * Formats a date as a string according to the given format.\n */\n abstract format(date: T, displayFormat: any): string;\n\n /**\n * Parse a user-provided value to a Date Object\n */\n abstract parse(value: any, parseFormat: any): T | null;\n\n /**\n * Compare two given dates\n * 1 if the first date is after the second,\n * -1 if the first date is before the second\n * 0 if dates are equal.\n * */\n compare(first: T, second: T): number {\n if (!this.isValid(first) || !this.isValid(second)) {\n throw Error('JSNativeDate: Cannot compare invalid dates.');\n }\n\n const dateFirst = this.clone(first);\n const dateSecond = this.clone(second);\n\n const diff = this.getTime(dateFirst) - this.getTime(dateSecond);\n\n if (diff < 0) {\n return -1;\n } else if (diff > 0) {\n return 1;\n } else {\n // Return 0 if diff is 0; return NaN if diff is NaN\n return diff;\n }\n }\n\n /**\n * Check if two given dates are in the same year\n * 1 if the first date's year is after the second,\n * -1 if the first date's year is before the second\n * 0 if two given dates are in the same year\n * */\n compareYear(first: T, second: T): number {\n if (!this.isValid(first) || !this.isValid(second)) {\n throw Error('JSNativeDate: Cannot compare invalid dates.');\n }\n\n const yearLeft = this.getYear(first);\n const yearRight = this.getYear(second);\n\n const diff = yearLeft - yearRight;\n\n if (diff < 0) {\n return -1;\n } else if (diff > 0) {\n return 1;\n } else {\n return 0;\n }\n }\n\n /**\n * Attempts to deserialize a value to a valid date object. This is different from parsing in that\n * deserialize should only accept non-ambiguous, locale-independent formats (e.g. a ISO 8601\n * string). The default implementation does not allow any deserialization, it simply checks that\n * the given value is already a valid date object or null. The `<mat-datepicker>` will call this\n * method on all of it's `@Input()` properties that accept dates. It is therefore possible to\n * support passing values from your backend directly to these properties by overriding this method\n * to also deserialize the format used by your backend.\n */\n deserialize(value: any): T | null {\n if (\n value == null ||\n (this.isDateInstance(value) && this.isValid(value))\n ) {\n return value;\n }\n return this.invalid();\n }\n\n /**\n * Sets the locale used for all dates.\n */\n setLocale(locale: any) {\n this.locale = locale;\n this._localeChanges.next();\n }\n\n /**\n * Clamp the given date between min and max dates.\n */\n clampDate(date: T, min?: T | null, max?: T | null): T {\n if (min && this.compare(date, min) < 0) {\n return min;\n }\n if (max && this.compare(date, max) > 0) {\n return max;\n }\n return date;\n }\n}\n","/**\n * calendar-body.component\n */\n\nimport {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n EventEmitter,\n Input,\n NgZone,\n OnInit,\n Output\n} from '@angular/core';\nimport { SelectMode } from './date-time.class';\nimport { take } from 'rxjs/operators';\n\nexport class CalendarCell {\n constructor(\n public value: number,\n public displayValue: string,\n public ariaLabel: string,\n public enabled: boolean,\n public out: boolean = false,\n public cellClass: string = ''\n ) {}\n}\n\n@Component({\n selector: '[owl-date-time-calendar-body]',\n exportAs: 'owlDateTimeCalendarBody',\n templateUrl: './calendar-body.component.html',\n styleUrls: ['./calendar-body.component.scss'],\n host:{\n '[class.owl-dt-calendar-body]': 'owlDTCalendarBodyClass'\n },\n preserveWhitespaces: false,\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class OwlCalendarBodyComponent implements OnInit {\n /**\n * The cell number of the active cell in the table.\n */\n @Input()\n activeCell = 0;\n\n /**\n * The cells to display in the table.\n * */\n @Input()\n rows: CalendarCell[][];\n\n /**\n * The number of columns in the table.\n * */\n @Input()\n numCols = 7;\n\n /**\n * The ratio (width / height) to use for the cells in the table.\n */\n @Input()\n cellRatio = 1;\n\n /**\n * The value in the table that corresponds to today.\n * */\n @Input()\n todayValue: number;\n\n /**\n * The value in the table that is currently selected.\n * */\n @Input()\n selectedValues: number[];\n\n /**\n * Current picker select mode\n */\n @Input()\n selectMode: SelectMode;\n\n /**\n * Emit when a calendar cell is selected\n * */\n @Output()\n public readonly select = new EventEmitter<CalendarCell>();\n\n get owlDTCalendarBodyClass(): boolean {\n return true;\n }\n\n get isInSingleMode(): boolean {\n return this.selectMode === 'single';\n }\n\n get isInRangeMode(): boolean {\n return (\n this.selectMode === 'range' ||\n this.selectMode === 'rangeFrom' ||\n this.selectMode === 'rangeTo'\n );\n }\n\n constructor(private elmRef: ElementRef, private ngZone: NgZone) {}\n\n public ngOnInit() {}\n\n public selectCell(cell: CalendarCell): void {\n this.select.emit(cell);\n }\n\n public isActiveCell(rowIndex: number, colIndex: number): boolean {\n const cellNumber = rowIndex * this.numCols + colIndex;\n return cellNumber === this.activeCell;\n }\n\n /**\n * Check if the cell is selected\n */\n public isSelected(value: number): boolean {\n if (!this.selectedValues || this.selectedValues.length === 0) {\n return false;\n }\n\n if (this.isInSingleMode) {\n return value === this.selectedValues[0];\n }\n\n if (this.isInRangeMode) {\n const fromValue = this.selectedValues[0];\n const toValue = this.selectedValues[1];\n\n return value === fromValue || value === toValue;\n }\n }\n\n /**\n * Check if the cell in the range\n * */\n public isInRange(value: number): boolean {\n if (this.isInRangeMode) {\n const fromValue = this.selectedValues[0];\n const toValue = this.selectedValues[1];\n\n if (fromValue !== null && toValue !== null) {\n return value >= fromValue && value <= toValue;\n } else {\n return value === fromValue || value === toValue;\n }\n }\n }\n\n /**\n * Check if the cell is the range from\n * */\n public isRangeFrom(value: number): boolean {\n if (this.isInRangeMode) {\n const fromValue = this.selectedValues[0];\n return fromValue !== null && value === fromValue;\n }\n }\n\n /**\n * Check if the cell is the range to\n * */\n public isRangeTo(value: number): boolean {\n if (this.isInRangeMode) {\n const toValue = this.selectedValues[1];\n return toValue !== null && value === toValue;\n }\n }\n\n /**\n * Focus to a active cell\n * */\n public focusActiveCell(): void {\n this.ngZone.runOutsideAngular(() => {\n this.ngZone.onStable\n .asObservable()\n .pipe(take(1))\n .subscribe(() => {\n this.elmRef.nativeElement\n .querySelector('.owl-dt-calendar-cell-active')\n .focus();\n });\n });\n }\n}\n","<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","/**\n * calendar-multi-year-view.component\n */\n\nimport {\n AfterContentInit,\n ChangeDetectionStrategy, ChangeDetectorRef,\n Component,\n EventEmitter,\n Input,\n OnInit,\n Optional,\n Output,\n ViewChild\n} from '@angular/core';\nimport { DateTimeAdapter } from './adapter/date-time-adapter.class';\nimport { CalendarCell, OwlCalendarBodyComponent } from './calendar-body.component';\nimport { SelectMode } from './date-time.class';\nimport {\n DOWN_ARROW,\n END,\n ENTER,\n HOME,\n LEFT_ARROW,\n PAGE_DOWN,\n PAGE_UP,\n RIGHT_ARROW,\n UP_ARROW\n} from '@angular/cdk/keycodes';\nimport { OwlDateTimeIntl } from './date-time-picker-intl.service';\n\nexport const YEARS_PER_ROW = 3;\nexport const YEAR_ROWS = 7;\n\n@Component({\n selector: 'owl-date-time-multi-year-view',\n templateUrl: './calendar-multi-year-view.component.html',\n styleUrls: ['./calendar-multi-year-view.component.scss'],\n host:{\n '[class.owl-dt-calendar-view]': 'owlDTCalendarView',\n '[class.owl-dt-calendar-multi-year-view]': 'owlDTCalendarMultiYearView'\n },\n preserveWhitespaces: false,\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\n\nexport class OwlMultiYearViewComponent<T> implements OnInit, AfterContentInit {\n\n /**\n * The select mode of the picker;\n * */\n private _selectMode: SelectMode = 'single';\n @Input()\n get selectMode(): SelectMode {\n return this._selectMode;\n }\n\n set selectMode( val: SelectMode ) {\n this._selectMode = val;\n if (this.initiated) {\n this.setSelectedYears();\n this.cdRef.markForCheck();\n }\n }\n\n /** The currently selected date. */\n private _selected: T | null;\n @Input()\n get selected(): T | null {\n return this._selected;\n }\n\n set selected( value: T | null ) {\n const oldSelected = this._selected;\n value = this.dateTimeAdapter.deserialize(value);\n this._selected = this.getValidDate(value);\n\n if (!this.dateTimeAdapter.isSameDay(oldSelected, this._selected)) {\n this.setSelectedYears();\n }\n }\n\n private _selecteds: T[] = [];\n @Input()\n get selecteds(): T[] {\n return this._selecteds;\n }\n\n set selecteds( values: T[] ) {\n this._selecteds = values.map(( v ) => {\n v = this.dateTimeAdapter.deserialize(v);\n return this.getValidDate(v);\n });\n this.setSelectedYears();\n }\n\n private _pickerDayjs: T | null;\n @Input()\n get pickerDayjs() {\n return this._pickerDayjs;\n }\n\n set pickerDayjs( value: T ) {\n const oldDayjs = this._pickerDayjs;\n value = this.dateTimeAdapter.deserialize(value);\n this._pickerDayjs = this.getValidDate(value) || this.dateTimeAdapter.now();\n\n if (oldDayjs && this._pickerDayjs &&\n !this.isSameYearList(oldDayjs, this._pickerDayjs)) {\n this.generateYearList();\n }\n }\n\n /**\n * A function used to filter which dates are selectable\n * */\n private _dateFilter: ( date: T ) => boolean;\n @Input()\n get dateFilter() {\n return this._dateFilter;\n }\n\n set dateFilter( filter: ( date: T ) => boolean ) {\n this._dateFilter = filter;\n if (this.initiated) {\n this.generateYearList();\n }\n }\n\n /** The minimum selectable date. */\n private _minDate: T | null;\n @Input()\n get minDate(): T | null {\n return this._minDate;\n }\n\n set minDate( value: T | null ) {\n value = this.dateTimeAdapter.deserialize(value);\n this._minDate = this.getValidDate(value);\n if (this.initiated) {\n this.generateYearList();\n }\n }\n\n /** The maximum selectable date. */\n private _maxDate: T | null;\n @Input()\n get maxDate(): T | null {\n return this._maxDate;\n }\n\n set maxDate( value: T | null ) {\n value = this.dateTimeAdapter.deserialize(value);\n this._maxDate = this.getValidDate(value);\n if (this.initiated) {\n this.generateYearList();\n }\n }\n\n private _todayYear: number;\n get todayYear(): number {\n return this._todayYear;\n }\n\n private _years: CalendarCell[][];\n get years() {\n return this._years;\n }\n\n private _selectedYears: number[];\n get selectedYears(): number[] {\n return this._selectedYears;\n }\n\n private initiated = false;\n\n get isInSingleMode(): boolean {\n return this.selectMode === 'single';\n }\n\n get isInRangeMode(): boolean {\n return this.selectMode === 'range' || this.selectMode === 'rangeFrom'\n || this.selectMode === 'rangeTo';\n }\n\n get activeCell(): number {\n if (this._pickerDayjs) {\n return this.dateTimeAdapter.getYear(this._pickerDayjs) % (YEARS_PER_ROW * YEAR_ROWS);\n }\n }\n\n get tableHeader(): string {\n if (this._years && this._years.length > 0) {\n return `${this._years[0][0].displayValue} ~ ${this._years[YEAR_ROWS - 1][YEARS_PER_ROW - 1].displayValue}`\n }\n }\n\n get prevButtonLabel(): string {\n return this.pickerIntl.prevMultiYearLabel;\n }\n\n get nextButtonLabel(): string {\n return this.pickerIntl.nextMultiYearLabel;\n }\n\n /**\n * Callback to invoke when a new month is selected\n * */\n @Output() readonly change = new EventEmitter<T>();\n\n /**\n * Emits the selected year. This doesn't imply a change on the selected date\n * */\n @Output() readonly yearSelected = new EventEmitter<T>();\n\n /** Emits when any date is activated. */\n @Output() readonly pickerDayjsChange: EventEmitter<T> = new EventEmitter<T>();\n\n /** Emits when use keyboard enter to select a calendar cell */\n @Output() readonly keyboardEnter: EventEmitter<any> = new EventEmitter<any>();\n\n /** The body of calendar table */\n @ViewChild(OwlCalendarBodyComponent, { static: true }) calendarBodyElm: OwlCalendarBodyComponent;\n\n get owlDTCalendarView(): boolean {\n return true;\n }\n\n get owlDTCalendarMultiYearView(): boolean {\n return true;\n }\n\n constructor( private cdRef: ChangeDetectorRef,\n private pickerIntl: OwlDateTimeIntl,\n @Optional() private dateTimeAdapter: DateTimeAdapter<T> ) {\n }\n\n public ngOnInit() {\n }\n\n public ngAfterContentInit(): void {\n this._todayYear = this.dateTimeAdapter.getYear(this.dateTimeAdapter.now());\n this.generateYearList();\n this.initiated = true;\n }\n\n /**\n * Handle a calendarCell selected\n */\n public selectCalendarCell( cell: CalendarCell ): void {\n this.selectYear(cell.value);\n }\n\n private selectYear( year: number ): void {\n this.yearSelected.emit(this.dateTimeAdapter.createDate(year, 0, 1));\n const firstDateOfMonth = this.dateTimeAdapter.createDate(\n year,\n this.dateTimeAdapter.getMonth(this.pickerDayjs),\n 1\n );\n const daysInMonth = this.dateTimeAdapter.getNumDaysInMonth(firstDateOfMonth);\n const selected = this.dateTimeAdapter.createDate(\n year,\n this.dateTimeAdapter.getMonth(this.pickerDayjs),\n Math.min(daysInMonth, this.dateTimeAdapter.getDate(this.pickerDayjs)),\n this.dateTimeAdapter.getHours(this.pickerDayjs),\n this.dateTimeAdapter.getMinutes(this.pickerDayjs),\n this.dateTimeAdapter.getSeconds(this.pickerDayjs),\n );\n\n this.change.emit(selected);\n }\n\n /**\n * Generate the previous year list\n * */\n public prevYearList( event: any ): void {\n this._pickerDayjs = this.dateTimeAdapter.addCalendarYears(this.pickerDayjs, -1 * YEAR_ROWS * YEARS_PER_ROW);\n this.generateYearList();\n event.preventDefault();\n }\n\n /**\n * Generate the next year list\n * */\n public nextYearList( event: any ): void {\n this._pickerDayjs = this.dateTimeAdapter.addCalendarYears(this.pickerDayjs, YEAR_ROWS * YEARS_PER_ROW);\n this.generateYearList();\n event.preventDefault();\n }\n\n public generateYearList(): void {\n this._years = [];\n\n const pickerDayjsYear = this.dateTimeAdapter.getYear(this._pickerDayjs);\n const offset = pickerDayjsYear % (YEARS_PER_ROW * YEAR_ROWS);\n\n for (let i = 0; i < YEAR_ROWS; i++) {\n const row = [];\n\n for (let j = 0; j < YEARS_PER_ROW; j++) {\n const year = pickerDayjsYear - offset + (j + i * YEARS_PER_ROW);\n const yearCell = this.createYearCell(year);\n row.push(yearCell);\n }\n\n this._years.push(row);\n }\n\n return;\n\n }\n\n /** Whether the previous period button is enabled. */\n public previousEnabled(): boolean {\n if (!this.minDate) {\n return true;\n }\n return !this.minDate || !this.isSameYearList(this._pickerDayjs, this.minDate);\n }\n\n /** Whether the next period button is enabled. */\n public nextEnabled(): boolean {\n return !this.maxDate || !this.isSameYearList(this._pickerDayjs, this.maxDate);\n }\n\n public handleCalendarKeydown( event: KeyboardEvent ): void {\n let dayjs;\n switch (event.keyCode) {\n // minus 1 year\n case LEFT_ARROW:\n dayjs = this.dateTimeAdapter.addCalendarYears(this._pickerDayjs, -1);\n this.pickerDayjsChange.emit(dayjs);\n break;\n\n // add 1 year\n case RIGHT_ARROW:\n dayjs = this.dateTimeAdapter.addCalendarYears(this._pickerDayjs, 1);\n this.pickerDayjsChange.emit(dayjs);\n break;\n\n // minus 3 years\n case UP_ARROW:\n dayjs = this.dateTimeAdapter.addCalendarYears(this._pickerDayjs, -1 * YEARS_PER_ROW);\n this.pickerDayjsChange.emit(dayjs);\n break;\n\n // add 3 years\n case DOWN_ARROW:\n dayjs = this.dateTimeAdapter.addCalendarYears(this._pickerDayjs, YEARS_PER_ROW);\n this.pickerDayjsChange.emit(dayjs);\n break;\n\n // go to the first year of the year page\n case HOME:\n dayjs = this.dateTimeAdapter.addCalendarYears(this._pickerDayjs,\n -this.dateTimeAdapter.getYear(this._pickerDayjs) % (YEARS_PER_ROW * YEAR_ROWS));\n this.pickerDayjsChange.emit(dayjs);\n break;\n\n // go to the last year of the year page\n case END:\n dayjs = this.dateTimeAdapter.addCalendarYears(this._pickerDayjs,\n (YEARS_PER_ROW * YEAR_ROWS) - this.dateTimeAdapter.getYear(this._pickerDayjs) % (YEARS_PER_ROW * YEAR_ROWS) - 1);\n this.pickerDayjsChange.emit(dayjs);\n break;\n\n // minus 1 year page (or 10 year pages)\n case PAGE_UP:\n dayjs = this.dateTimeAdapter.addCalendarYears(this.pickerDayjs, event.altKey ? -10 * (YEARS_PER_ROW * YEAR_ROWS) : -1 * (YEARS_PER_ROW * YEAR_ROWS));\n this.pickerDayjsChange.emit(dayjs);\n break;\n\n // add 1 year page (or 10 year pages)\n case PAGE_DOWN:\n dayjs = this.dateTimeAdapter.addCalendarYears(this.pickerDayjs, event.altKey ? 10 * (YEARS_PER_ROW * YEAR_ROWS) : (YEARS_PER_ROW * YEAR_ROWS));\n this.pickerDayjsChange.emit(dayjs);\n break;\n\n case ENTER:\n this.selectYear(this.dateTimeAdapter.getYear(this._pickerDayjs));\n this.keyboardEnter.emit();\n break;\n\n default:\n return;\n }\n\n this.focusActiveCell();\n event.preventDefault();\n }\n\n /**\n * Creates an CalendarCell for the given year.\n */\n private createYearCell( year: number ): CalendarCell {\n const startDateOfYear = this.dateTimeAdapter.createDate(year, 0, 1);\n const ariaLabel = this.dateTimeAdapter.getYearName(startDateOfYear);\n const cellClass = 'owl-dt-year-' + year;\n return new CalendarCell(year, year.toString(), ariaLabel, this.isYearEnabled(year), false, cellClass);\n }\n\n private setSelectedYears(): void {\n\n this._selectedYears = [];\n\n if (this.isInSingleMode && this.selected) {\n this._selectedYears[0] = this.dateTimeAdapter.getYear(this.selected);\n }\n\n if (this.isInRangeMode && this.selecteds) {\n this._selectedYears = this.selecteds.map(( selected ) => {\n if (this.dateTimeAdapter.isValid(selected)) {\n return this.dateTimeAdapter.getYear(selected);\n } else {\n return null;\n }\n })\n }\n }\n\n /** Whether the given year is enabled. */\n private isYearEnabled( year: number ) {\n // disable if the year is greater than maxDate lower than minDate\n if (year === undefined || year === null ||\n (this.maxDate && year > this.dateTimeAdapter.getYear(this.maxDate)) ||\n (this.minDate && year < this.dateTimeAdapter.getYear(this.minDate))) {\n return false;\n }\n\n // enable if it reaches here and there's no filter defined\n if (!this.dateFilter) {\n return true;\n }\n\n const firstOfYear = this.dateTimeAdapter.createDate(year, 0, 1);\n\n // If any date in the year is enabled count the year as enabled.\n for (let date = firstOfYear; this.dateTimeAdapter.getYear(date) == year;\n date = this.dateTimeAdapter.addCalendarDays(date, 1)) {\n if (this.dateFilter(date)) {\n return true;\n }\n }\n\n return false;\n }\n\n private isSameYearList( date1: T, date2: T ): boolean {\n return Math.floor(this.dateTimeAdapter.getYear(date1) / (YEARS_PER_ROW * YEAR_ROWS)) ===\n Math.floor(this.dateTimeAdapter.getYear(date2) / (YEARS_PER_ROW * YEAR_ROWS));\n }\n\n /**\n * Get a valid date object\n */\n private getValidDate( obj: any ): T | null {\n return (this.dateTimeAdapter.isDateInstance(obj) && this.dateTimeAdapter.isValid(obj)) ? obj : null;\n }\n\n private focusActiveCell() {\n this.calendarBodyElm.focusActiveCell();\n }\n}\n","<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","/**\n * calendar-year-view.component\n */\n\nimport {\n AfterContentInit,\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n EventEmitter,\n Inject,\n Input,\n OnDestroy,\n OnInit,\n Optional,\n Output,\n ViewChild\n} from '@angular/core';\nimport {\n CalendarCell,\n OwlCalendarBodyComponent\n} from './calendar-body.component';\nimport { DateTimeAdapter } from './adapter/date-time-adapter.class';\nimport {\n OWL_DATE_TIME_FORMATS,\n OwlDateTimeFormats\n} from './adapter/date-time-format.class';\nimport { Subscription } from 'rxjs';\nimport { SelectMode } from './date-time.class';\nimport {\n DOWN_ARROW,\n END,\n ENTER,\n HOME,\n LEFT_ARROW,\n PAGE_DOWN,\n PAGE_UP,\n RIGHT_ARROW,\n UP_ARROW\n} from '@angular/cdk/keycodes';\n\nconst MONTHS_PER_YEAR = 12;\nconst MONTHS_PER_ROW = 3;\n\n@Component({\n selector: 'owl-date-time-year-view',\n exportAs: 'owlMonthView',\n templateUrl: './calendar-year-view.component.html',\n styleUrls: ['./calendar-year-view.component.scss'],\n host: {\n '[class.owl-dt-calendar-view]': 'owlDTCalendarView'\n },\n preserveWhitespaces: false,\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class OwlYearViewComponent<T>\n implements OnInit, AfterContentInit, OnDestroy {\n /**\n * The select mode of the picker;\n * */\n private _selectMode: SelectMode = 'single';\n @Input()\n get selectMode(): SelectMode {\n return this._selectMode;\n }\n\n set selectMode(val: SelectMode) {\n this._selectMode = val;\n if (this.initiated) {\n this.generateMonthList();\n this.cdRef.markForCheck();\n }\n }\n\n /** The currently selected date. */\n private _selected: T | null;\n @Input()\n get selected(): T | null {\n return this._selected;\n }\n\n set selected(value: T | null) {\n value = this.dateTimeAdapter.deserialize(value);\n this._selected = this.getValidDate(value);\n this.setSelectedMonths();\n }\n\n private _selecteds: T[] = [];\n @Input()\n get selecteds(): T[] {\n return this._selecteds;\n }\n\n set selecteds(values: T[]) {\n this._selecteds = [];\n for (let i = 0; i < values.length; i++) {\n const value = this.dateTimeAdapter.deserialize(values[i]);\n this._selecteds.push(this.getValidDate(value));\n }\n\n this.setSelectedMonths();\n }\n\n private _pickerDayjs: T | null;\n @Input()\n get pickerDayjs() {\n return this._pickerDayjs;\n }\n\n set pickerDayjs(value: T) {\n const oldDayjs = this._pickerDayjs;\n value = this.dateTimeAdapter.deserialize(value);\n this._pickerDayjs =\n this.getValidDate(value) || this.dateTimeAdapter.now();\n\n if (\n !this.hasSameYear(oldDayjs, this._pickerDayjs) &&\n this.initiated\n ) {\n this.generateMonthList();\n }\n }\n\n /**\n * A function used to filter which dates are selectable\n * */\n private _dateFilter: (date: T) => boolean;\n @Input()\n get dateFilter() {\n return this._dateFilter;\n }\n\n set dateFilter(filter: (date: T) => boolean) {\n this._dateFilter = filter;\n if (this.initiated) {\n this.generateMonthList();\n }\n }\n\n /** The minimum selectable date. */\n private _minDate: T | null;\n @Input()\n get minDate(): T | null {\n return this._minDate;\n }\n\n set minDate(value: T | null) {\n value = this.dateTimeAdapter.deserialize(value);\n this._minDate = this.getValidDate(value);\n if (this.initiated) {\n this.generateMonthList();\n }\n }\n\n /** The maximum selectable date. */\n private _maxDate: T | null;\n @Input()\n get maxDate(): T | null {\n return this._maxDate;\n }\n\n set maxDate(value: T | null) {\n value = this.dateTimeAdapter.deserialize(value);\n this._maxDate = this.getValidDate(value);\n if (this.initiated) {\n this.generateMonthList();\n }\n }\n\n private readonly monthNames: string[];\n\n private _months: CalendarCell[][];\n get months() {\n return this._months;\n }\n\n get activeCell(): number {\n if (this._pickerDayjs) {\n return this.dateTimeAdapter.getMonth(this._pickerDayjs);\n }\n }\n\n get isInSingleMode(): boolean {\n return this.selectMode === 'single';\n }\n\n get isInRangeMode(): boolean {\n return (\n this.selectMode === 'range' ||\n this.selectMode === 'rangeFrom' ||\n this.selectMode === 'rangeTo'\n );\n }\n\n private localeSub: Subscription = Subscription.EMPTY;\n\n private initiated = false;\n\n public todayMonth: number | null;\n\n /**\n * An array to hold all selectedDates' month value\n * the value is the month number in current year\n * */\n public selectedMonths: number[] = [];\n\n /**\n * Callback to invoke when a new month is selected\n * */\n @Output()\n readonly change = new EventEmitter<T>();\n\n /**\n * Emits the selected year. This doesn't imply a change on the selected date\n * */\n @Output()\n readonly monthSelected = new EventEmitter<T>();\n\n /** Emits when any date is activated. */\n @Output()\n readonly pickerDayjsChange: EventEmitter<T> = new EventEmitter<T>();\n\n /** Emits when use keyboard enter to select a calendar cell */\n @Output()\n readonly keyboardEnter: EventEmitter<any> = new EventEmitter<any>();\n\n /** The body of calendar table */\n @ViewChild(OwlCalendarBodyComponent, { static: true })\n calendarBodyElm: OwlCalendarBodyComponent;\n\n get owlDTCalendarView(): boolean {\n return true;\n }\n\n constructor(\n private cdRef: ChangeDetectorRef,\n @Optional() private dateTimeAdapter: DateTimeAdapter<T>,\n @Optional()\n @Inject(OWL_DATE_TIME_FORMATS)\n private dateTimeFormats: OwlDateTimeFormats\n ) {\n this.monthNames = this.dateTimeAdapter.getMonthNames('short');\n }\n\n public ngOnInit() {\n this.localeSub = this.dateTimeAdapter.localeChanges.subscribe(() => {\n this.generateMonthList();\n this.cdRef.markForCheck();\n });\n }\n\n public ngAfterContentInit(): void {\n this.generateMonthList();\n this.initiated = true;\n }\n\n public ngOnDestroy(): void {\n this.localeSub.unsubscribe();\n }\n\n /**\n * Handle a calendarCell selected\n */\n public selectCalendarCell(cell: CalendarCell): void {\n this.selectMonth(cell.value);\n }\n\n /**\n * Handle a new month selected\n */\n private selectMonth(month: number): void {\n const firstDateOfMonth = this.dateTimeAdapter.createDate(\n this.dateTimeAdapter.getYear(this.pickerDayjs),\n month,\n 1\n );\n\n this.monthSelected.emit(firstDateOfMonth);\n\n const daysInMonth = this.dateTimeAdapter.getNumDaysInMonth(\n firstDateOfMonth\n );\n const result = this.dateTimeAdapter.createDate(\n this.dateTimeAdapter.getYear(this.pickerDayjs),\n month,\n Math.min(\n daysInMonth,\n this.dateTimeAdapter.getDate(this.pickerDayjs)\n ),\n this.dateTimeAdapter.getHours(this.pickerDayjs),\n this.dateTimeAdapter.getMinutes(this.pickerDayjs),\n this.dateTimeAdapter.getSeconds(this.pickerDayjs)\n );\n\n this.change.emit(result);\n }\n\n /**\n * Handle keydown event on calendar body\n */\n public handleCalendarKeydown(event: KeyboardEvent): void {\n let dayjs;\n switch (event.keyCode) {\n // minus 1 month\n case LEFT_ARROW:\n dayjs = this.dateTimeAdapter.addCalendarMonths(\n this.pickerDayjs,\n -1\n );\n this.pickerDayjsChange.emit(dayjs);\n break;\n\n // add 1 month\n case RIGHT_ARROW:\n dayjs = this.dateTimeAdapter.addCalendarMonths(\n this.pickerDayjs,\n 1\n );\n this.pickerDayjsChange.emit(dayjs);\n break;\n\n // minus 3 months\n case UP_ARROW:\n dayjs = this.dateTimeAdapter.addCalendarMonths(\n this.pickerDayjs,\n -3\n );\n this.pickerDayjsChange.emit(dayjs);\n break;\n\n // add 3 months\n case DOWN_ARROW:\n dayjs = this.dateTimeAdapter.addCalendarMonths(\n this.pickerDayjs,\n 3\n