UNPKG

react-date-picker

Version:

A carefully crafted date picker for React

377 lines (284 loc) 8.42 kB
import React, { PropTypes } from 'react' import Component from 'react-class' import { Flex, Item } from 'react-flex' import InlineBlock from 'react-inline-block' import assign from 'object-assign' import assignDefined from './assignDefined' import toMoment from './toMoment' import join from './join' import bemFactory from './bemFactory' import HistoryView from './HistoryView' const ARROWS = { prev: <svg height="24" viewBox="0 0 24 24" width="24"> <path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z" /> <path d="M0 0h24v24H0z" fill="none" /> </svg>, next: <svg height="24" viewBox="0 0 24 24" width="24"> <path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z" /> <path d="M0 0h24v24H0z" fill="none" /> </svg> } const bem = bemFactory('react-date-picker__nav-bar') export default class NavBar extends Component { constructor(props) { super(props) this.state = { viewDate: props.defaultViewDate } } prepareViewDate(props) { return props.viewDate === undefined ? this.state.viewDate : props.viewDate } render() { const props = this.p = assign({}, this.props) const viewMoment = props.viewMoment = props.viewMoment || this.toMoment( this.prepareViewDate(props) ) props.historyViewEnabled = props.expandedHistoryView || props.enableHistoryView const secondary = props.secondary const className = join( props.className, bem(), bem(null, `theme-${props.theme}`), props.historyViewEnabled && bem(null, 'with-history-view') ) const historyView = props.historyViewEnabled ? this.renderHistoryView() : null const flexProps = assign({}, props) delete flexProps.arrows delete flexProps.date delete flexProps.enableHistoryView delete flexProps.historyViewEnabled delete flexProps.isDatePickerNavBar delete flexProps.minDate delete flexProps.maxDate delete flexProps.navDateFormat delete flexProps.onNavClick delete flexProps.onViewDateChange delete flexProps.secondary delete flexProps.theme delete flexProps.viewDate delete flexProps.viewMoment if (typeof props.cleanup == 'function') { props.cleanup(flexProps) } return <Flex inline row {...flexProps} className={className}> {secondary && this.renderNav(-2, viewMoment)} {this.renderNav(-1, viewMoment)} <Item className={bem('date')} style={{ textAlign: 'center' }} onMouseDown={props.historyViewEnabled ? this.toggleHistoryView : null} > {this.renderNavDate(viewMoment)} </Item> {this.renderNav(1, viewMoment)} {secondary && this.renderNav(2, viewMoment)} {historyView} </Flex> } renderHistoryView() { if (!this.state.historyView) { return null } const className = bem('history-view') const { viewMoment, theme, minDate, maxDate } = this.p const historyViewProps = assignDefined({ defaultViewDate: viewMoment, defaultDate: viewMoment, ref: view => { this.historyView = view }, focusDecadeView: false, className, theme, onOkClick: this.onHistoryViewOk, onCancelClick: this.onHistoryViewCancel }, { minDate, maxDate }) if (this.props.renderHistoryView) { return this.props.renderHistoryView(historyViewProps) } return <HistoryView {...historyViewProps} /> } toggleHistoryView(event) { if (this.isHistoryViewVisible()) { this.hideHistoryView(event) } else { this.showHistoryView(event) } } getHistoryView() { return this.historyView } isHistoryViewVisible() { return !!this.historyView } onHistoryViewOk(dateString, { dateMoment, timestamp }) { this.hideHistoryView() this.onViewDateChange({ dateMoment, timestamp }) } onHistoryViewCancel() { this.hideHistoryView() } showHistoryView(event) { event.preventDefault() this.setState({ historyView: true }) if (this.props.onShowHistoryView) { this.props.onShowHistoryView() } } hideHistoryView(event) { if (event && event.preventDefault) { event.preventDefault() } this.setState({ historyView: false }) if (this.props.onHideHistoryView) { this.props.onHideHistoryView() } } toMoment(value, props) { props = props || this.props return toMoment(value, { locale: props.locale, dateFormat: props.dateFormat }) } renderNav(dir, viewMoment) { const props = this.p const name = dir < 0 ? 'prev' : 'next' let disabled = dir < 0 ? props.prevDisabled : props.nextDisabled const secondary = Math.abs(dir) == 2 if (dir < 0 && props.minDate) { const gotoMoment = this.getGotoMoment(dir, viewMoment).endOf('month') if (gotoMoment.isBefore(this.toMoment(props.minDate))) { disabled = true } } if (dir > 0 && props.maxDate) { const gotoMoment = this.getGotoMoment(dir, viewMoment).startOf('month') if (gotoMoment.isAfter(this.toMoment(props.maxDate))) { disabled = true } } if (this.state.historyView) { disabled = true } const className = [ bem('arrow'), bem(`arrow--${name}`), secondary && bem('secondary-arrow'), disabled && bem('arrow--disabled') ] const arrow = props.arrows[dir] || props.arrows[name] || ARROWS[name] let children if (secondary) { const dirArrow = props.arrows[dir] if (dirArrow) { children = dirArrow } else { const secondArrow = <InlineBlock style={{ position: 'absolute', [dir < 0 ? 'left' : 'left']: 7 }}>{arrow}</InlineBlock> children = dir < 0 ? [secondArrow, arrow] : [secondArrow, arrow] } } else { children = arrow } const navProps = { dir, name, disabled, className: join(className), onClick: !disabled && this.onNavClick.bind(this, dir, viewMoment), children } if (props.renderNav) { return props.renderNav(navProps) } if (dir < 0 && props.renderNavPrev) { return props.renderNavPrev(navProps) } if (dir > 0 && props.renderNavNext) { return props.renderNavNext(navProps) } return <InlineBlock {...navProps} disabled={null} name={null} /> } getGotoMoment(dir, viewMoment) { viewMoment = viewMoment || this.p.viewMoment const sign = dir < 0 ? -1 : 1 const abs = Math.abs(dir) const mom = this.toMoment(viewMoment) mom.add(sign, abs == 1 ? 'month' : 'year') return mom } onNavClick(dir, viewMoment, event) { const props = this.props let dateMoment = this.toMoment(viewMoment) if (props.onUpdate) { dateMoment = props.onUpdate(dateMoment, dir) } else { const sign = dir < 0 ? -1 : 1 const abs = Math.abs(dir) dateMoment.add(sign, abs == 1 ? 'month' : 'year') } const timestamp = +dateMoment props.onNavClick(dir, viewMoment, event) const disabled = dir < 0 ? props.prevDisabled : props.nextDisabled if (disabled) { return } this.onViewDateChange({ dateMoment, timestamp }) } renderNavDate(viewMoment) { const props = this.props const text = viewMoment.format(props.navDateFormat) if (props.renderNavDate) { return props.renderNavDate(viewMoment, text) } return text } onViewDateChange({ dateMoment, timestamp }) { if (this.props.viewDate === undefined) { this.setState({ viewDate: timestamp }) } if (this.props.onViewDateChange) { const dateString = dateMoment.format(this.props.dateFormat) this.props.onViewDateChange(dateString, { dateString, dateMoment, timestamp }) } } } NavBar.defaultProps = { arrows: {}, theme: 'default', isDatePickerNavBar: true, navDateFormat: 'MMM YYYY', enableHistoryView: true, onNavClick: (dir, viewMoment) => {}, onViewDateChange: () => {} } NavBar.propTypes = { secondary: PropTypes.bool, renderNav: PropTypes.func, renderNavPrev: PropTypes.func, renderNavNext: PropTypes.func, arrows: PropTypes.object, navDateFormat: PropTypes.string, onUpdate: PropTypes.func, onNavClick: PropTypes.func, onViewDateChange: PropTypes.func }