react-date-field
Version:
React DateField
356 lines (269 loc) • 7.16 kB
JavaScript
import React from 'react'
import { findDOMNode } from 'react-dom'
import Component from 'react-class'
import assign from 'object-assign'
import { Flex, Item } from 'react-flex'
import Input from 'react-field'
import toMoment from './toMoment'
const notEmpty = (s) => !!s
const join = (array) => array.filter(notEmpty).join(' ')
const getPicker = (props) => {
return React.Children.toArray(props.children).filter(c => c && c.props && c.props.isDatePicker)[0]
}
export default class DateField extends Component {
constructor(props){
super(props)
const picker = getPicker(props) || { props: {} }
const pickerProps = picker.props
const date = pickerProps.defaultDate !== undefined?
pickerProps.defaultDate:
new Date()
this.state = {
date,
expanded: props.defaultExpanded || false,
focused: false
}
}
render(){
const props = this.p = this.prepareProps(this.props)
return <Flex
inline
row
{...props}
onFocus={this.onFocus}
tabIndex={this.state.focused? -1: this.props.tabIndex || 0}
>
{this.renderInput()}
{this.renderClearIcon()}
{this.renderCalendarIcon()}
{this.renderDatePicker()}
</Flex>
}
renderInput(){
const props = this.p
const inputProps = this.prepareInputProps(props)
let input
if (props.renderInput){
input = props.renderInput(inputProps)
}
if (input === undefined){
input = <Input {...inputProps} />
}
return input
}
renderClearIcon(){
const props = this.p
if (!props.clearIcon){
return
}
const clearIcon = props.clearIcon === true?
'✖':
props.clearIcon
const clearIconProps = {
style: {
visibility: props.textValue? 'visible': 'hidden'
},
className: 'react-date-field__clear-icon',
onMouseDown: this.onClearMouseDown,
children: clearIcon
}
let result
if (props.renderClearIcon){
result = props.renderClearIcon(clearIconProps)
}
if (result === undefined){
result = <span {...clearIconProps} />
}
return result
}
onClearMouseDown(event){
event.preventDefault()
this.onPickerChange('', null, event)
}
renderCalendarIcon(){
let result
const renderIcon = this.props.renderCalendarIcon
const calendarIconProps = {
className: 'react-date-field__calendar-icon',
onMouseDown: this.onCalendarIconMouseDown,
children: <div className="react-date-field__calendar-icon-inner" />
}
if(renderIcon){
result = renderIcon(calendarIconProps)
}
if(result === undefined){
result = <div {...calendarIconProps} />
}
return result
}
onCalendarIconMouseDown(event){
if (this.state.focused){
event.preventDefault()
}
this.toggleExpand()
}
toggleExpand(){
this.setExpanded(!this.p.expanded)
}
prepareProps(thisProps){
const props = assign({}, thisProps)
props.expanded = props.expanded === undefined? this.state.expanded: props.expanded
const picker = getPicker(props) || { props: {} }
const pickerProps = picker.props
this.pickerProps = pickerProps
props.locale = pickerProps.locale
props.dateFormat = pickerProps.dateFormat
props.date = pickerProps.date !== undefined?
//we allow null for input date
pickerProps.date:
this.state.date
let date = props.date
if (date != null){
date = toMoment(props.date, props.displayFormat || props.dateFormat, {
strict: false,
locale: props.locale
})
if (!date.isValid() && props.displayFormat){
date = toMoment(props.date, props.dateFormat, {
strict: false,
locale: props.locale
})
}
}
props.date = date
props.textValue = date && date.isValid()?
date.format(props.displayFormat || props.dateFormat):
''
props.className = this.prepareClassName(props)
return props
}
prepareClassName(props){
return join([
'react-date-field',
props.className,
this.state.focused?
join([
'react-date-field--focused',
props.focusedClassName
]):
''
])
}
prepareInputProps(props){
const newInputProps = {
ref: 'field',
date: props.date,
onFocus: this.onFieldFocus,
onBlur: this.onFieldBlur,
onChange: this.onInputChange,
className: join([
'react-date-field__input',
props.inputProps && props.inputProps.className,
props.inputClassName
])
}
if (props.textValue !== undefined){
newInputProps.value = props.textValue
}
const inputProps = assign(
{},
props.defaultInputProps,
props.inputProps,
newInputProps
)
return inputProps
}
onFieldFocus(event){
if (this.state.focused){
return
}
this.setState({
focused: true
})
if (this.props.expandOnFocus){
this.setExpanded(true)
}
this.props.onFocus(event)
}
onFieldBlur(event){
this.setState({
focused: false
})
this.setExpanded(false)
this.props.onBlur(event)
}
renderDatePicker(){
const props = this.p
if (props.expanded){
const picker = getPicker(props)
if (!picker){
return
}
return React.cloneElement(picker, {
onMouseDown: (event) => {
event.preventDefault()
},
onChange: this.onPickerChange
})
}
}
onInputChange(value, event){
}
setExpanded(bool){
if (bool === this.p.expanded){
return
}
if (this.props.expanded === undefined){
this.setState({
expanded: bool
})
}
this.props.onExpandChange(bool)
}
onPickerChange(dateText, mom, event){
if (this.p.collapseOnChange){
this.setExpanded(false)
}
const pickerProps = this.pickerProps
if (pickerProps.date === undefined){
//the picker is uncontrolled, so also the datefield should be
//uncontrolled and reflect the changes
this.setState({
date: dateText
})
}
//call the onChange of the picker!
const args = [...arguments]
if (pickerProps.onChange){
pickerProps.onChange(...args)
}
}
onFocus(event){
if (this.state.focused){
return
}
this.focusField()
}
focusField(){
const input = findDOMNode(this.refs.field);
input.focus()
}
}
const emptyFn = () => {}
DateField.defaultProps = {
expandOnFocus: true,
collapseOnChange: true,
onChange: emptyFn,
onBlur: emptyFn,
onFocus: emptyFn,
clearIcon: true,
onExpandChange: () => {}
}
DateField.propTypes = {
children: function(props, propName){
const picker = React.Children.toArray(props.children).filter(c => c && c.props && c.props.isDatePicker)[0]
if (!picker){
return new Error('You should render a "DatePicker" child in the DateField component.')
}
}
}