UNPKG

ng-devui

Version:

DevUI components based on Angular

1 lines 231 kB
{"version":3,"file":"ng-devui-datepicker-pro.mjs","sources":["../../devui/datepicker-pro/datepicker-pro.service.ts","../../devui/datepicker-pro/lib/timepicker-panel.component.ts","../../devui/datepicker-pro/lib/timepicker-panel.component.html","../../devui/datepicker-pro/lib/footer-panel.component.ts","../../devui/datepicker-pro/lib/footer-panel.component.html","../../devui/datepicker-pro/lib/calendar-panel.component.ts","../../devui/datepicker-pro/lib/calendar-panel.component.html","../../devui/datepicker-pro/lib/month-panel.component.ts","../../devui/datepicker-pro/lib/month-panel.component.html","../../devui/datepicker-pro/lib/year-panel.component.ts","../../devui/datepicker-pro/lib/year-panel.component.html","../../devui/datepicker-pro/datepicker-panel.component.ts","../../devui/datepicker-pro/datepicker-panel.component.html","../../devui/datepicker-pro/datepicker-pro-calendar.component.ts","../../devui/datepicker-pro/datepicker-pro-calendar.component.html","../../devui/datepicker-pro/datepicker-pro.component.ts","../../devui/datepicker-pro/datepicker-pro.component.html","../../devui/datepicker-pro/range-datepicker-pro.component.ts","../../devui/datepicker-pro/range-datepicker-pro.component.html","../../devui/datepicker-pro/datepicker-pro.module.ts","../../devui/datepicker-pro/ng-devui-datepicker-pro.ts"],"sourcesContent":["import { DOCUMENT } from '@angular/common';\nimport { Inject, Injectable, OnDestroy, TemplateRef } from '@angular/core';\nimport { Subject } from 'rxjs';\n\n@Injectable()\nexport class DatepickerProService implements OnDestroy {\n curDate: Date;\n curRangeDate: Date[] = [];\n curHoverDate: Date;\n startIndexOfWeek: number;\n isRange: boolean;\n markedRangeDateList: Date[][];\n markDateInfoTemplate: TemplateRef<any>;\n markedDateList: Date[];\n showTime: boolean;\n calendarRange = [1970, 2099];\n currentActiveInput: 'start' | 'end' = 'start';\n _minDate: Date = new Date(this.calendarRange[0], 0, 1);\n set minDate(value: Date) {\n this._minDate = new Date(value) || new Date(this.calendarRange[0], 0, 1);\n this.detectedChanges.next();\n }\n get minDate(): Date {\n return this._minDate;\n }\n _maxDate: Date = new Date(this.calendarRange[1], 11, 31, 23, 59, 59);\n set maxDate(value: Date) {\n this._maxDate = new Date(value) || new Date(this.calendarRange[1], 11, 31, 23, 59, 59);\n this.detectedChanges.next();\n }\n get maxDate(): Date {\n return this._maxDate;\n }\n document: Document;\n get closeAfterSelected(): boolean {\n return !this.isRange && !this.showTime;\n }\n\n get curHour() {\n if (this.isRange) {\n return (this.currentActiveInput === 'start' ? this.curRangeDate[0]?.getHours() : this.curRangeDate[1]?.getHours()) || 0;\n } else {\n return this.curDate?.getHours() || 0;\n }\n }\n get curMin() {\n if (this.isRange) {\n return (this.currentActiveInput === 'start' ? this.curRangeDate[0]?.getMinutes() : this.curRangeDate[1]?.getMinutes()) || 0;\n } else {\n return this.curDate?.getMinutes() || 0;\n }\n }\n get curSec() {\n if (this.isRange) {\n return (this.currentActiveInput === 'start' ? this.curRangeDate[0]?.getSeconds() : this.curRangeDate[1]?.getSeconds()) || 0;\n } else {\n return this.curDate?.getSeconds() || 0;\n }\n }\n readonly toggleEvent = new Subject<boolean>();\n readonly closeDropdownEvent = new Subject<boolean>();\n readonly activeInputChange = new Subject<'start' | 'end'>();\n readonly selectedDateChange = new Subject<{\n type: 'single' | 'range';\n value: Date | Date[];\n }>();\n readonly updateDateValue = new Subject<{\n type: 'single' | 'range';\n value: Date | Date[];\n }>();\n readonly selectedTimeChange = new Subject<{\n activeInput?: 'start' | 'end';\n hour: number;\n min: number;\n seconds: number;\n }>();\n readonly updateTimeChange = new Subject<{\n activeInput?: 'start' | 'end';\n hour: number;\n min: number;\n seconds: number;\n }>();\n readonly detectedChanges = new Subject<void>();\n\n constructor(@Inject(DOCUMENT) private doc: any) {\n this.document = this.doc;\n }\n dateInRange(date: Date): boolean {\n if (!date) {\n return true;\n }\n return (date.getTime() > this.minDate.getTime() && date.getTime() < this.maxDate.getTime()) ||\n date.toDateString() === this.minDate.toDateString() || date.toDateString() === this.maxDate.toDateString();\n }\n\n resetMin() {\n this.minDate = new Date(this.calendarRange[0], 0, 1);\n }\n\n resetMax() {\n this.maxDate = new Date(this.calendarRange[1], 11, 31);\n }\n\n // 对范围模式下一些非法的选择进行修正\n fixRangeDate() {\n const start = this.curRangeDate[0]?.getTime();\n const end = this.curRangeDate[1]?.getTime();\n\n if (start && end && end < start) {\n if (this.currentActiveInput === 'start') {\n this.curRangeDate[1] = null;\n } else if (this.currentActiveInput === 'end') {\n this.curRangeDate[0] = null;\n }\n }\n }\n\n // 判断日期是否为起始日期\n isStartDate(date: Date): boolean {\n if (!this.isRange) {\n return false;\n }\n\n if (this.currentActiveInput === 'start') {\n return date.toDateString() === this.curHoverDate?.toDateString() || date.toDateString() === this.curRangeDate[0]?.toDateString();\n }\n\n return date.toDateString() === this.curRangeDate[0]?.toDateString();\n }\n\n // 判断日期是否为结束日期\n isEndDate(date: Date): boolean {\n if (!this.isRange) {\n return false;\n }\n\n if (this.currentActiveInput === 'end') {\n return date.toDateString() === this.curHoverDate?.toDateString() || date.toDateString() === this.curRangeDate[1]?.toDateString();\n }\n\n return date.toDateString() === this.curRangeDate[1]?.toDateString();\n }\n\n // 判断日期是否在hover范围或者选中的范围内\n isDateInRange(date: Date): boolean {\n const dateTime = date.getTime();\n const dateStr = date.toDateString();\n if (this.isRange) {\n if (this.currentActiveInput === 'start') {\n return (this.curHoverDate || this.curRangeDate[0])?.getTime() < dateTime &&\n (this.curHoverDate || this.curRangeDate[0])?.toDateString() !== dateStr &&\n this.curRangeDate[1]?.getTime() > dateTime &&\n this.curRangeDate[1]?.toDateString() !== dateStr;\n } else {\n return this.curRangeDate[0]?.getTime() < dateTime &&\n this.curRangeDate[0]?.toDateString() !== dateStr &&\n (this.curHoverDate || this.curRangeDate[1])?.getTime() > dateTime &&\n (this.curHoverDate || this.curRangeDate[1]).toDateString() !== dateStr;\n }\n } else {\n return false;\n }\n }\n\n // 判断日期是否在已选中的范围内,与hover做区分\n isDateInSelectRange(date: Date) {\n if (!this.isRange) {\n return false;\n }\n\n if (!this.curRangeDate[0] || !this.curRangeDate[1]) {\n return false;\n }\n\n return this.curRangeDate[0].getTime() < date.getTime() && this.curRangeDate[1].getTime() > date.getTime() &&\n this.curRangeDate[1].toDateString() !== date.toDateString() && this.curRangeDate[0].toDateString() !== date.toDateString();\n }\n\n isDateActive(date: Date): boolean {\n const dateStr = date.toDateString();\n if (this.isRange) {\n return dateStr === this.curRangeDate[0]?.toDateString() || dateStr === this.curRangeDate[1]?.toDateString();\n } else {\n return dateStr === this.curDate?.toDateString();\n }\n }\n\n isMonthActive(yearIndex: number, monthIndex: number): boolean {\n if (this.isRange) {\n return (yearIndex === this.curRangeDate[0]?.getFullYear() && monthIndex === this.curRangeDate[0]?.getMonth()) ||\n (yearIndex === this.curRangeDate[1]?.getFullYear() && monthIndex === this.curRangeDate[1]?.getMonth());\n } else {\n return yearIndex === this.curDate?.getFullYear() && monthIndex === this.curDate?.getMonth();\n }\n }\n\n isYearActive(yearIndex: number): boolean {\n if (this.isRange) {\n return yearIndex === this.curRangeDate[0]?.getFullYear() || yearIndex === this.curRangeDate[1]?.getFullYear();\n } else {\n return yearIndex === this.curDate?.getFullYear();\n }\n }\n\n // 是否为范围选中日期中对应的input激活项\n isActiveInputTypeDate(date: Date) {\n if (!this.isRange) {\n return false;\n }\n\n if (this.currentActiveInput === 'start') {\n return date.toDateString() === this.curRangeDate[0]?.toDateString();\n } else {\n return date.toDateString() === this.curRangeDate[1]?.toDateString();\n }\n }\n\n // 是否为选中日期且在废弃范围逻辑内\n isDateAbandon(date: Date): boolean {\n if (!this.isRange || (!this.curRangeDate[0] || !this.curRangeDate[1])) {\n return false;\n }\n\n if (!this.isDateActive(date)) {\n return false;\n }\n\n if (this.currentActiveInput === 'start') {\n return this.curHoverDate?.getTime() > date.getTime();\n } else {\n return this.curHoverDate?.getTime() < date.getTime();\n }\n }\n\n isInSuggestList(date: Date): boolean {\n if (!this.markedRangeDateList) {\n return false;\n }\n\n for (let index = 0; index < this.markedRangeDateList.length; index++) {\n const range = this.markedRangeDateList[index];\n if (\n range[0]?.getTime() < date.getTime() && range[1]?.getTime() > date.getTime()\n ) {\n return true;\n }\n\n if (\n range[0]?.toDateString() === date.toDateString() || range[1]?.toDateString() === date.toDateString()\n ) {\n return true;\n }\n }\n return false;\n }\n\n isMarkedDate(date: Date): boolean {\n for (let index = 0; index < this.markedDateList?.length; index++) {\n if (this.markedDateList[index]?.toDateString() === date.toDateString()) {\n return true;\n }\n }\n return false;\n }\n\n mearsureStrWidth(str: string): number {\n const mearsureDom = this.document.createElement('span');\n mearsureDom.innerText = str;\n mearsureDom.style.visibility = 'hidden';\n this.document.body.appendChild(mearsureDom);\n\n const domWidth = mearsureDom.offsetWidth;\n\n this.document.body.removeChild(mearsureDom);\n return domWidth;\n }\n\n ngOnDestroy(): void {\n this.toggleEvent.complete();\n this.selectedDateChange.complete();\n this.closeDropdownEvent.complete();\n this.updateDateValue.complete();\n this.updateTimeChange.complete();\n this.selectedTimeChange.complete();\n this.activeInputChange.complete();\n this.detectedChanges.complete();\n }\n\n}\n\n@Injectable()\nexport class DatepickerProCommonDataService {\n calendarDataCache = {};\n}\n","import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, OnDestroy, OnInit } from '@angular/core';\r\nimport { I18nInterface, I18nService } from 'ng-devui/i18n';\r\nimport { Subject, Subscription } from 'rxjs';\r\nimport { takeUntil } from 'rxjs/operators';\r\nimport { DatepickerProService } from '../datepicker-pro.service';\r\n\r\ninterface TimeObj {\r\n time: string;\r\n type?: string;\r\n active?: boolean;\r\n disabled?: boolean;\r\n}\r\n\r\n@Component({\r\n selector: 'd-timepicker-panel',\r\n templateUrl: './timepicker-panel.component.html',\r\n styleUrls: ['./timepicker-panel.component.scss'],\r\n preserveWhitespaces: false,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n})\r\nexport class TimepickerPanelComponent implements OnInit, OnDestroy {\r\n firstList: Array<TimeObj> = [];\r\n secondList: Array<TimeObj> = [];\r\n thirdList: Array<TimeObj> = [];\r\n\r\n hourIndex = 0;\r\n minIndex = 0;\r\n secIndex = 0;\r\n\r\n typeList = ['hour', 'min', 'sec'];\r\n\r\n unsubscribe$ = new Subject<void>();\r\n i18nText: I18nInterface['datePickerPro'];\r\n i18nSubscription: Subscription;\r\n\r\n constructor(private el: ElementRef, private pickSrv: DatepickerProService, protected i18n: I18nService, private cdr: ChangeDetectorRef) {}\r\n\r\n ngOnInit(): void {\r\n this.initDateList();\r\n this.initObservable();\r\n this.setI18nText();\r\n }\r\n\r\n setI18nText() {\r\n this.i18nText = this.i18n.getI18nText().datePickerPro;\r\n this.i18nSubscription = this.i18n.langChange().subscribe((data) => {\r\n this.i18nText = data.datePickerPro;\r\n });\r\n }\r\n\r\n initObservable() {\r\n this.pickSrv.toggleEvent.pipe(takeUntil(this.unsubscribe$)).subscribe((isOpen) => {\r\n if (isOpen) {\r\n setTimeout(() => {\r\n this.initDateList(true);\r\n this.cdr.detectChanges();\r\n });\r\n }\r\n });\r\n\r\n this.pickSrv.updateTimeChange.pipe(takeUntil(this.unsubscribe$)).subscribe((value) => {\r\n if (this.hourIndex !== value.hour) {\r\n this.chooseTime('hour', value.hour);\r\n }\r\n\r\n if (this.minIndex !== value.min) {\r\n this.chooseTime('min', value.min);\r\n }\r\n\r\n if (this.secIndex !== value.seconds) {\r\n this.chooseTime('sec', value.seconds);\r\n }\r\n this.cdr.detectChanges();\r\n });\r\n\r\n if (this.pickSrv.isRange) {\r\n this.pickSrv.activeInputChange.pipe(takeUntil(this.unsubscribe$)).subscribe((type) => {\r\n const isNull = (type === 'start' && !this.pickSrv.curRangeDate[0]) || (type === 'end' && !this.pickSrv.curRangeDate[1]);\r\n if (this.hourIndex !== this.pickSrv.curHour || isNull) {\r\n this.chooseTime('hour', isNull ? null : this.pickSrv.curHour);\r\n }\r\n\r\n if (this.minIndex !== this.pickSrv.curMin || isNull) {\r\n this.chooseTime('min', isNull ? null : this.pickSrv.curMin);\r\n }\r\n\r\n if (this.secIndex !== this.pickSrv.curSec || isNull) {\r\n this.chooseTime('sec', isNull ? null : this.pickSrv.curSec);\r\n }\r\n });\r\n }\r\n\r\n this.pickSrv.selectedDateChange.pipe(takeUntil(this.unsubscribe$)).subscribe(() => {\r\n const isFixTime = this.fixTime();\r\n\r\n this.cdr.detectChanges();\r\n\r\n if (isFixTime) {\r\n setTimeout(() => {\r\n this.pickSrv.selectedTimeChange.next({\r\n activeInput: this.pickSrv.currentActiveInput,\r\n hour: this.hourIndex,\r\n min: this.minIndex,\r\n seconds: this.secIndex,\r\n });\r\n });\r\n }\r\n });\r\n }\r\n\r\n initDateList(justScroll = false) {\r\n if (this.pickSrv.curDate) {\r\n this.hourIndex = this.pickSrv.curDate.getHours();\r\n this.minIndex = this.pickSrv.curDate.getMinutes();\r\n this.secIndex = this.pickSrv.curDate.getSeconds();\r\n }\r\n this.typeList.forEach((type) => {\r\n this.chooseTime(type, this[`${type}Index`], false, justScroll);\r\n });\r\n }\r\n\r\n chooseTime(type: string, index: number, handle = false, justScroll = false) {\r\n if (this.itemIsDisabled(type, index)) {\r\n return;\r\n }\r\n let fixed: boolean;\r\n switch (type) {\r\n case 'hour':\r\n this.hourIndex = index;\r\n fixed = this.fixTime(justScroll);\r\n this.firstList = new Array(24).fill(1).map((t, i) => {\r\n return {\r\n time: i < 10 ? `0${i}` : String(i),\r\n type: 'hour',\r\n active: this.hourIndex === i,\r\n };\r\n });\r\n if (!fixed) {\r\n this.setScroll('first', this.hourIndex, justScroll);\r\n }\r\n break;\r\n case 'min':\r\n this.minIndex = index;\r\n this.fixTime(justScroll);\r\n this.secondList = new Array(60).fill(1).map((t, i) => {\r\n return {\r\n time: i < 10 ? `0${i}` : String(i),\r\n type: 'min',\r\n active: this.minIndex === i,\r\n };\r\n });\r\n if (!fixed) {\r\n this.setScroll('second', this.minIndex, justScroll);\r\n }\r\n break;\r\n case 'sec':\r\n this.secIndex = index;\r\n this.thirdList = new Array(60).fill(1).map((t, i) => {\r\n return {\r\n time: i < 10 ? `0${i}` : String(i),\r\n type: 'sec',\r\n active: this.secIndex === i,\r\n };\r\n });\r\n this.setScroll('third', this.secIndex, justScroll);\r\n break;\r\n default:\r\n }\r\n\r\n this.cdr.detectChanges();\r\n\r\n if (handle) {\r\n this.pickSrv.selectedTimeChange.next({\r\n activeInput: this.pickSrv.currentActiveInput,\r\n hour: this.hourIndex,\r\n min: this.minIndex,\r\n seconds: this.secIndex,\r\n });\r\n }\r\n }\r\n\r\n fixTime(justScroll = false) {\r\n let curTime: Date;\r\n\r\n if (this.pickSrv.isRange) {\r\n const target = this.pickSrv.curRangeDate[this.pickSrv.currentActiveInput === 'start' ? 0 : 1];\r\n if (!target) {\r\n return;\r\n }\r\n curTime = new Date(target);\r\n } else {\r\n if (!this.pickSrv.curDate) {\r\n return;\r\n }\r\n curTime = new Date(this.pickSrv.curDate);\r\n }\r\n curTime.setHours(this.hourIndex);\r\n curTime.setMinutes(this.minIndex);\r\n curTime.setSeconds(this.secIndex);\r\n\r\n let isFixTime = false;\r\n\r\n if (curTime.getTime() < this.pickSrv.minDate.getTime()) {\r\n this.pickSrv.curDate = this.pickSrv.minDate;\r\n isFixTime = true;\r\n }\r\n\r\n if (curTime.getTime() > this.pickSrv.maxDate.getTime()) {\r\n this.pickSrv.curDate = this.pickSrv.maxDate;\r\n isFixTime = true;\r\n }\r\n\r\n if (isFixTime) {\r\n this.initDateList(justScroll);\r\n }\r\n\r\n return isFixTime;\r\n }\r\n\r\n itemIsDisabled(type, index) {\r\n let curTime: Date;\r\n if (this.pickSrv.isRange) {\r\n const target = this.pickSrv.curRangeDate[this.pickSrv.currentActiveInput === 'start' ? 0 : 1];\r\n if (!target) {\r\n return false;\r\n }\r\n curTime = new Date(target);\r\n } else {\r\n if (!this.pickSrv.curDate) {\r\n return false;\r\n }\r\n curTime = new Date(this.pickSrv.curDate);\r\n }\r\n\r\n if (!curTime) {\r\n return false;\r\n }\r\n\r\n let flag = false;\r\n let time;\r\n let isTimeBoundary = false;\r\n const maxTime = this.pickSrv.maxDate.getTime();\r\n const minTime = this.pickSrv.minDate.getTime();\r\n switch (type) {\r\n case 'hour':\r\n curTime.setHours(index);\r\n curTime.setMinutes(this.minIndex);\r\n time = curTime.setSeconds(this.secIndex);\r\n isTimeBoundary = time > maxTime ? this.pickSrv.maxDate.getHours() === index : this.pickSrv.minDate.getHours() === index;\r\n break;\r\n case 'min':\r\n curTime.setHours(this.hourIndex);\r\n curTime.setMinutes(index);\r\n time = curTime.setSeconds(this.secIndex);\r\n isTimeBoundary = time > maxTime ? this.pickSrv.maxDate.getMinutes() === index : this.pickSrv.minDate.getMinutes() === index;\r\n break;\r\n case 'sec':\r\n curTime.setHours(this.hourIndex);\r\n curTime.setMinutes(this.minIndex);\r\n time = curTime.setSeconds(index);\r\n break;\r\n default:\r\n }\r\n flag = time > maxTime || time < minTime;\r\n\r\n return flag && !isTimeBoundary;\r\n }\r\n\r\n setAllScroll(first: number, second: number, third: number, justScroll: boolean) {\r\n this.setScroll('first', first, justScroll);\r\n this.setScroll('second', second, justScroll);\r\n this.setScroll('third', third, justScroll);\r\n }\r\n\r\n setScroll(whichList, index, justScroll?) {\r\n const scroll = (22 + 8) * index;\r\n const duration = justScroll ? 0 : 100;\r\n if (this.el) {\r\n this.scrollTo(this.el.nativeElement.querySelector(`.devui-${whichList}-list`), scroll, duration);\r\n }\r\n }\r\n\r\n scrollTo(element: HTMLElement, to: number, duration: number): void {\r\n if (typeof window === undefined || !element) {\r\n return;\r\n }\r\n if (duration <= 0) {\r\n element.scrollTop = to;\r\n return;\r\n }\r\n const difference = to - element.scrollTop;\r\n const perTick = (difference / duration) * 10;\r\n const reqAnimFrame =\r\n (window as any).requestAnimationFrame ||\r\n (window as any).mozRequestAnimationFrame ||\r\n (window as any).msRequestAnimationFrame ||\r\n (window as any).oRequestAnimationFrame;\r\n reqAnimFrame(() => {\r\n element.scrollTop = element.scrollTop + perTick;\r\n if (element.scrollTop === to) {\r\n return;\r\n }\r\n this.scrollTo(element, to, duration - 10);\r\n });\r\n }\r\n\r\n ngOnDestroy() {\r\n this.unsubscribe$.next();\r\n this.unsubscribe$.complete();\r\n }\r\n}\r\n","<div class=\"devui-timepicker-panel\">\n <div class=\"devui-time-header\">\n <span class=\"devui-time-header-item\">{{ i18nText?.hour }}</span>\n <span class=\"devui-time-header-item\">{{ i18nText?.min }}</span>\n <span class=\"devui-time-header-item\">{{ i18nText?.second }}</span>\n </div>\n <div class=\"devui-time-picker\">\n <ul *ngIf=\"firstList.length\" class=\"devui-time-list devui-first-list\">\n <li\n *ngFor=\"let item of firstList; let i = index\"\n class=\"devui-time-item devui-first-item\"\n [ngClass]=\"{\n active: item?.active,\n disabled: itemIsDisabled('hour', i)\n }\"\n (click)=\"chooseTime(item.type, i, true)\"\n >\n {{ item?.time }}\n </li>\n </ul>\n <ul *ngIf=\"secondList.length\" class=\"devui-time-list devui-second-list\">\n <li\n *ngFor=\"let item of secondList; let i = index\"\n class=\"devui-time-item devui-second-item\"\n [ngClass]=\"{\n active: item?.active,\n disabled: itemIsDisabled('min', i)\n }\"\n (click)=\"chooseTime(item.type, i, true)\"\n >\n {{ item?.time }}\n </li>\n </ul>\n <ul *ngIf=\"thirdList.length\" class=\"devui-time-list devui-third-list\">\n <li\n *ngFor=\"let item of thirdList; let i = index\"\n class=\"devui-time-item devui-third-item\"\n [ngClass]=\"{\n active: item?.active,\n disabled: itemIsDisabled('sec', i)\n }\"\n (click)=\"chooseTime(item.type, i, true)\"\n >\n {{ item?.time }}\n </li>\n </ul>\n </div>\n</div>\n","import {\n Component, Input, OnDestroy, TemplateRef\n} from '@angular/core';\nimport { I18nInterface, I18nService } from 'ng-devui/i18n';\nimport { Subscription } from 'rxjs';\nimport { DatepickerProService } from '../datepicker-pro.service';\n\n@Component({\n selector: 'd-datepicker-footer-panel',\n templateUrl: './footer-panel.component.html',\n styleUrls: ['./footer-panel.component.scss'],\n preserveWhitespaces: false,\n})\nexport class FooterPanelComponent implements OnDestroy {\n @Input() footerTemplate: TemplateRef<any>;\n\n i18nText: I18nInterface['common'];\n i18nSubscription: Subscription;\n\n get isRange() {\n return this.pickerSrv.isRange;\n }\n\n get confirmDisable() {\n if (this.isRange) {\n return (this.pickerSrv.currentActiveInput === 'start' && !this.pickerSrv.curRangeDate[0]) ||\n (this.pickerSrv.currentActiveInput === 'end' && !this.pickerSrv.curRangeDate[1]);\n } else {\n return false;\n }\n }\n\n constructor(\n private pickerSrv: DatepickerProService,\n protected i18n: I18nService\n ) {\n this.setI18nText();\n }\n\n setI18nText() {\n this.i18nText = this.i18n.getI18nText().common;\n this.i18nSubscription = this.i18n.langChange().subscribe((data) => {\n this.i18nText = data.common;\n });\n }\n\n ensureDate() {\n if (this.pickerSrv.isRange) {\n if (this.pickerSrv.currentActiveInput === 'start') {\n this.pickerSrv.currentActiveInput = 'end';\n this.pickerSrv.activeInputChange.next('end');\n } else if (!this.pickerSrv.curRangeDate[0]) {\n this.pickerSrv.currentActiveInput = 'start';\n this.pickerSrv.activeInputChange.next('start');\n } else {\n this.close(true);\n }\n } else {\n this.close(true);\n }\n }\n\n close(isConfirm = false) {\n this.pickerSrv.closeDropdownEvent.next(isConfirm);\n }\n\n ngOnDestroy() {\n this.i18nSubscription.unsubscribe();\n }\n\n}\n","<div class=\"devui-datepicekr-pro-footer\">\n <ng-template [ngTemplateOutlet]=\"footerTemplate || default\"> </ng-template>\n</div>\n\n<ng-template #default>\n <d-button bsStyle=\"primary\" [disabled]=\"confirmDisable\" (click)=\"ensureDate()\"> {{ i18nText.btnOk }} </d-button>\n <d-button class=\"devui-cancel-button\" bsStyle=\"common\" *ngIf=\"isRange\" (click)=\"close()\"> {{ i18nText.btnCancel }} </d-button>\n</ng-template>\n","import { CdkScrollable, CdkVirtualScrollViewport, ScrollDispatcher } from '@angular/cdk/scrolling';\r\nimport {\r\n ChangeDetectionStrategy,\r\n ChangeDetectorRef,\r\n Component, Input, OnDestroy, OnInit, ViewChild\r\n} from '@angular/core';\r\nimport { I18nInterface, I18nService } from 'ng-devui/i18n';\r\nimport { Subject, Subscription } from 'rxjs';\r\nimport { debounceTime, filter, takeUntil } from 'rxjs/operators';\r\nimport { DatepickerProCommonDataService, DatepickerProService } from '../datepicker-pro.service';\r\nimport { DevuiCalendarDateItem } from './datepicker-pro.type';\r\n\r\nconst DAY_DURATION = 24 * 60 * 60 * 1000;\r\nconst HOUR_DURATION = 60 * 60 * 1000;\r\n\r\n@Component({\r\n selector: 'd-calendar-panel',\r\n templateUrl: './calendar-panel.component.html',\r\n styleUrls: ['./calendar-panel.component.scss'],\r\n preserveWhitespaces: false,\r\n changeDetection: ChangeDetectionStrategy.OnPush\r\n})\r\nexport class CalendarPanelComponent implements OnInit, OnDestroy {\r\n @ViewChild('scrollBody') scrollBodyCmp: CdkVirtualScrollViewport;\r\n @ViewChild('scrollList') scrollListCmp: CdkVirtualScrollViewport;\r\n @Input() isRangeType: boolean;\r\n @Input() isWeekSelect = false;\r\n\r\n currentBodyIndex = 0;\r\n today: Date;\r\n calendarItemSize = 186; // 一个月份日历的高度\r\n\r\n i18nText: I18nInterface['datePickerPro'];\r\n i18nSubscription: Subscription;\r\n unsubscribe$ = new Subject<void>();\r\n\r\n allMonthList = [];\r\n yearAndMonthList = [];\r\n scrollListener: Subscription;\r\n isListCollopse = false;\r\n weekHoverRange = [];\r\n curWeekHoverDate: Date;\r\n isSummerTimeZone = false;\r\n\r\n get curHoverDate() {\r\n return this.pickerSrv.curHoverDate;\r\n }\r\n\r\n set curHoverDate(value: Date) {\r\n this.pickerSrv.curHoverDate = value;\r\n this.cdr.detectChanges();\r\n }\r\n\r\n get curDate(): Date {\r\n if (this.isRangeType) {\r\n if (this.pickerSrv.currentActiveInput === 'start') {\r\n return this.pickerSrv.curRangeDate[0];\r\n } else if (this.pickerSrv.currentActiveInput === 'end') {\r\n return this.pickerSrv.curRangeDate[1];\r\n }\r\n } else {\r\n return this.pickerSrv.curDate;\r\n }\r\n }\r\n\r\n set curDate(value: Date) {\r\n if (this.isRangeType) {\r\n if (this.pickerSrv.currentActiveInput === 'start') {\r\n this.pickerSrv.curRangeDate[0] = value;\r\n } else if (this.pickerSrv.currentActiveInput === 'end') {\r\n if (this.pickerSrv.showTime) {\r\n this.pickerSrv.curRangeDate[1] =\r\n (this.pickerSrv.curRangeDate[1] && this.pickerSrv.curRangeDate[1].toDateString() === value.toDateString())\r\n ? value\r\n : new Date(value.setHours(23, 59, 59));\r\n } else {\r\n this.pickerSrv.curRangeDate[1] = new Date(value.setHours(23, 59, 59));\r\n }\r\n }\r\n } else {\r\n this.pickerSrv.curDate = value;\r\n }\r\n }\r\n\r\n get selectedRangeDate(): Date[] {\r\n return this.pickerSrv.curRangeDate;\r\n }\r\n\r\n set selectedRangeDate(dateList: Date[]) {\r\n this.pickerSrv.curRangeDate = dateList;\r\n }\r\n\r\n get markDateTemplate() {\r\n return this.pickerSrv.markDateInfoTemplate;\r\n }\r\n\r\n constructor(\r\n protected i18n: I18nService,\r\n private pickerSrv: DatepickerProService,\r\n private dataSrv: DatepickerProCommonDataService,\r\n private cdr: ChangeDetectorRef,\r\n private scrollDispatcher: ScrollDispatcher\r\n ) {\r\n }\r\n\r\n setI18nText() {\r\n this.i18nText = this.i18n.getI18nText().datePickerPro;\r\n this.i18nSubscription = this.i18n.langChange().pipe(\r\n takeUntil(this.unsubscribe$)\r\n ).subscribe((data) => {\r\n this.i18nText = data.datePickerPro;\r\n this.cdr.detectChanges();\r\n });\r\n }\r\n\r\n ngOnInit(): void {\r\n this.setI18nText();\r\n this.today = new Date();\r\n this.isSummerTimeZone = this.judgeIsSummerTimeZone();\r\n this.initDataList();\r\n this.initObservable();\r\n }\r\n\r\n // 判断当前时区是否需要夏令时变换\r\n private judgeIsSummerTimeZone() {\r\n const Jan1 = new Date(2023, 0);\r\n const Jul1 = new Date(2023, 6);\r\n // 两个日期之间在非夏令时地区相差181天整\r\n if ((Jul1.getTime() - Jan1.getTime()) / DAY_DURATION !== 181) {\r\n return true;\r\n };\r\n\r\n return false;\r\n }\r\n\r\n private initObservable() {\r\n this.pickerSrv.toggleEvent.pipe(\r\n takeUntil(this.unsubscribe$),\r\n ).subscribe(isOpen => {\r\n if (isOpen) {\r\n setTimeout(() => {\r\n this.scrollBodyCmp.checkViewportSize();\r\n this.scrollListCmp.checkViewportSize();\r\n this.goToDate(this.curDate || new Date(), 'auto');\r\n });\r\n\r\n // 首次展开添加滚动监听\r\n if (!this.scrollListener) {\r\n this.initScrollListener();\r\n }\r\n } else {\r\n this.curHoverDate = null;\r\n }\r\n });\r\n\r\n this.pickerSrv.updateDateValue.pipe(\r\n takeUntil(this.unsubscribe$),\r\n ).subscribe(res => {\r\n if (this.isRangeType) {\r\n this.updateRangeDate(res.value as Date[]);\r\n } else {\r\n this.updateSingleDate(res.value as Date);\r\n }\r\n });\r\n\r\n this.pickerSrv.detectedChanges.pipe(\r\n takeUntil(this.unsubscribe$),\r\n ).subscribe(() => {\r\n this.cdr.detectChanges();\r\n });\r\n\r\n this.pickerSrv.activeInputChange.pipe(\r\n takeUntil(this.unsubscribe$)\r\n ).subscribe(type => {\r\n if (type === 'start') {\r\n this.goToDate(this.selectedRangeDate[0] || this.selectedRangeDate[1] || new Date());\r\n } else {\r\n if (!this.selectedRangeDate[1] && this.selectedRangeDate[0]) {\r\n this.updateRangeDate([\r\n this.selectedRangeDate[0],\r\n new Date(new Date(this.selectedRangeDate[0].getTime()).setHours(23, 59, 59))\r\n ]);\r\n }\r\n this.goToDate(this.selectedRangeDate[1] || this.selectedRangeDate[0] || new Date());\r\n }\r\n });\r\n }\r\n\r\n private initScrollListener() {\r\n this.scrollDispatcher.scrolled().pipe(\r\n takeUntil(this.unsubscribe$),\r\n filter((res: CdkScrollable) => {\r\n return res && res.getElementRef().nativeElement.classList.contains('devui-tbody-wrapper');\r\n }),\r\n debounceTime(50)\r\n ).subscribe(() => {\r\n const offsetY = this.scrollBodyCmp.measureScrollOffset();\r\n // 当滚动超过一个月面板的一半时,就更新月份\r\n this.currentBodyIndex = Math.floor(offsetY / this.calendarItemSize) + (offsetY % this.calendarItemSize > 100 ? 1 : 0);\r\n const listIndex = this.isListCollopse ?\r\n Math.floor(this.currentBodyIndex / 12) : this.currentBodyIndex + Math.floor(this.currentBodyIndex / 12) + 1;\r\n this.goToListByIndex(listIndex);\r\n this.cdr.detectChanges();\r\n });\r\n }\r\n\r\n private initDataList() {\r\n const key = `${this.pickerSrv.calendarRange.join('-')}-${\r\n this.pickerSrv.minDate.toDateString() + this.pickerSrv.maxDate.toDateString()\r\n }`;\r\n if (this.dataSrv.calendarDataCache[key]) {\r\n this.yearAndMonthList = this.dataSrv.calendarDataCache[key].yearAndMonthList;\r\n this.allMonthList = this.dataSrv.calendarDataCache[key].allMonthList;\r\n return;\r\n }\r\n\r\n this.yearAndMonthList = [];\r\n this.allMonthList = [];\r\n\r\n for (let yearIndex = this.pickerSrv.calendarRange[0]; yearIndex <= this.pickerSrv.calendarRange[1]; yearIndex++) {\r\n this.yearAndMonthList.push({\r\n year: yearIndex,\r\n isMonth: false,\r\n active: false\r\n });\r\n for (let monthIndex = 0; monthIndex < 12; monthIndex++) {\r\n this.allMonthList.push({\r\n year: yearIndex,\r\n month: monthIndex,\r\n displayWeeks: this.getDisplayWeeks(yearIndex, monthIndex)\r\n });\r\n\r\n this.yearAndMonthList.push({\r\n year: yearIndex,\r\n month: monthIndex,\r\n isMonth: true,\r\n active: false\r\n });\r\n }\r\n }\r\n\r\n this.dataSrv.calendarDataCache[key] = {\r\n yearAndMonthList: this.yearAndMonthList,\r\n allMonthList: this.allMonthList\r\n };\r\n }\r\n\r\n private getDisplayWeeks(yearIndex: number, monthIndex: number): DevuiCalendarDateItem[] {\r\n const firstDayOfMonth = new Date(yearIndex, monthIndex, 1);\r\n const weekOfDay = firstDayOfMonth.getDay();\r\n const startDate = new Date(firstDayOfMonth.getTime() - weekOfDay * DAY_DURATION);\r\n const displayWeeks = [];\r\n for (let i = 0; i < 6; i++) {\r\n const startWeekDate = startDate.getTime() + i * 7 * DAY_DURATION;\r\n const weekDays = new Array(7).fill(0).map((value, index) => {\r\n let currentDate = new Date(startWeekDate + index * DAY_DURATION);\r\n // 夏令时区检测出错误的冬令时时间,加快一小时\r\n if (this.isSummerTimeZone && currentDate.getHours() === 23) {\r\n currentDate = new Date(startWeekDate + index * DAY_DURATION + HOUR_DURATION);\r\n }\r\n return {\r\n day: this.fillLeft(currentDate.getDate()),\r\n date: currentDate,\r\n inMonth: currentDate.getMonth().toString() === monthIndex.toString(),\r\n isToday: currentDate.toDateString() === this.today.toDateString()\r\n };\r\n });\r\n displayWeeks.push(weekDays);\r\n }\r\n return displayWeeks;\r\n }\r\n\r\n private goToDate(date: Date, scrollBehavior?) {\r\n const indexObj = this.getCurrentIndex(date);\r\n const scroll = scrollBehavior || (Math.abs(indexObj.bodyIndex - this.currentBodyIndex) > 18 ? 'auto' : 'smooth');\r\n this.currentBodyIndex = indexObj.bodyIndex;\r\n this.scrollBodyCmp.scrollToIndex(indexObj.bodyIndex, scroll);\r\n this.goToListByIndex(indexObj.listIndex);\r\n this.cdr.detectChanges();\r\n }\r\n\r\n private goToListByIndex(index) {\r\n const indexDelta = Math.abs(this.scrollListCmp.measureScrollOffset() / 30 - index);\r\n this.scrollListCmp.scrollToIndex(index - 4, indexDelta < 12 ? 'smooth' : 'auto');\r\n this.updateListActive(index);\r\n }\r\n\r\n selectMonth(year: number, month: number) {\r\n const date = new Date(year, month, 1);\r\n const curYear = this.yearAndMonthList.find(t => t.active)?.year || this.curDate.getFullYear();\r\n // 太远的虚拟滚动会导致白屏,所以超过两年的滚动都直接跳转\r\n const isSmoothAnimation = Math.abs(curYear - year) < 2;\r\n\r\n if (this.isListCollopse) {\r\n this.toggleListCollopse(date);\r\n } else {\r\n this.goToDate(date, isSmoothAnimation ? 'smooth' : 'auto');\r\n }\r\n }\r\n\r\n updateRangeDate(dateList: Date[]) {\r\n if (!dateList) {\r\n this.selectedRangeDate = [];\r\n this.cdr.detectChanges();\r\n return;\r\n }\r\n\r\n const curDate = (this.pickerSrv.currentActiveInput === 'start' ?\r\n (dateList[0] || dateList[1]) : (dateList[1] || dateList[0])) || new Date();\r\n const moreThanOneYear = Math.abs(curDate.getFullYear() - (this.currentBodyIndex / 12 + this.pickerSrv.calendarRange[0])) > 1;\r\n this.selectedRangeDate = dateList;\r\n this.goToDate(curDate, moreThanOneYear ? 'auto' : 'smooth');\r\n this.cdr.detectChanges();\r\n }\r\n\r\n updateSingleDate(date: Date) {\r\n if (!date) {\r\n this.curDate = null;\r\n this.cdr.detectChanges();\r\n return;\r\n }\r\n const moreThanOneYear = Math.abs(date.getFullYear() - (this.currentBodyIndex / 12 + this.pickerSrv.calendarRange[0])) > 1;\r\n this.curDate = date;\r\n this.goToDate(this.curDate, moreThanOneYear ? 'auto' : 'smooth');\r\n this.cdr.detectChanges();\r\n }\r\n\r\n updateListActive(index: number) {\r\n const curActive = this.yearAndMonthList.find(t => t.active);\r\n if (curActive) {\r\n curActive.active = false;\r\n }\r\n this.yearAndMonthList[index].active = true;\r\n }\r\n\r\n getCurrentIndex(curDate: Date) {\r\n const year = curDate.getFullYear();\r\n const month = curDate.getMonth();\r\n const listIndex = this.isListCollopse ?\r\n (year - this.pickerSrv.calendarRange[0]) : (year - this.pickerSrv.calendarRange[0]) * 13 + month + 1;\r\n return {\r\n bodyIndex: (year - this.pickerSrv.calendarRange[0]) * 12 + month,\r\n listIndex\r\n };\r\n }\r\n\r\n selectDate(day: DevuiCalendarDateItem) {\r\n if (this.isDisabled(day.date) || !day.inMonth) {\r\n return;\r\n }\r\n\r\n if (this.isWeekSelect) {\r\n this.pickerSrv.curRangeDate = this.getWeekRange(day.date);\r\n this.pickerSrv.currentActiveInput = 'end';\r\n } else {\r\n this.curDate = new Date(day.date.setHours(this.pickerSrv.curHour, this.pickerSrv.curMin, this.pickerSrv.curSec));\r\n }\r\n if (this.isRangeType) {\r\n this.pickerSrv.fixRangeDate();\r\n }\r\n this.cdr.detectChanges();\r\n // 非时间模式下选完开始日期跳转到结束日期\r\n if (this.isRangeType && !this.pickerSrv.showTime) {\r\n if (this.pickerSrv.currentActiveInput === 'start') {\r\n this.pickerSrv.currentActiveInput = 'end';\r\n } else if (this.pickerSrv.currentActiveInput === 'end' && !this.selectedRangeDate[0]) {\r\n this.selectedRangeDate[0] = this.curDate;\r\n this.pickerSrv.currentActiveInput = 'start';\r\n } else {\r\n this.pickerSrv.closeDropdownEvent.next(false);\r\n }\r\n }\r\n this.pickerSrv.selectedDateChange.next({\r\n type: this.isRangeType ? 'range' : 'single',\r\n value: this.isRangeType ? this.selectedRangeDate : this.curDate\r\n });\r\n\r\n if (this.isRangeType && this.pickerSrv.showTime) {\r\n this.pickerSrv.updateTimeChange.next({\r\n activeInput: this.pickerSrv.currentActiveInput,\r\n hour: this.pickerSrv.curHour,\r\n min: this.pickerSrv.curMin,\r\n seconds: this.pickerSrv.curSec\r\n });\r\n }\r\n\r\n if (this.pickerSrv.closeAfterSelected) {\r\n this.pickerSrv.closeDropdownEvent.next(false);\r\n }\r\n }\r\n\r\n isStartDate(date: Date): boolean {\r\n return this.pickerSrv.isStartDate(date);\r\n }\r\n\r\n isDisabled(date: Date): boolean {\r\n return !this.pickerSrv.dateInRange(date);\r\n }\r\n\r\n isEndDate(date: Date): boolean {\r\n return this.pickerSrv.isEndDate(date);\r\n }\r\n\r\n isDateInRange(date: Date): boolean {\r\n if (this.isWeekSelect) {\r\n return this.isInWeekHoverRange(date);\r\n } else {\r\n return this.pickerSrv.isDateInRange(date);\r\n }\r\n }\r\n\r\n isDateInSelectRange(date: Date): boolean {\r\n return this.pickerSrv.isDateInSelectRange(date);\r\n }\r\n\r\n isDateActive(date: Date): boolean {\r\n return this.pickerSrv.isDateActive(date);\r\n }\r\n\r\n isActiveTypeDate(date: Date): boolean {\r\n return this.pickerSrv.isActiveInputTypeDate(date);\r\n }\r\n\r\n isDateAbandon(date: Date): boolean {\r\n return this.pickerSrv.isDateAbandon(date);\r\n }\r\n\r\n isDateSuggest(date: Date): boolean {\r\n return this.pickerSrv.isInSuggestList(date);\r\n }\r\n\r\n isDateMarked(date: Date): boolean {\r\n return this.pickerSrv.isMarkedDate(date);\r\n }\r\n\r\n isSingleDate(): boolean {\r\n if (this.pickerSrv.currentActiveInput === 'start') {\r\n return !this.pickerSrv.curRangeDate[1];\r\n } else if (this.pickerSrv.currentActiveInput === 'end') {\r\n return !this.pickerSrv.curRangeDate[0];\r\n }\r\n }\r\n\r\n setHoverTarget(date: Date, isInMonth: boolean) {\r\n if (!isInMonth) {\r\n this.curHoverDate = null;\r\n return;\r\n }\r\n this.curHoverDate = date;\r\n if (this.isWeekSelect) {\r\n this.weekHoverRange = this.getWeekRange(date);\r\n this.curWeekHoverDate = date;\r\n this.cdr.markForCheck();\r\n return;\r\n }\r\n }\r\n\r\n getWeekRange(date: Date) {\r\n if (!date) {\r\n return [];\r\n }\r\n const diff = date.getDay() < this.pickerSrv.startIndexOfWeek\r\n ? 7 - (this.pickerSrv.startIndexOfWeek - date.getDay())\r\n : date.getDay() - this.pickerSrv.startIndexOfWeek;\r\n const weekStart = new Date(date.getTime() - diff * DAY_DURATION);\r\n const weekEnd = new Date(weekStart.getTime() + DAY_DURATION * 6);\r\n weekEnd.setHours(23, 59, 59);\r\n\r\n return [weekStart, weekEnd];\r\n }\r\n\r\n isInWeekHoverRange(date: Date) {\r\n const range = this.getWeekRange(this.curWeekHoverDate);\r\n const time = date.getTime();\r\n const timeStr = date.toDateString();\r\n if (this.pickerSrv.isDateActive(date)) {\r\n return false;\r\n }\r\n return ((range[0]?.getTime() < time && time < range[1]?.getTime()) ||\r\n (range[0]?.toDateString() === timeStr || timeStr === range[1]?.toDateString()));\r\n }\r\n\r\n toggleListCollopse(toDate?: Date) {\r\n const activeItem = this.yearAndMonthList.find(t => t.active);\r\n const curYear = activeItem?.year;\r\n const curMonth = activeItem?.month;\r\n this.isListCollopse = !this.isListCollopse;\r\n if (this.isListCollopse) {\r\n this.yearAndMonthList = this.yearAndMonthList.filter(t => !t.isMonth);\r\n } else {\r\n this.initDataList();\r\n }\r\n setTimeout(() => {\r\n this.goToDate(toDate || new Date(curYear, curMonth || 0, 1), 'auto');\r\n });\r\n }\r\n\r\n protected fillLeft(num: number) {\r\n return num < 10 ? `0${num}` : `${num}`;\r\n }\r\n\r\n ngOnDestroy() {\r\n this.unsubscribe$.next();\r\n this.unsubscribe$.complete();\r\n }\r\n\r\n}\r\n","<div class=\"devui-canlender-panel\">\n <cdk-virtual-scroll-viewport #scrollList [itemSize]=\"30\" class=\"devui-canlender-panel-year-list\" minBufferPx=\"400\" maxBufferPx=\"600\">\n <div\n class=\"devui-canlender-panel-year-list-item\"\n [ngClass]=\"{ 'title-active': item.active }\"\n *cdkVirtualFor=\"let item of yearAndMonthList; let index = index\"\n >\n <p class=\"devui-year-title\" *ngIf=\"!item.isMonth && !isListCollopse\" (click)=\"toggleListCollopse()\">{{ item.year }}</p>\n <p class=\"devui-year-title\" *ngIf=\"!item.isMonth && isListCollopse\" (click)=\"selectMonth(item.year, 0)\">{{ item.year }}</p>\n <p class=\"devui-month-title\" *ngIf=\"item.isMonth\" (click)=\"selectMonth(item.year, item.month)\">\n {{ i18nText.monthsOfYear[item.month] }}\n </p>\n </div>\n </cdk-virtual-scroll-viewport>\n\n <div class=\"devui-canlender-panel-main\">\n <table class=\"devui-table\">\n <thead>\n <tr class=\"small text-center devui-week-header\">\n <td *ngFor=\"let item of i18nText?.daysOfWeek\">{{ item }}</td>\n </tr>\n </thead>\n <tbody>\n <tr>\n <td colspan=\"7\">\n <cdk-virtual-scroll-viewport\n #scrollBody\n [itemSize]=\"calendarItemSize\"\n class=\"devui-tbody-wrapper\"\n minBufferPx=\"400\"\n maxBufferPx=\"600\"\n >\n <div *cdkVirtualFor=\"let month of allMonthList; let index = index\">\n <p class=\"devui-table-month-title\">{{ i18nText?.getYearMonthStr(month.year, month.month + 1) }}</p>\n <table\n class=\"devui-month-table\"\n [ngClass]=\"{\n 'devui-single-date': isSingleDate()\n }\"\n (mouseleave)=\"curHoverDate = null; curWeekHoverDate = null\"\n >\n <tbody>\n <tr class=\"small text-center devui-week-header\" *ngFor=\"let week of month.displayWeeks\">\n <td\n [ngClass]=\"{\n 'devui-table-date': day.inMonth,\n 'devui-table-date-selected': isDateActive(day.date),\n 'devui-table-date-abandon-selected': isDateAbandon(day.date),\n 'devui-table-date-today': day.isToday,\n 'devui-table-date-disable': isDisabled(day.date),\n 'devui-table-date-inrange': isDateInRange(day.date),\n 'devui-table-date-start': isStartDate(day.date),\n 'devui-table-date-end': isEndDate(day.date),\n 'devui-table-date-in-selected-range': isDateInSelectRange(day.date),\n 'devui-table-date-active-type': isActiveTypeDate(day.date),\n 'devui-table-date-suggested': isDateSuggest(day.date),\n 'devui-table-date-marked': isDateMarked(day.date)\n }\"\n *ngFor=\"let day of week\"\n dPopover\n [content]=\"markDate\"\n [visible]=\"isDateMarked(day.date) && curHoverDate === day.date && !!markDateTemplate\"\n (click)=\"selectDate(day)\"\n (mouseenter)=\"setHoverTarget(day.date, day.inMonth)\"\n >\n <span>{{ day.inMonth ? day.day : '' }}</span>\n <ng-template #markDate>\n <ng-template [ngTemplateOutlet]=\"markDateTemplate\" [ngTemplateOutletContext]=\"{ date: day.date }\"></ng-template>\n </ng-template>\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n </cdk-virtual-scroll-viewport>\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n</div>\n","import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';\nimport {\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n Input,\n OnDestroy,\n OnInit,\n ViewChild\n} from '@angular/core';\nimport { I18nInterface, I18nService } from 'ng-devui/i18n';\nimport { fromEvent, Subject, Subscription } from 'rxjs';\nimport { debounceTime, takeUntil } from 'rxjs/operators';\nimport { DatepickerProService } from './../datepicker-pro.service';\n\n@Component({\n selector: 'd-month-panel',\n templateUrl: './month-panel.component.html',\n styleUrls: ['./month-panel.component.scss'],\n preserveWhitespaces: false,\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class MonthPanelComponent implements OnInit, OnDestroy {\n @ViewChild('scrollBody') scrollBodyCmp: CdkVirtualScrollViewport;\n @ViewChild('scrollList') scrollListCmp: CdkVirtualScrollViewport;\n @Input() isRangeType = false;\n\n unsubscribe$ = new Subject<void>();\n scrollListener: Subscription;\n i18nText: I18nInterface['datePickerPro'];\n i18nSubscription: Subscription;\n\n yearList = [];\n calenderItemSize = 186;\n currentBodyIndex;\n monthList = [\n [1, 2, 3],\n [4, 5, 6],\n [7, 8, 9],\n [10, 11, 12]\n ];\n thisMonth: Date;\n\n get curHoverDate() {\n return this.pickerSrv.curHoverDate;\n }\n\n set curHoverDate(value: Date) {\n this.pickerSrv.curHoverDate = value;\n }\n\n get selectedRangeDate(): Date[] {\n return this.pickerSrv.curRangeDate;\n }\n\n set selectedRangeDate(dateList: Date[]) {\n this.pickerSrv.curRangeDate = dateList;\n }\n\n get currentDate(): Date {\n if (this.isRangeType) {\n if (this.pickerSrv.currentActiveInput === 'start') {\n return this.pickerSrv.curRangeDate[0];\n } else if (this.pickerSrv.currentActiveInput === 'end') {\n return this.pickerSrv.curRangeDate[1];\n }\n } else {\n return this.pickerSrv.curDate;\n }\n }\n\n set currentDate(value: Date) {\n if (this.isRangeType) {\n if (this.pickerSrv.currentActiveInput === 'start') {\n this.pickerSrv.curRangeDate[0] = value;\n } else if (this.pickerSrv.currentActiveInput === 'end') {\n this.pickerSrv.curRangeDate[1] = value;\n }\n } else {\n this.pickerSrv.curDate = value;\n }\n }\n\n constructor(\n protected i18n: I18nService,\n private pickerSrv: DatepickerProService,\n private cdr: ChangeDetectorRef\n ) {}\n\n ngOnInit() {\n this.thisMonth = new Date();\n this.initList();\n this.initObservable();\n this.setI18nText();\n }\n\n setI18nText() {\n this.i18nText = this.i18n.getI18nText().datePickerPro;\n this.i18nSubscription = this.i18n.langChange().subscribe((data) => {\n this.i18nText = data.datePickerPro;\n this.cdr.detectChanges();\n });\n }\n\n initObservable() {\n this.pickerSrv.toggleEvent.pipe(\n takeUntil(this.unsubscribe$),\n ).subscribe(isOpen => {\n if (isOpen) {\n setTimeout(() => {\n this.goToDate(this.currentDate || new Date());\n });\n }\n if (!this.scrollListener) {\n this.initScrollListener();\n }\n });\n\n this.pickerSrv.updateDateValue.pipe(\n takeUntil(this.unsubscribe$),\n ).subscribe(res => {\n if (res.type === 'range') {\n this.pickerSrv.curRangeDate = res.value as Date[];\n } else {\n this.pickerSrv.curDate = res.value as Date;\n }\n this.goToDate(this.currentDate || new Date());\n this.cdr.detectChanges();\n });\n\n this.pickerSrv.activeInputChange.pipe(\n takeUntil(this.unsubscribe$)\n ).subscribe(type => {\n if (type === 'start') {\n this.goToDate(this.selectedRangeDate[0] || this.selectedRangeDate[1] || new Date());\n } else {\n this.goToDate(this.selectedRangeDate[1] || this.selectedRangeDate[0] || new D