import { endOfDay , startOfDay } from 'date-fns' ; import { FunctionProp , helpers , sortRangeValue , TinyDate , TinyDateCompareGrain } from 'ngx-tethys/util' ; import { ChangeDetectionStrategy , ChangeDetectorRef , Component , EventEmitter , Input , OnChanges , OnInit , Output , SimpleChanges , TemplateRef } from '@angular/core' ; import { dateAddAmount , hasValue , makeValue , transformDateValue } from '../../picker.util' ; import { CompatibleDate , CompatibleValue , DisabledDateFn , PanelMode , RangePartType , SupportTimeOptions } from '../../standard-types' ; @ Component ( { changeDetection : ChangeDetectionStrategy . OnPush , selector : 'date-popup' , exportAs : 'datePopup' , templateUrl : './date-popup.component.html' } ) export class DatePopupComponent implements OnChanges , OnInit { @ Input ( ) isRange : boolean ; @ Input ( ) showWeek : boolean ; @ Input ( ) format : string ; @ Input ( ) disabledDate : DisabledDateFn ; @ Input ( ) minDate : Date | number ; @ Input ( ) maxDate : Date | number ; @ Input ( ) showToday : boolean ; @ Input ( ) showTime : SupportTimeOptions | boolean ; @ Input ( ) mustShowTime : boolean ; @ Input ( ) dateRender : FunctionProp < TemplateRef < Date > | string > ; @ Input ( ) className : string ; @ Input ( ) panelMode : PanelMode | PanelMode [ ] ; @ Input ( ) value : CompatibleValue ; @ Input ( ) defaultPickerValue : CompatibleDate | number ; @ Output ( ) readonly panelModeChange = new EventEmitter < PanelMode | PanelMode [ ] > ( ) ; @ Output ( ) readonly calendarChange = new EventEmitter < CompatibleValue > ( ) ; @ Output ( ) readonly valueChange = new EventEmitter < CompatibleValue > ( ) ; @ Output ( ) readonly resultOk = new EventEmitter < void > ( ) ; @ Output ( ) readonly showTimePickerChange = new EventEmitter < boolean > ( ) ; prefixCls = 'thy-calendar' ; showTimePicker = false ; timeOptions : SupportTimeOptions | SupportTimeOptions [ ] | null ; activeDate : TinyDate | TinyDate [ ] ; selectedValue : TinyDate [ ] = [ ] ; hoverValue : TinyDate [ ] = [ ] ; get hasTimePicker ( ) : boolean { return ! ! this . showTime ; } private partTypeMap : { [ key ] : number ; } = { left : 0 , right : 1 } ; [ property ] : any ; endPanelMode : PanelMode | PanelMode [ ] = 'date' ; constructor ( private cdr ) { } setProperty < T extends keyof DatePopupComponent > ( key , value ) { this [ key ] = value ; this . cdr . markForCheck ( ) ; } ngOnInit ( ) { this . initPanelMode ( ) ; if ( this . defaultPickerValue && ! hasValue ( this . value ) ) { const { value } = transformDateValue ( this . defaultPickerValue ) ; this . value = makeValue ( value , this . isRange ) ; } this . updateActiveDate ( ) ; this . initDisabledDate ( ) ; } ngOnChanges ( changes ) { if ( changes . panelMode ) { if ( helpers . isArray ( this . panelMode ) ) { this . endPanelMode = [ ... this . panelMode ] ; } else { this . endPanelMode = this . panelMode ; } } if ( changes . defaultPickerValue ) { this . updateActiveDate ( ) ; } if ( changes . value && changes . value . currentValue ) { this . updateActiveDate ( ) ; } } updateActiveDate ( ) { this . clearHoverValue ( ) ; if ( ! this . value ) { const { value } = transformDateValue ( this . defaultPickerValue ) ; this . value = makeValue ( value , this . isRange ) ; } if ( this . isRange ) { this . selectedValue = this . value as TinyDate [ ] ; this . activeDate = this . normalizeRangeValue ( this . value as TinyDate [ ] , this . getPanelMode ( this . endPanelMode ) as PanelMode ) ; } else { this . activeDate = this . value as TinyDate ; } } initPanelMode ( ) { if ( ! this . panelMode ) { this . panelMode = this . isRange ? [ 'date' , 'date' ] : 'date' ; } if ( helpers . isArray ( this . panelMode ) ) { this . endPanelMode = [ ... this . panelMode ] ; } else { this . endPanelMode = this . panelMode ; } } initDisabledDate ( ) { let minDate ; let maxDate ; let disabledDateFn ; if ( this . minDate ) { const { value } = transformDateValue ( this . minDate ) ; minDate = new TinyDate ( value as Date ) ; } if ( this . maxDate ) { const { value } = transformDateValue ( this . maxDate ) ; maxDate = new TinyDate ( value as Date ) ; } if ( this . disabledDate ) { disabledDateFn = this . disabledDate ; } this . disabledDate = d => { let expression = false ; if ( minDate ) { expression = d < minDate . startOfDay ( ) . nativeDate ; } if ( maxDate && ! expression ) { expression = d > maxDate . endOfDay ( ) . nativeDate ; } if ( disabledDateFn && typeof disabledDateFn === 'function' && ! expression ) { expression = disabledDateFn ( d ) ; } return expression ; } ; } onShowTimePickerChange ( show ) { this . showTimePicker = show ; this . showTimePickerChange . emit ( show ) ; } onClickOk ( ) { this . setValue ( this . value ) ; this . valueChange . emit ( this . value ) ; this . resultOk . emit ( ) ; } onClickRemove ( ) { this . value = this . isRange ? [ ] : null ; this . setValue ( this . value ) ; this . valueChange . emit ( this . value ) ; } onDayHover ( value ) { if ( this . isRange && this . selectedValue [ 0 ] && ! this . selectedValue [ 1 ] ) { const base = this . selectedValue [ 0 ] ; if ( base . isBeforeDay ( value ) ) { this . hoverValue = [ base , value ] ; } else { this . hoverValue = [ value , base ] ; } } } onPanelModeChange ( mode , partType ? ) { if ( this . isRange ) { ( this . panelMode as PanelMode [ ] ) [ this . getPartTypeIndex ( partType ) ] = mode ; } else { this . panelMode = mode ; } this . panelModeChange . emit ( this . panelMode ) ; } onHeaderChange ( value , partType ? ) { if ( this . isRange ) { this . activeDate [ this . getPartTypeIndex ( partType ) ] = value ; this . activeDate = this . normalizeRangeValue ( this . activeDate as TinyDate [ ] , this . getPanelMode ( this . endPanelMode , partType ) as PanelMode ) ; } else { this . activeDate = value ; } } onSelectTime ( value , partType ? ) { if ( this . isRange ) { } else { this . setValue ( new TinyDate ( value . nativeDate ) ) ; } } changeValueFromSelect ( value , partType ? ) { if ( this . isRange ) { const [ left , right ] = this . selectedValue as TinyDate [ ] ; if ( ( ! left && ! right ) || ( left && right ) ) { this . hoverValue = this . selectedValue = [ value ] ; this . selectedValue = [ new TinyDate ( startOfDay ( this . selectedValue [ 0 ] . nativeDate ) ) ] ; this . calendarChange . emit ( [ this . selectedValue [ 0 ] . clone ( ) ] ) ; } else if ( left && ! right ) { this . clearHoverValue ( ) ; this . setRangeValue ( 'right' , value ) ; this . selectedValue = sortRangeValue ( this . selectedValue ) ; this . selectedValue = [ new TinyDate ( startOfDay ( this . selectedValue [ 0 ] . nativeDate ) ) , new TinyDate ( endOfDay ( this . selectedValue [ 1 ] . nativeDate ) ) ] ; this . activeDate = this . normalizeRangeValue ( this . selectedValue , this . getPanelMode ( this . endPanelMode , partType ) as PanelMode ) ; this . setValue ( this . cloneRangeDate ( this . selectedValue ) ) ; this . calendarChange . emit ( this . cloneRangeDate ( this . selectedValue ) ) ; } } else { this . setValue ( value ) ; } } enablePrevNext ( direction , partType ? ) { if ( this . isRange && this . panelMode === this . endPanelMode ) { const [ start , end ] = this . activeDate as TinyDate [ ] ; const showMiddle = ! start . addMonths ( 1 ) . isSame ( end , 'month' ) ; if ( ( partType === 'left' && direction === 'next' ) || ( partType === 'right' && direction === 'prev' ) ) { return showMiddle ; } return true ; } else { return true ; } } getPanelMode ( panelMode , partType ? ) { if ( this . isRange ) { return panelMode [ this . getPartTypeIndex ( partType ) ] as PanelMode ; } else { return panelMode as PanelMode ; } } getValueBySelector ( partType ? ) { if ( this . isRange ) { const valueShow = this . selectedValue ; return ( valueShow as TinyDate [ ] ) [ this . getPartTypeIndex ( partType ) ] ; } else { return this . value as TinyDate ; } } getActiveDate ( partType ? ) { if ( this . isRange ) { return this . activeDate [ this . getPartTypeIndex ( partType ) ] ; } else { return this . activeDate as TinyDate ; } } getPartTypeIndex ( partType = 'left' ) { return this . partTypeMap [ partType ] ; } private clearHoverValue ( ) { this . hoverValue = [ ] ; } private setValue ( value ) { this . value = value ; if ( ! this . showTime || ! this . showTimePicker ) { this . valueChange . emit ( this . value ) ; } } private normalizeRangeValue ( value , mode = 'month' ) { const headerModes = { date : 'month' , month : 'year' , year : 'decade' } ; const headerMode = headerModes [ mode ] ; const [ start , end ] = value ; const newStart = start || new TinyDate ( ) ; let newEnd = end ; if ( ! newEnd ) { newEnd = dateAddAmount ( newStart , 1 , headerMode ) ; } if ( newStart . isSame ( end , headerMode as TinyDateCompareGrain ) ) { newEnd = dateAddAmount ( newStart , 1 , headerMode ) ; } return [ newStart , newEnd ] ; } private setRangeValue ( partType , value ) { const ref = ( this . selectedValue = this . cloneRangeDate ( this . selectedValue as TinyDate [ ] ) ) ; ref [ this . getPartTypeIndex ( partType ) ] = value ; } private cloneRangeDate ( value ) { return [ value [ 0 ] && value [ 0 ] . clone ( ) , value [ 1 ] && value [ 1 ] . clone ( ) ] as TinyDate [ ] ; } private initialArray ( key ) { if ( ! this [ key ] || ! Array . isArray ( this [ key ] ) ) { this [ key ] = [ ] ; } } }