UNPKG

ng-devui

Version:

DevUI components based on Angular

996 lines (992 loc) 217 kB
import * as i0 from '@angular/core'; import { Injectable, Inject, Component, ChangeDetectionStrategy, Input, ViewChild, HostListener, EventEmitter, forwardRef, Output, ContentChild, HostBinding, NgModule } from '@angular/core'; import * as i4 from '@angular/common'; import { DOCUMENT, CommonModule } from '@angular/common'; import * as i1 from 'ng-devui/i18n'; import { I18nFormat, EN_US } from 'ng-devui/i18n'; import { Subject, fromEvent } from 'rxjs'; import { takeUntil, filter, debounceTime, map } from 'rxjs/operators'; import * as i4$1 from 'ng-devui/button'; import { ButtonModule } from 'ng-devui/button'; import * as i3 from '@angular/cdk/scrolling'; import { ScrollingModule } from '@angular/cdk/scrolling'; import * as i5 from 'ng-devui/popover'; import { PopoverModule } from 'ng-devui/popover'; import { chunk } from 'lodash-es'; import * as i5$1 from '@angular/forms'; import { NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms'; import * as i3$1 from 'ng-devui/utils'; import { DefaultDateConverter, WithConfig } from 'ng-devui/utils'; import { __decorate, __metadata } from 'tslib'; import * as i6 from 'ng-devui/dropdown'; import { DropDownModule } from 'ng-devui/dropdown'; class DatepickerProService { set minDate(value) { this._minDate = new Date(value) || new Date(this.calendarRange[0], 0, 1); this.detectedChanges.next(); } get minDate() { return this._minDate; } set maxDate(value) { this._maxDate = new Date(value) || new Date(this.calendarRange[1], 11, 31, 23, 59, 59); this.detectedChanges.next(); } get maxDate() { return this._maxDate; } get closeAfterSelected() { return !this.isRange && !this.showTime; } get curHour() { if (this.isRange) { return (this.currentActiveInput === 'start' ? this.curRangeDate[0]?.getHours() : this.curRangeDate[1]?.getHours()) || 0; } else { return this.curDate?.getHours() || 0; } } get curMin() { if (this.isRange) { return (this.currentActiveInput === 'start' ? this.curRangeDate[0]?.getMinutes() : this.curRangeDate[1]?.getMinutes()) || 0; } else { return this.curDate?.getMinutes() || 0; } } get curSec() { if (this.isRange) { return (this.currentActiveInput === 'start' ? this.curRangeDate[0]?.getSeconds() : this.curRangeDate[1]?.getSeconds()) || 0; } else { return this.curDate?.getSeconds() || 0; } } constructor(doc) { this.doc = doc; this.curRangeDate = []; this.calendarRange = [1970, 2099]; this.currentActiveInput = 'start'; this._minDate = new Date(this.calendarRange[0], 0, 1); this._maxDate = new Date(this.calendarRange[1], 11, 31, 23, 59, 59); this.toggleEvent = new Subject(); this.closeDropdownEvent = new Subject(); this.activeInputChange = new Subject(); this.selectedDateChange = new Subject(); this.updateDateValue = new Subject(); this.selectedTimeChange = new Subject(); this.updateTimeChange = new Subject(); this.detectedChanges = new Subject(); this.document = this.doc; } dateInRange(date) { if (!date) { return true; } return (date.getTime() > this.minDate.getTime() && date.getTime() < this.maxDate.getTime()) || date.toDateString() === this.minDate.toDateString() || date.toDateString() === this.maxDate.toDateString(); } resetMin() { this.minDate = new Date(this.calendarRange[0], 0, 1); } resetMax() { this.maxDate = new Date(this.calendarRange[1], 11, 31); } // 对范围模式下一些非法的选择进行修正 fixRangeDate() { const start = this.curRangeDate[0]?.getTime(); const end = this.curRangeDate[1]?.getTime(); if (start && end && end < start) { if (this.currentActiveInput === 'start') { this.curRangeDate[1] = null; } else if (this.currentActiveInput === 'end') { this.curRangeDate[0] = null; } } } // 判断日期是否为起始日期 isStartDate(date) { if (!this.isRange) { return false; } if (this.currentActiveInput === 'start') { return date.toDateString() === this.curHoverDate?.toDateString() || date.toDateString() === this.curRangeDate[0]?.toDateString(); } return date.toDateString() === this.curRangeDate[0]?.toDateString(); } // 判断日期是否为结束日期 isEndDate(date) { if (!this.isRange) { return false; } if (this.currentActiveInput === 'end') { return date.toDateString() === this.curHoverDate?.toDateString() || date.toDateString() === this.curRangeDate[1]?.toDateString(); } return date.toDateString() === this.curRangeDate[1]?.toDateString(); } // 判断日期是否在hover范围或者选中的范围内 isDateInRange(date) { const dateTime = date.getTime(); const dateStr = date.toDateString(); if (this.isRange) { if (this.currentActiveInput === 'start') { return (this.curHoverDate || this.curRangeDate[0])?.getTime() < dateTime && (this.curHoverDate || this.curRangeDate[0])?.toDateString() !== dateStr && this.curRangeDate[1]?.getTime() > dateTime && this.curRangeDate[1]?.toDateString() !== dateStr; } else { return this.curRangeDate[0]?.getTime() < dateTime && this.curRangeDate[0]?.toDateString() !== dateStr && (this.curHoverDate || this.curRangeDate[1])?.getTime() > dateTime && (this.curHoverDate || this.curRangeDate[1]).toDateString() !== dateStr; } } else { return false; } } // 判断日期是否在已选中的范围内,与hover做区分 isDateInSelectRange(date) { if (!this.isRange) { return false; } if (!this.curRangeDate[0] || !this.curRangeDate[1]) { return false; } return this.curRangeDate[0].getTime() < date.getTime() && this.curRangeDate[1].getTime() > date.getTime() && this.curRangeDate[1].toDateString() !== date.toDateString() && this.curRangeDate[0].toDateString() !== date.toDateString(); } isDateActive(date) { const dateStr = date.toDateString(); if (this.isRange) { return dateStr === this.curRangeDate[0]?.toDateString() || dateStr === this.curRangeDate[1]?.toDateString(); } else { return dateStr === this.curDate?.toDateString(); } } isMonthActive(yearIndex, monthIndex) { if (this.isRange) { return (yearIndex === this.curRangeDate[0]?.getFullYear() && monthIndex === this.curRangeDate[0]?.getMonth()) || (yearIndex === this.curRangeDate[1]?.getFullYear() && monthIndex === this.curRangeDate[1]?.getMonth()); } else { return yearIndex === this.curDate?.getFullYear() && monthIndex === this.curDate?.getMonth(); } } isYearActive(yearIndex) { if (this.isRange) { return yearIndex === this.curRangeDate[0]?.getFullYear() || yearIndex === this.curRangeDate[1]?.getFullYear(); } else { return yearIndex === this.curDate?.getFullYear(); } } // 是否为范围选中日期中对应的input激活项 isActiveInputTypeDate(date) { if (!this.isRange) { return false; } if (this.currentActiveInput === 'start') { return date.toDateString() === this.curRangeDate[0]?.toDateString(); } else { return date.toDateString() === this.curRangeDate[1]?.toDateString(); } } // 是否为选中日期且在废弃范围逻辑内 isDateAbandon(date) { if (!this.isRange || (!this.curRangeDate[0] || !this.curRangeDate[1])) { return false; } if (!this.isDateActive(date)) { return false; } if (this.currentActiveInput === 'start') { return this.curHoverDate?.getTime() > date.getTime(); } else { return this.curHoverDate?.getTime() < date.getTime(); } } isInSuggestList(date) { if (!this.markedRangeDateList) { return false; } for (let index = 0; index < this.markedRangeDateList.length; index++) { const range = this.markedRangeDateList[index]; if (range[0]?.getTime() < date.getTime() && range[1]?.getTime() > date.getTime()) { return true; } if (range[0]?.toDateString() === date.toDateString() || range[1]?.toDateString() === date.toDateString()) { return true; } } return false; } isMarkedDate(date) { for (let index = 0; index < this.markedDateList?.length; index++) { if (this.markedDateList[index]?.toDateString() === date.toDateString()) { return true; } } return false; } mearsureStrWidth(str) { const mearsureDom = this.document.createElement('span'); mearsureDom.innerText = str; mearsureDom.style.visibility = 'hidden'; this.document.body.appendChild(mearsureDom); const domWidth = mearsureDom.offsetWidth; this.document.body.removeChild(mearsureDom); return domWidth; } ngOnDestroy() { this.toggleEvent.complete(); this.selectedDateChange.complete(); this.closeDropdownEvent.complete(); this.updateDateValue.complete(); this.updateTimeChange.complete(); this.selectedTimeChange.complete(); this.activeInputChange.complete(); this.detectedChanges.complete(); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DatepickerProService, deps: [{ token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DatepickerProService }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DatepickerProService, decorators: [{ type: Injectable }], ctorParameters: () => [{ type: undefined, decorators: [{ type: Inject, args: [DOCUMENT] }] }] }); class DatepickerProCommonDataService { constructor() { this.calendarDataCache = {}; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DatepickerProCommonDataService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DatepickerProCommonDataService }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DatepickerProCommonDataService, decorators: [{ type: Injectable }] }); class TimepickerPanelComponent { constructor(el, pickSrv, i18n, cdr) { this.el = el; this.pickSrv = pickSrv; this.i18n = i18n; this.cdr = cdr; this.firstList = []; this.secondList = []; this.thirdList = []; this.hourIndex = 0; this.minIndex = 0; this.secIndex = 0; this.typeList = ['hour', 'min', 'sec']; this.unsubscribe$ = new Subject(); } ngOnInit() { this.initDateList(); this.initObservable(); this.setI18nText(); } setI18nText() { this.i18nText = this.i18n.getI18nText().datePickerPro; this.i18nSubscription = this.i18n.langChange().subscribe((data) => { this.i18nText = data.datePickerPro; }); } initObservable() { this.pickSrv.toggleEvent.pipe(takeUntil(this.unsubscribe$)).subscribe((isOpen) => { if (isOpen) { setTimeout(() => { this.initDateList(true); this.cdr.detectChanges(); }); } }); this.pickSrv.updateTimeChange.pipe(takeUntil(this.unsubscribe$)).subscribe((value) => { if (this.hourIndex !== value.hour) { this.chooseTime('hour', value.hour); } if (this.minIndex !== value.min) { this.chooseTime('min', value.min); } if (this.secIndex !== value.seconds) { this.chooseTime('sec', value.seconds); } this.cdr.detectChanges(); }); if (this.pickSrv.isRange) { this.pickSrv.activeInputChange.pipe(takeUntil(this.unsubscribe$)).subscribe((type) => { const isNull = (type === 'start' && !this.pickSrv.curRangeDate[0]) || (type === 'end' && !this.pickSrv.curRangeDate[1]); if (this.hourIndex !== this.pickSrv.curHour || isNull) { this.chooseTime('hour', isNull ? null : this.pickSrv.curHour); } if (this.minIndex !== this.pickSrv.curMin || isNull) { this.chooseTime('min', isNull ? null : this.pickSrv.curMin); } if (this.secIndex !== this.pickSrv.curSec || isNull) { this.chooseTime('sec', isNull ? null : this.pickSrv.curSec); } }); } this.pickSrv.selectedDateChange.pipe(takeUntil(this.unsubscribe$)).subscribe(() => { const isFixTime = this.fixTime(); this.cdr.detectChanges(); if (isFixTime) { setTimeout(() => { this.pickSrv.selectedTimeChange.next({ activeInput: this.pickSrv.currentActiveInput, hour: this.hourIndex, min: this.minIndex, seconds: this.secIndex, }); }); } }); } initDateList(justScroll = false) { if (this.pickSrv.curDate) { this.hourIndex = this.pickSrv.curDate.getHours(); this.minIndex = this.pickSrv.curDate.getMinutes(); this.secIndex = this.pickSrv.curDate.getSeconds(); } this.typeList.forEach((type) => { this.chooseTime(type, this[`${type}Index`], false, justScroll); }); } chooseTime(type, index, handle = false, justScroll = false) { if (this.itemIsDisabled(type, index)) { return; } let fixed; switch (type) { case 'hour': this.hourIndex = index; fixed = this.fixTime(justScroll); this.firstList = new Array(24).fill(1).map((t, i) => { return { time: i < 10 ? `0${i}` : String(i), type: 'hour', active: this.hourIndex === i, }; }); if (!fixed) { this.setScroll('first', this.hourIndex, justScroll); } break; case 'min': this.minIndex = index; this.fixTime(justScroll); this.secondList = new Array(60).fill(1).map((t, i) => { return { time: i < 10 ? `0${i}` : String(i), type: 'min', active: this.minIndex === i, }; }); if (!fixed) { this.setScroll('second', this.minIndex, justScroll); } break; case 'sec': this.secIndex = index; this.thirdList = new Array(60).fill(1).map((t, i) => { return { time: i < 10 ? `0${i}` : String(i), type: 'sec', active: this.secIndex === i, }; }); this.setScroll('third', this.secIndex, justScroll); break; default: } this.cdr.detectChanges(); if (handle) { this.pickSrv.selectedTimeChange.next({ activeInput: this.pickSrv.currentActiveInput, hour: this.hourIndex, min: this.minIndex, seconds: this.secIndex, }); } } fixTime(justScroll = false) { let curTime; if (this.pickSrv.isRange) { const target = this.pickSrv.curRangeDate[this.pickSrv.currentActiveInput === 'start' ? 0 : 1]; if (!target) { return; } curTime = new Date(target); } else { if (!this.pickSrv.curDate) { return; } curTime = new Date(this.pickSrv.curDate); } curTime.setHours(this.hourIndex); curTime.setMinutes(this.minIndex); curTime.setSeconds(this.secIndex); let isFixTime = false; if (curTime.getTime() < this.pickSrv.minDate.getTime()) { this.pickSrv.curDate = this.pickSrv.minDate; isFixTime = true; } if (curTime.getTime() > this.pickSrv.maxDate.getTime()) { this.pickSrv.curDate = this.pickSrv.maxDate; isFixTime = true; } if (isFixTime) { this.initDateList(justScroll); } return isFixTime; } itemIsDisabled(type, index) { let curTime; if (this.pickSrv.isRange) { const target = this.pickSrv.curRangeDate[this.pickSrv.currentActiveInput === 'start' ? 0 : 1]; if (!target) { return false; } curTime = new Date(target); } else { if (!this.pickSrv.curDate) { return false; } curTime = new Date(this.pickSrv.curDate); } if (!curTime) { return false; } let flag = false; let time; let isTimeBoundary = false; const maxTime = this.pickSrv.maxDate.getTime(); const minTime = this.pickSrv.minDate.getTime(); switch (type) { case 'hour': curTime.setHours(index); curTime.setMinutes(this.minIndex); time = curTime.setSeconds(this.secIndex); isTimeBoundary = time > maxTime ? this.pickSrv.maxDate.getHours() === index : this.pickSrv.minDate.getHours() === index; break; case 'min': curTime.setHours(this.hourIndex); curTime.setMinutes(index); time = curTime.setSeconds(this.secIndex); isTimeBoundary = time > maxTime ? this.pickSrv.maxDate.getMinutes() === index : this.pickSrv.minDate.getMinutes() === index; break; case 'sec': curTime.setHours(this.hourIndex); curTime.setMinutes(this.minIndex); time = curTime.setSeconds(index); break; default: } flag = time > maxTime || time < minTime; return flag && !isTimeBoundary; } setAllScroll(first, second, third, justScroll) { this.setScroll('first', first, justScroll); this.setScroll('second', second, justScroll); this.setScroll('third', third, justScroll); } setScroll(whichList, index, justScroll) { const scroll = (22 + 8) * index; const duration = justScroll ? 0 : 100; if (this.el) { this.scrollTo(this.el.nativeElement.querySelector(`.devui-${whichList}-list`), scroll, duration); } } scrollTo(element, to, duration) { if (typeof window === undefined || !element) { return; } if (duration <= 0) { element.scrollTop = to; return; } const difference = to - element.scrollTop; const perTick = (difference / duration) * 10; const reqAnimFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.msRequestAnimationFrame || window.oRequestAnimationFrame; reqAnimFrame(() => { element.scrollTop = element.scrollTop + perTick; if (element.scrollTop === to) { return; } this.scrollTo(element, to, duration - 10); }); } ngOnDestroy() { this.unsubscribe$.next(); this.unsubscribe$.complete(); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TimepickerPanelComponent, deps: [{ token: i0.ElementRef }, { token: DatepickerProService }, { token: i1.I18nService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: TimepickerPanelComponent, selector: "d-timepicker-panel", ngImport: i0, template: "<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", styles: [":host{display:inline-block;vertical-align:top;font-size:12px}.devui-timepicker-panel{width:115px;height:306px;text-align:center;border-left:1px solid var(--devui-dividing-line, #f0f0f0)}.devui-timepicker-panel .devui-time-header{display:flex;height:30px;padding:0 12px}.devui-timepicker-panel .devui-time-header-item{flex:1;line-height:30px;margin-right:12px}.devui-timepicker-panel .devui-time-header-item:last-child{margin-right:0}.devui-timepicker-panel .devui-time-picker{display:flex;height:276px;width:114px;padding:0 12px;overflow:hidden;border-radius:var(--devui-border-radius, 2px) var(--devui-border-radius, 2px) 0 0}.devui-timepicker-panel .devui-time-picker .devui-time-list{flex:1;overflow:hidden;scroll-behavior:auto;padding-right:6px;scrollbar-gutter:stable}.devui-timepicker-panel .devui-time-picker .devui-time-list:last-child{padding-right:0}.devui-timepicker-panel .devui-time-picker .devui-time-list::-webkit-scrollbar{width:4px}.devui-timepicker-panel .devui-time-picker .devui-time-list:hover{overflow-y:scroll}.devui-timepicker-panel .devui-time-picker .devui-time-list .devui-time-item{width:100%;height:22px;line-height:22px;text-align:center;cursor:pointer}.devui-timepicker-panel .devui-time-picker .devui-time-list .devui-time-item:not(:last-child){margin-bottom:8px}.devui-timepicker-panel .devui-time-picker .devui-time-list .devui-time-item:last-child{margin-bottom:254px}.devui-timepicker-panel .devui-time-picker .devui-time-list .devui-time-item.disabled{cursor:not-allowed}.devui-timepicker-panel .devui-time-picker:not(.devui-disabled) .devui-time-list .devui-time-item:hover{color:var(--devui-list-item-hover-text, #000000);background-color:var(--devui-list-item-hover-bg, #f2f2f3);border-radius:var(--devui-border-radius-feedback, 4px)}.devui-timepicker-panel .devui-time-picker:not(.devui-disabled) .devui-time-list .devui-time-item.active{color:var(--devui-list-item-active-text, #000000);background-color:var(--devui-list-item-active-bg, #cedefd);border-radius:var(--devui-border-radius-feedback, 4px)}.devui-timepicker-panel .devui-time-picker:not(.devui-disabled) .devui-time-list .devui-time-item.disabled{color:var(--devui-disabled-text, #c2c2c2);background-color:var(--devui-disabled-bg, #f3f3f3);border-radius:var(--devui-border-radius-feedback, 4px)}\n"], dependencies: [{ kind: "directive", type: i4.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TimepickerPanelComponent, decorators: [{ type: Component, args: [{ selector: 'd-timepicker-panel', preserveWhitespaces: false, changeDetection: ChangeDetectionStrategy.OnPush, template: "<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", styles: [":host{display:inline-block;vertical-align:top;font-size:12px}.devui-timepicker-panel{width:115px;height:306px;text-align:center;border-left:1px solid var(--devui-dividing-line, #f0f0f0)}.devui-timepicker-panel .devui-time-header{display:flex;height:30px;padding:0 12px}.devui-timepicker-panel .devui-time-header-item{flex:1;line-height:30px;margin-right:12px}.devui-timepicker-panel .devui-time-header-item:last-child{margin-right:0}.devui-timepicker-panel .devui-time-picker{display:flex;height:276px;width:114px;padding:0 12px;overflow:hidden;border-radius:var(--devui-border-radius, 2px) var(--devui-border-radius, 2px) 0 0}.devui-timepicker-panel .devui-time-picker .devui-time-list{flex:1;overflow:hidden;scroll-behavior:auto;padding-right:6px;scrollbar-gutter:stable}.devui-timepicker-panel .devui-time-picker .devui-time-list:last-child{padding-right:0}.devui-timepicker-panel .devui-time-picker .devui-time-list::-webkit-scrollbar{width:4px}.devui-timepicker-panel .devui-time-picker .devui-time-list:hover{overflow-y:scroll}.devui-timepicker-panel .devui-time-picker .devui-time-list .devui-time-item{width:100%;height:22px;line-height:22px;text-align:center;cursor:pointer}.devui-timepicker-panel .devui-time-picker .devui-time-list .devui-time-item:not(:last-child){margin-bottom:8px}.devui-timepicker-panel .devui-time-picker .devui-time-list .devui-time-item:last-child{margin-bottom:254px}.devui-timepicker-panel .devui-time-picker .devui-time-list .devui-time-item.disabled{cursor:not-allowed}.devui-timepicker-panel .devui-time-picker:not(.devui-disabled) .devui-time-list .devui-time-item:hover{color:var(--devui-list-item-hover-text, #000000);background-color:var(--devui-list-item-hover-bg, #f2f2f3);border-radius:var(--devui-border-radius-feedback, 4px)}.devui-timepicker-panel .devui-time-picker:not(.devui-disabled) .devui-time-list .devui-time-item.active{color:var(--devui-list-item-active-text, #000000);background-color:var(--devui-list-item-active-bg, #cedefd);border-radius:var(--devui-border-radius-feedback, 4px)}.devui-timepicker-panel .devui-time-picker:not(.devui-disabled) .devui-time-list .devui-time-item.disabled{color:var(--devui-disabled-text, #c2c2c2);background-color:var(--devui-disabled-bg, #f3f3f3);border-radius:var(--devui-border-radius-feedback, 4px)}\n"] }] }], ctorParameters: () => [{ type: i0.ElementRef }, { type: DatepickerProService }, { type: i1.I18nService }, { type: i0.ChangeDetectorRef }] }); class FooterPanelComponent { get isRange() { return this.pickerSrv.isRange; } get confirmDisable() { if (this.isRange) { return (this.pickerSrv.currentActiveInput === 'start' && !this.pickerSrv.curRangeDate[0]) || (this.pickerSrv.currentActiveInput === 'end' && !this.pickerSrv.curRangeDate[1]); } else { return false; } } constructor(pickerSrv, i18n) { this.pickerSrv = pickerSrv; this.i18n = i18n; this.setI18nText(); } setI18nText() { this.i18nText = this.i18n.getI18nText().common; this.i18nSubscription = this.i18n.langChange().subscribe((data) => { this.i18nText = data.common; }); } ensureDate() { if (this.pickerSrv.isRange) { if (this.pickerSrv.currentActiveInput === 'start') { this.pickerSrv.currentActiveInput = 'end'; this.pickerSrv.activeInputChange.next('end'); } else if (!this.pickerSrv.curRangeDate[0]) { this.pickerSrv.currentActiveInput = 'start'; this.pickerSrv.activeInputChange.next('start'); } else { this.close(true); } } else { this.close(true); } } close(isConfirm = false) { this.pickerSrv.closeDropdownEvent.next(isConfirm); } ngOnDestroy() { this.i18nSubscription.unsubscribe(); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FooterPanelComponent, deps: [{ token: DatepickerProService }, { token: i1.I18nService }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: FooterPanelComponent, selector: "d-datepicker-footer-panel", inputs: { footerTemplate: "footerTemplate" }, ngImport: i0, template: "<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", styles: [".devui-datepicekr-pro-footer{padding:8px;text-align:center;border-top:1px solid var(--devui-dividing-line, #f0f0f0)}.devui-datepicekr-pro-footer .devui-cancel-button{margin-left:4px}\n"], dependencies: [{ kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i4.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: i4$1.ButtonComponent, selector: "d-button", inputs: ["id", "type", "bsStyle", "shape", "bsSize", "bsPosition", "bordered", "icon", "disabled", "showLoading", "width", "autofocus", "loadingTemplateRef"], outputs: ["btnClick"] }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FooterPanelComponent, decorators: [{ type: Component, args: [{ selector: 'd-datepicker-footer-panel', preserveWhitespaces: false, template: "<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", styles: [".devui-datepicekr-pro-footer{padding:8px;text-align:center;border-top:1px solid var(--devui-dividing-line, #f0f0f0)}.devui-datepicekr-pro-footer .devui-cancel-button{margin-left:4px}\n"] }] }], ctorParameters: () => [{ type: DatepickerProService }, { type: i1.I18nService }], propDecorators: { footerTemplate: [{ type: Input }] } }); const DAY_DURATION = 24 * 60 * 60 * 1000; const HOUR_DURATION = 60 * 60 * 1000; class CalendarPanelComponent { get curHoverDate() { return this.pickerSrv.curHoverDate; } set curHoverDate(value) { this.pickerSrv.curHoverDate = value; this.cdr.detectChanges(); } get curDate() { if (this.isRangeType) { if (this.pickerSrv.currentActiveInput === 'start') { return this.pickerSrv.curRangeDate[0]; } else if (this.pickerSrv.currentActiveInput === 'end') { return this.pickerSrv.curRangeDate[1]; } } else { return this.pickerSrv.curDate; } } set curDate(value) { if (this.isRangeType) { if (this.pickerSrv.currentActiveInput === 'start') { this.pickerSrv.curRangeDate[0] = value; } else if (this.pickerSrv.currentActiveInput === 'end') { if (this.pickerSrv.showTime) { this.pickerSrv.curRangeDate[1] = (this.pickerSrv.curRangeDate[1] && this.pickerSrv.curRangeDate[1].toDateString() === value.toDateString()) ? value : new Date(value.setHours(23, 59, 59)); } else { this.pickerSrv.curRangeDate[1] = new Date(value.setHours(23, 59, 59)); } } } else { this.pickerSrv.curDate = value; } } get selectedRangeDate() { return this.pickerSrv.curRangeDate; } set selectedRangeDate(dateList) { this.pickerSrv.curRangeDate = dateList; } get markDateTemplate() { return this.pickerSrv.markDateInfoTemplate; } constructor(i18n, pickerSrv, dataSrv, cdr, scrollDispatcher) { this.i18n = i18n; this.pickerSrv = pickerSrv; this.dataSrv = dataSrv; this.cdr = cdr; this.scrollDispatcher = scrollDispatcher; this.isWeekSelect = false; this.currentBodyIndex = 0; this.calendarItemSize = 186; // 一个月份日历的高度 this.unsubscribe$ = new Subject(); this.allMonthList = []; this.yearAndMonthList = []; this.isListCollopse = false; this.weekHoverRange = []; this.isSummerTimeZone = false; } setI18nText() { this.i18nText = this.i18n.getI18nText().datePickerPro; this.i18nSubscription = this.i18n.langChange().pipe(takeUntil(this.unsubscribe$)).subscribe((data) => { this.i18nText = data.datePickerPro; this.cdr.detectChanges(); }); } ngOnInit() { this.setI18nText(); this.today = new Date(); this.isSummerTimeZone = this.judgeIsSummerTimeZone(); this.initDataList(); this.initObservable(); } // 判断当前时区是否需要夏令时变换 judgeIsSummerTimeZone() { const Jan1 = new Date(2023, 0); const Jul1 = new Date(2023, 6); // 两个日期之间在非夏令时地区相差181天整 if ((Jul1.getTime() - Jan1.getTime()) / DAY_DURATION !== 181) { return true; } ; return false; } initObservable() { this.pickerSrv.toggleEvent.pipe(takeUntil(this.unsubscribe$)).subscribe(isOpen => { if (isOpen) { setTimeout(() => { this.scrollBodyCmp.checkViewportSize(); this.scrollListCmp.checkViewportSize(); this.goToDate(this.curDate || new Date(), 'auto'); }); // 首次展开添加滚动监听 if (!this.scrollListener) { this.initScrollListener(); } } else { this.curHoverDate = null; } }); this.pickerSrv.updateDateValue.pipe(takeUntil(this.unsubscribe$)).subscribe(res => { if (this.isRangeType) { this.updateRangeDate(res.value); } else { this.updateSingleDate(res.value); } }); this.pickerSrv.detectedChanges.pipe(takeUntil(this.unsubscribe$)).subscribe(() => { this.cdr.detectChanges(); }); this.pickerSrv.activeInputChange.pipe(takeUntil(this.unsubscribe$)).subscribe(type => { if (type === 'start') { this.goToDate(this.selectedRangeDate[0] || this.selectedRangeDate[1] || new Date()); } else { if (!this.selectedRangeDate[1] && this.selectedRangeDate[0]) { this.updateRangeDate([ this.selectedRangeDate[0], new Date(new Date(this.selectedRangeDate[0].getTime()).setHours(23, 59, 59)) ]); } this.goToDate(this.selectedRangeDate[1] || this.selectedRangeDate[0] || new Date()); } }); } initScrollListener() { this.scrollDispatcher.scrolled().pipe(takeUntil(this.unsubscribe$), filter((res) => { return res && res.getElementRef().nativeElement.classList.contains('devui-tbody-wrapper'); }), debounceTime(50)).subscribe(() => { const offsetY = this.scrollBodyCmp.measureScrollOffset(); // 当滚动超过一个月面板的一半时,就更新月份 this.currentBodyIndex = Math.floor(offsetY / this.calendarItemSize) + (offsetY % this.calendarItemSize > 100 ? 1 : 0); const listIndex = this.isListCollopse ? Math.floor(this.currentBodyIndex / 12) : this.currentBodyIndex + Math.floor(this.currentBodyIndex / 12) + 1; this.goToListByIndex(listIndex); this.cdr.detectChanges(); }); } initDataList() { const key = `${this.pickerSrv.calendarRange.join('-')}-${this.pickerSrv.minDate.toDateString() + this.pickerSrv.maxDate.toDateString()}`; if (this.dataSrv.calendarDataCache[key]) { this.yearAndMonthList = this.dataSrv.calendarDataCache[key].yearAndMonthList; this.allMonthList = this.dataSrv.calendarDataCache[key].allMonthList; return; } this.yearAndMonthList = []; this.allMonthList = []; for (let yearIndex = this.pickerSrv.calendarRange[0]; yearIndex <= this.pickerSrv.calendarRange[1]; yearIndex++) { this.yearAndMonthList.push({ year: yearIndex, isMonth: false, active: false }); for (let monthIndex = 0; monthIndex < 12; monthIndex++) { this.allMonthList.push({ year: yearIndex, month: monthIndex, displayWeeks: this.getDisplayWeeks(yearIndex, monthIndex) }); this.yearAndMonthList.push({ year: yearIndex, month: monthIndex, isMonth: true, active: false }); } } this.dataSrv.calendarDataCache[key] = { yearAndMonthList: this.yearAndMonthList, allMonthList: this.allMonthList }; } getDisplayWeeks(yearIndex, monthIndex) { const firstDayOfMonth = new Date(yearIndex, monthIndex, 1); const weekOfDay = firstDayOfMonth.getDay(); const startDate = new Date(firstDayOfMonth.getTime() - weekOfDay * DAY_DURATION); const displayWeeks = []; for (let i = 0; i < 6; i++) { const startWeekDate = startDate.getTime() + i * 7 * DAY_DURATION; const weekDays = new Array(7).fill(0).map((value, index) => { let currentDate = new Date(startWeekDate + index * DAY_DURATION); // 夏令时区检测出错误的冬令时时间,加快一小时 if (this.isSummerTimeZone && currentDate.getHours() === 23) { currentDate = new Date(startWeekDate + index * DAY_DURATION + HOUR_DURATION); } return { day: this.fillLeft(currentDate.getDate()), date: currentDate, inMonth: currentDate.getMonth().toString() === monthIndex.toString(), isToday: currentDate.toDateString() === this.today.toDateString() }; }); displayWeeks.push(weekDays); } return displayWeeks; } goToDate(date, scrollBehavior) { const indexObj = this.getCurrentIndex(date); const scroll = scrollBehavior || (Math.abs(indexObj.bodyIndex - this.currentBodyIndex) > 18 ? 'auto' : 'smooth'); this.currentBodyIndex = indexObj.bodyIndex; this.scrollBodyCmp.scrollToIndex(indexObj.bodyIndex, scroll); this.goToListByIndex(indexObj.listIndex); this.cdr.detectChanges(); } goToListByIndex(index) { const indexDelta = Math.abs(this.scrollListCmp.measureScrollOffset() / 30 - index); this.scrollListCmp.scrollToIndex(index - 4, indexDelta < 12 ? 'smooth' : 'auto'); this.updateListActive(index); } selectMonth(year, month) { const date = new Date(year, month, 1); const curYear = this.yearAndMonthList.find(t => t.active)?.year || this.curDate.getFullYear(); // 太远的虚拟滚动会导致白屏,所以超过两年的滚动都直接跳转 const isSmoothAnimation = Math.abs(curYear - year) < 2; if (this.isListCollopse) { this.toggleListCollopse(date); } else { this.goToDate(date, isSmoothAnimation ? 'smooth' : 'auto'); } } updateRangeDate(dateList) { if (!dateList) { this.selectedRangeDate = []; this.cdr.detectChanges(); return; } const curDate = (this.pickerSrv.currentActiveInput === 'start' ? (dateList[0] || dateList[1]) : (dateList[1] || dateList[0])) || new Date(); const moreThanOneYear = Math.abs(curDate.getFullYear() - (this.currentBodyIndex / 12 + this.pickerSrv.calendarRange[0])) > 1; this.selectedRangeDate = dateList; this.goToDate(curDate, moreThanOneYear ? 'auto' : 'smooth'); this.cdr.detectChanges(); } updateSingleDate(date) { if (!date) { this.curDate = null; this.cdr.detectChanges(); return; } const moreThanOneYear = Math.abs(date.getFullYear() - (this.currentBodyIndex / 12 + this.pickerSrv.calendarRange[0])) > 1; this.curDate = date; this.goToDate(this.curDate, moreThanOneYear ? 'auto' : 'smooth'); this.cdr.detectChanges(); } updateListActive(index) { const curActive = this.yearAndMonthList.find(t => t.active); if (curActive) { curActive.active = false; } this.yearAndMonthList[index].active = true; } getCurrentIndex(curDate) { const year = curDate.getFullYear(); const month = curDate.getMonth(); const listIndex = this.isListCollopse ? (year - this.pickerSrv.calendarRange[0]) : (year - this.pickerSrv.calendarRange[0]) * 13 + month + 1; return { bodyIndex: (year - this.pickerSrv.calendarRange[0]) * 12 + month, listIndex }; } selectDate(day) { if (this.isDisabled(day.date) || !day.inMonth) { return; } if (this.isWeekSelect) { this.pickerSrv.curRangeDate = this.getWeekRange(day.date); this.pickerSrv.currentActiveInput = 'end'; } else { this.curDate = new Date(day.date.setHours(this.pickerSrv.curHour, this.pickerSrv.curMin, this.pickerSrv.curSec)); } if (this.isRangeType) { this.pickerSrv.fixRangeDate(); } this.cdr.detectChanges(); // 非时间模式下选完开始日期跳转到结束日期 if (this.isRangeType && !this.pickerSrv.showTime) { if (this.pickerSrv.currentActiveInput === 'start') { this.pickerSrv.currentActiveInput = 'end'; } else if (this.pickerSrv.currentActiveInput === 'end' && !this.selectedRangeDate[0]) { this.selectedRangeDate[0] = this.curDate; this.pickerSrv.currentActiveInput = 'start'; } else { this.pickerSrv.closeDropdownEvent.next(false); } } this.pickerSrv.selectedDateChange.next({ type: this.isRangeType ? 'range' : 'single', value: this.isRangeType ? this.selectedRangeDate : this.curDate }); if (this.isRangeType && this.pickerSrv.showTime) { this.pickerSrv.updateTimeChange.next({ activeInput: this.pickerSrv.currentActiveInput, hour: this.pickerSrv.curHour, min: this.pickerSrv.curMin, seconds: this.pickerSrv.curSec }); } if (this.pickerSrv.closeAfterSelected) { this.pickerSrv.closeDropdownEvent.next(false); } } isStartDate(date) { return this.pickerSrv.isStartDate(date); } isDisabled(date) { return !this.pickerSrv.dateInRange(date); } isEndDate(date) { return this.pickerSrv.isEndDate(date); } isDateInRange(date) { if (this.isWeekSelect) { return this.isInWeekHoverRange(date); } else { return this.pickerSrv.isDateInRange(date); } } isDateInSelectRange(date) { return this.pickerSrv.isDateInSelectRange(date); } isDateActive(date) { return this.pickerSrv.isDateActive(date); } isActiveTypeDate(date) { return this.pickerSrv.isActiveInputTypeDate(date); } isDateAbandon(date) { return this.pickerSrv.isDateAbandon(date); } isDateSuggest(date) { return this.pickerSrv.isInSuggestList(date); } isDateMarked(date) { return this.pickerSrv.isMarkedDate(date); } isSingleDate() { if (this.pickerSrv.currentActiveInput === 'start') { return !this.pickerSrv.curRangeDate[1]; } else if (this.pickerSrv.currentActiveInput === 'end') { return !this.pickerSrv.curRangeDate[0]; } } setHoverTarget(date, isInMonth) { if (!isInMonth) { this.curHoverDate = null; return; } this.curHoverDate = date; if (this.isWeekSelect) { this.weekHoverRange = this.getWeekRange(date); this.curWeekHoverDate = date; this.cdr.markForCheck(); return; } } getWeekRange(date) { if (!date) { return []; } const diff = date.getDay() < this.pickerSrv.startIndexOfWeek ? 7 - (this.pickerSrv.startIndexOfWeek - date.getDay()) : date.getDay() - this.pickerSrv.startIndexOfWeek; const weekStart = new Date(date.getTime() - diff * DAY_DURATION); const weekEnd = new Date(weekStart.getTime() + DAY_DURATION * 6); weekEnd.setHours(23, 59, 59); return [weekStart, weekEnd]; } isInWeekHoverRange(date) { const range = this.getWeekRange(this.curWeekHoverDate); const time = date.getTime(); const timeStr = date.toDateString(); if (this.pickerSrv.isDateActive(date)) { return false; } return ((range[0]?.getTime() < time && time < range[1]?.getTime()) || (range[0]?.toDateString() === timeStr || timeStr === ran