import * as React from "react" ; import { isEqual } from '@microsoft/sp-lodash-subset' ; import { TimeConvention , DateConvention } from "./DateTimeConventions" ; import { DatePicker } from "office-ui-fabric-react/lib/DatePicker" ; import { Label } from "office-ui-fabric-react/lib/Label" ; import ErrorMessage from "../errorMessage/ErrorMessage" ; import styles from "./DateTimePicker.module.scss" ; import HoursComponent from "./HoursComponent" ; import MinutesComponent from "./MinutesComponent" ; import SecondsComponent from "./SecondsComponent" ; import * as telemetry from "../../common/telemetry" ; import { Async , css } from 'office-ui-fabric-react/lib/Utilities' ; import { IDateTimePickerProps , IDateTimePickerState , DateTimePickerStrings } from "." ; import { TimeHelper } from "./TimeHelper" ; import { TimeDisplayControlType } from "./TimeDisplayControlType" ; interface IDateComponents { day : Date ; hours : number ; minutes : number ; seconds : number ; } /**
 * Renders the controls for DateTimePicker component
 */ export class DateTimePicker extends React . Component < IDateTimePickerProps , IDateTimePickerState > { private _latestValidateValue : number = NaN ; private async : Async ; private delayedValidate : ( value : Date ) => void ; /**
     * Constructor
     */ constructor ( props ) { super ( props ) ; telemetry . track ( 'ReactDateTimePicker' , { dateConvention : props . dateConvention ? DateConvention [ props . dateConvention ] : '' , formatDate : ! ! props . formatDate , timeConvention : props . timeConvention ? TimeConvention [ props . timeConvention ] : '' , disabled : props . disabled } ) ; const { value = null } = this . props ; const { day , hours , minutes , seconds } = DateTimePicker . getDateComponents ( value , props . dateConvention ) ; this . state = { day , hours , minutes , seconds , errorMessage : '' } ; this . async = new Async ( this ) ; this . delayedValidate = this . async . debounce ( this . validate , props . deferredValidationTime ) ; } /**
     * Called when the component will unmount
     */ public componentWillUnmount ( ) { this . async . dispose ( ) ; } /**
     * Called before the component receives new props, used for matching state with new props.
     */ public componentWillReceiveProps ( nextProps ) { if ( ! isEqual ( nextProps . value , this . props . value ) ) { const { day , hours , minutes , seconds } = DateTimePicker . getDateComponents ( nextProps . value , this . props . dateConvention ) ; this . setState ( { day , hours , minutes , seconds } ) ; } } param date param dateConvention private static getDateComponents ( date , dateConvention ) { const day = TimeHelper . cloneDate ( date ) ; const hours = dateConvention !== DateConvention . Date && day !== null ? day . getHours ( ) : 0 ; const minutes = dateConvention !== DateConvention . Date && day !== null ? day . getMinutes ( ) : 0 ; const seconds = dateConvention !== DateConvention . Date && day !== null ? day . getSeconds ( ) : 0 ; if ( day !== null ) { day . setHours ( hours ) ; day . setMinutes ( minutes ) ; day . setSeconds ( seconds ) ; } return { day , hours , minutes , seconds } ; } /**
     * Function called when the DatePicker Office UI Fabric component selected date changed
     */ private onSelectDate = ( date ) : void => { if ( ! TimeHelper . isValidDate ( date ) ) { return ; } const { hours = 0 , minutes = 0 , seconds = 0 } = this . state ; const day = TimeHelper . cloneDate ( date ) ; day . setHours ( hours ) ; day . setMinutes ( minutes ) ; day . setSeconds ( seconds ) ; this . setState ( { day } , ( ) => this . delayedValidate ( this . state . day ) ) ; } ; param element private dropdownHoursChanged = ( value ? ) : void => { this . setState ( ( { day } ) => { const hoursSplit = value . split ( " " ) ; const hoursValue = hoursSplit [ 0 ] . length > 2 ? hoursSplit [ 0 ] . substring ( 0 , 2 ) : hoursSplit [ 0 ] ; let hours = parseInt ( hoursValue ) ; if ( isNaN ( hours ) ) { return ; } if ( this . props . timeConvention !== TimeConvention . Hours24 ) { if ( hoursSplit [ 1 ] && hoursSplit [ 1 ] . toLowerCase ( ) . indexOf ( "pm" ) !== - 1 ) { hours += 12 ; if ( hours === 24 ) { hours = 12 ; } } else if ( hours === 12 ) { hours = 0 ; } } if ( hours > 23 ) { return ; } const state = { hours } ; const date = TimeHelper . cloneDate ( day ) ; if ( date ) { date . setHours ( hours ) ; state . day = date ; } return state ; } , ( ) => this . delayedValidate ( this . state . day ) ) ; } ; param element private dropdownMinutesChanged = ( value ? ) : void => { this . setState ( ( { day } ) => { const minutes = parseInt ( value . length > 2 ? value . substring ( 0 , 2 ) : value ) ; const state = { minutes } ; const date = TimeHelper . cloneDate ( day ) ; if ( date ) { date . setMinutes ( minutes ) ; state . day = date ; } return state ; } , ( ) => this . delayedValidate ( this . state . day ) ) ; } ; param element private dropdownSecondsChanged = ( value ? ) : void => { this . setState ( ( { day } ) => { const seconds = parseInt ( value . length > 2 ? value . substring ( 0 , 2 ) : value ) ; const state = { seconds } ; const date = TimeHelper . cloneDate ( day ) ; if ( date ) { date . setSeconds ( seconds ) ; state . day = date ; } return state ; } , ( ) => this . delayedValidate ( this . state . day ) ) ; } ; /**
     * Validates string input on date time field
     */ private handleTextChange = ( e , newValue , locString ) => { if ( ! TimeHelper . isValidDate ( newValue ) ) { this . setState ( { errorMessage : locString , } ) ; return ; } } ; /**
     * Renders the control
     */ public render ( ) { const { label , disabled , timeConvention , dateConvention = DateConvention . DateTime , firstDayOfWeek , isMonthPickerVisible = true , showGoToToday , showMonthPickerAsOverlay = false , showWeekNumbers = false , showSeconds = false , formatDate , value = this . state . day , strings : dateStrings = new DateTimePickerStrings ( ) , timeDisplayControlType , placeholder , showLabels , minDate , maxDate , minutesIncrementStep } = this . props ; const { textErrorMessage } = dateStrings ; const hours = value != null ? value . getHours ( ) : this . state . hours ; const minutes = value != null ? value . getMinutes ( ) : this . state . minutes ; const seconds = value != null ? value . getSeconds ( ) : this . state . seconds ; let timeElm = < div className = "hidden" / > ; if ( dateConvention === DateConvention . DateTime ) { timeElm = ( < div className = { css ( styles . row , styles . timeRow ) } > { showLabels !== false && < div className = { styles . labelCell } > < Label className = { styles . fieldLabel } > { dateStrings . timeLabel } < /Label></div > } < div className = { styles . time } > < div className = { styles . picker } > < HoursComponent disabled = { disabled } timeConvention = { timeConvention } value = { hours } onChange = { this . dropdownHoursChanged } timeDisplayControlType = { timeDisplayControlType || TimeDisplayControlType . Text } /> < /div> < div className = { styles . separator } > < Label > { dateStrings . timeSeparator } < /Label> < /div> < div className = { styles . picker } > < MinutesComponent disabled = { disabled } value = { minutes } minutesIncrementStep = { minutesIncrementStep } onChange = { this . dropdownMinutesChanged } timeDisplayControlType = { timeDisplayControlType || TimeDisplayControlType . Text } /> < /div> { showSeconds && ( < div className = { styles . separator } > < Label > { dateStrings . timeSeparator } < /Label> < /div>)} { showSeconds && ( < div className = { styles . picker } > < SecondsComponent disabled = { disabled } value = { seconds } onChange = { this . dropdownSecondsChanged } timeDisplayControlType = { timeDisplayControlType || TimeDisplayControlType . Text } /> < /div>)} < /div> < /div>); } return ( < div className = { styles . dateTimePicker } > { label && < Label > { label } < /Label>} < div className = { styles . container } > < div className = { styles . row } > { showLabels !== false && ( < div className = { styles . labelCell } > < Label className = { styles . fieldLabel } > { dateStrings . dateLabel } < /Label> < /div>)} < div className = { styles . picker } > < DatePicker formatDate = { formatDate } disabled = { disabled } value = { value } strings = { dateStrings } isMonthPickerVisible = { isMonthPickerVisible } onSelectDate = { this . onSelectDate } allowTextInput = { true } firstDayOfWeek = { firstDayOfWeek } showGoToToday = { showGoToToday } showMonthPickerAsOverlay = { showMonthPickerAsOverlay } showWeekNumbers = { showWeekNumbers } placeholder = { placeholder } minDate = { minDate } maxDate = { maxDate } textField = { { onChange : ( e , newValue ) => this . handleTextChange ( e , newValue , textErrorMessage ) } } /> < /div> < /div> { timeElm } < /div> < ErrorMessage errorMessage = { this . state . errorMessage } / > < /div>); } private validate = ( dateVal ) : void => { if ( typeof this . props . onGetErrorMessage === 'undefined' || this . props . onGetErrorMessage === null ) { this . notifyAfterValidate ( this . props . value , dateVal ) ; return ; } const timestamp = dateVal . getTime ( ) ; if ( this . _latestValidateValue === timestamp ) { return ; } this . _latestValidateValue = timestamp ; const result = this . props . onGetErrorMessage ( dateVal ) ; if ( typeof result !== 'undefined' ) { if ( typeof result === 'string' ) { if ( result === '' ) { this . notifyAfterValidate ( this . props . value , dateVal ) ; } this . setState ( { errorMessage : result } ) ; } else { result . then ( ( errorMessage ) => { if ( typeof errorMessage === 'undefined' || errorMessage === '' ) { this . notifyAfterValidate ( this . props . value , dateVal ) ; } this . setState ( { errorMessage : errorMessage } ) ; } ) ; } } else { this . notifyAfterValidate ( this . props . value , dateVal ) ; } } ; private notifyAfterValidate = ( oldValue , newValue ) => { if ( typeof this . props . onChange !== 'undefined' && this . props . onChange !== null && newValue !== null ) { this . props . onChange ( newValue ) ; } } ; }