import * as React from "react" ; import { CaptionElementProps } from "react-day-picker" ; import { polyfill } from "react-lifecycles-compat" ; import { AbstractPureComponent2 , Divider , HTMLSelect , IconSize , OptionProps } from "@blueprintjs/core" ; import * as Classes from "./common/classes" ; import { clone } from "./common/dateUtils" ; import { measureTextWidth } from "./common/utils" ; export interface IDatePickerCaptionProps extends CaptionElementProps { maxDate : Date ; minDate : Date ; onMonthChange ? : ( month : number ) => void ; onYearChange ? : ( year : number ) => void ; /** Callback invoked when the month or year `<select>` is changed. */ onDateChange ? : ( date : Date ) => void ; reverseMonthAndYearMenus ? : boolean ; } export interface IDatePickerCaptionState { monthRightOffset : number ; } @ polyfill export class DatePickerCaption extends AbstractPureComponent2 < IDatePickerCaptionProps , IDatePickerCaptionState > { public state : IDatePickerCaptionState = { monthRightOffset : 0 } ; private containerElement : HTMLElement ; private displayedMonthText : string ; private handleMonthSelectChange = this . dateChangeHandler ( ( d , month ) => d . setMonth ( month ) , this . props . onMonthChange ) ; private handleYearSelectChange = this . dateChangeHandler ( ( d , year ) => d . setFullYear ( year ) , this . props . onYearChange ) ; public render ( ) { const { date , locale , localeUtils , minDate , maxDate , months = localeUtils . getMonths ( locale ) } = this . props ; const minYear = minDate . getFullYear ( ) ; const maxYear = maxDate . getFullYear ( ) ; const displayMonth = date . getMonth ( ) ; const displayYear = date . getFullYear ( ) ; const startMonth = displayYear === minYear ? minDate . getMonth ( ) : 0 ; const endMonth = displayYear === maxYear ? maxDate . getMonth ( ) + 1 : undefined ; const monthOptionElements = months . map < OptionProps > ( ( month , i ) => ( { label : month , value : i } ) ) . slice ( startMonth , endMonth ) ; const years = [ minYear ] ; for ( let year = minYear + 1 ; year <= maxYear ; ++ year ) { years . push ( year ) ; } if ( displayYear > maxYear ) { years . push ( { value : displayYear , disabled : true } ) ; } this . displayedMonthText = months [ displayMonth ] ; const monthSelect = ( < HTMLSelect iconProps = { { style : { right : this . state . monthRightOffset } } } className = { Classes . DATEPICKER_MONTH_SELECT } key = "month" minimal = { true } onChange = { this . handleMonthSelectChange } value = { displayMonth } options = { monthOptionElements } / > ) ; const yearSelect = ( < HTMLSelect className = { Classes . DATEPICKER_YEAR_SELECT } key = "year" minimal = { true } onChange = { this . handleYearSelectChange } value = { displayYear } options = { years } / > ) ; const orderedSelects = this . props . reverseMonthAndYearMenus ? [ yearSelect , monthSelect ] : [ monthSelect , yearSelect ] ; return ( < div className = { this . props . classNames . caption } > < div className = { Classes . DATEPICKER_CAPTION } ref = { ref => ( this . containerElement = ref ) } > { orderedSelects } < /div> < Divider / > < /div>); } public componentDidMount ( ) { this . requestAnimationFrame ( ( ) => this . positionArrows ( ) ) ; } public componentDidUpdate ( ) { this . positionArrows ( ) ; } private positionArrows ( ) { const monthTextWidth = measureTextWidth ( this . displayedMonthText , Classes . DATEPICKER_CAPTION_MEASURE , this . containerElement ) ; const monthSelectWidth = this . containerElement == null ? 0 : this . containerElement . firstElementChild . clientWidth ; const rightOffset = Math . max ( 2 , monthSelectWidth - monthTextWidth - IconSize . STANDARD - 2 ) ; this . setState ( { monthRightOffset : rightOffset } ) ; } private dateChangeHandler ( updater , handler ? ) { return ( e ) => { const value = parseInt ( ( e . target as HTMLSelectElement ) . value , 10 ) ; if ( isNaN ( value ) ) { return ; } const newDate = clone ( this . props . date ) ; updater ( newDate , value ) ; this . props . onDateChange ?. ( newDate ) ; handler ?. ( value ) ; } ; } }