-
-
Notifications
You must be signed in to change notification settings - Fork 32.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[DatePicker] Add ability to use keyboard to write date #5677
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import React from 'react'; | ||
import DatePicker from 'material-ui/DatePicker'; | ||
|
||
/** | ||
* This example allows you to use keyboard to write date. | ||
* | ||
* Unfortunately by now it is only working with default locale (`en-US`). | ||
* In other cases `keyboardEnabled` prop will be ignored. | ||
* Also it will not work if it is controlled component or has disabled dates. | ||
*/ | ||
const DatePickerExampleKeyboardInput = () => ( | ||
<div> | ||
<DatePicker | ||
hintText="YYYY-MM-DD" | ||
keyboardEnabled={true} | ||
/> | ||
</div> | ||
); | ||
|
||
export default DatePickerExampleKeyboardInput; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,18 @@ import React, {Component, PropTypes} from 'react'; | |
import {dateTimeFormat, formatIso, isEqualDate} from './dateUtils'; | ||
import DatePickerDialog from './DatePickerDialog'; | ||
import TextField from '../TextField'; | ||
import IconButton from '../IconButton'; | ||
import DateRangeIcon from '../svg-icons/action/date-range'; | ||
|
||
export function getStyles() { | ||
const styles = { | ||
iconButtonStyle: { | ||
verticalAlign: 'top', | ||
}, | ||
}; | ||
|
||
return styles; | ||
} | ||
|
||
class DatePicker extends Component { | ||
static propTypes = { | ||
|
@@ -50,6 +62,10 @@ class DatePicker extends Component { | |
* Disables the DatePicker. | ||
*/ | ||
disabled: PropTypes.bool, | ||
/** | ||
* Text for validation error. | ||
*/ | ||
errorText: PropTypes.string, | ||
/** | ||
* Used to change the first day of week. It varies from | ||
* Saturday to Monday between different locales. | ||
|
@@ -65,6 +81,15 @@ class DatePicker extends Component { | |
* @returns {any} The formatted date. | ||
*/ | ||
formatDate: PropTypes.func, | ||
/** | ||
* Override the inline-styles of calendar IconButton (it is showing when `keyboardEnabled` is `true`). | ||
*/ | ||
iconButtonStyle: PropTypes.object, | ||
/** | ||
* Tells the datepicker to handle keyboard input. | ||
* Will not work if `locale` or `shouldDisableDate` props are specified or if it is controlled component. | ||
*/ | ||
keyboardEnabled: PropTypes.bool, | ||
/** | ||
* Locale used for formatting the `DatePicker` date strings. Other than for 'en-US', you | ||
* must provide a `DateTimeFormat` that supports the chosen `locale`. | ||
|
@@ -140,7 +165,9 @@ class DatePicker extends Component { | |
container: 'dialog', | ||
disabled: false, | ||
disableYearSelection: false, | ||
errorText: 'Enter a valid date in "YYYY-MM-DD" format', | ||
firstDayOfWeek: 1, | ||
keyboardEnabled: false, | ||
style: {}, | ||
}; | ||
|
||
|
@@ -150,11 +177,15 @@ class DatePicker extends Component { | |
|
||
state = { | ||
date: undefined, | ||
hasError: false, | ||
textDate: '', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's the use case for this new state? |
||
}; | ||
|
||
componentWillMount() { | ||
const defaultDate = this.isControlled() ? this.getControlledDate() : this.props.defaultDate; | ||
this.setState({ | ||
date: this.isControlled() ? this.getControlledDate() : this.props.defaultDate, | ||
date: defaultDate, | ||
textDate: defaultDate ? this.formatDate(defaultDate) : '', | ||
}); | ||
} | ||
|
||
|
@@ -164,6 +195,7 @@ class DatePicker extends Component { | |
if (!isEqualDate(this.state.date, newDate)) { | ||
this.setState({ | ||
date: newDate, | ||
textDate: this.formatDate(newDate), | ||
}); | ||
} | ||
} | ||
|
@@ -200,10 +232,18 @@ class DatePicker extends Component { | |
this.openDialog(); | ||
} | ||
|
||
shouldHandleKeyboard() { | ||
return this.props.keyboardEnabled && !this.props.disabled && | ||
!this.props.locale && !this.props.shouldDisableDate && | ||
!this.isControlled(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm worried about this feature being disabled for controlled component. |
||
} | ||
|
||
|
||
handleAccept = (date) => { | ||
if (!this.isControlled()) { | ||
this.setState({ | ||
date: date, | ||
textDate: this.formatDate(date), | ||
}); | ||
} | ||
if (this.props.onChange) { | ||
|
@@ -212,12 +252,37 @@ class DatePicker extends Component { | |
}; | ||
|
||
handleFocus = (event) => { | ||
event.target.blur(); | ||
if (!this.shouldHandleKeyboard()) { | ||
event.target.blur(); | ||
} | ||
|
||
if (this.props.onFocus) { | ||
this.props.onFocus(event); | ||
} | ||
}; | ||
|
||
handleBlur = () => { | ||
if (this.shouldHandleKeyboard()) { | ||
if (this.getDate()) { | ||
this.setState({ | ||
textDate: this.formatDate(this.getDate()), | ||
}); | ||
} | ||
|
||
this.setState({ | ||
hasError: !this.getDate() && !!this.state.textDate.trim(), | ||
}); | ||
} | ||
} | ||
|
||
handleTextChange = (event, value) => { | ||
const parsedDate = this.parseDate(value); | ||
this.setState({ | ||
date: parsedDate, | ||
textDate: value, | ||
}); | ||
}; | ||
|
||
handleTouchTap = (event) => { | ||
if (this.props.onTouchTap) { | ||
this.props.onTouchTap(event); | ||
|
@@ -241,7 +306,9 @@ class DatePicker extends Component { | |
} | ||
|
||
formatDate = (date) => { | ||
if (this.props.locale) { | ||
if (this.props.formatDate) { | ||
return this.props.formatDate(date); | ||
} else if (this.props.locale) { | ||
const DateTimeFormat = this.props.DateTimeFormat || dateTimeFormat; | ||
return new DateTimeFormat(this.props.locale, { | ||
day: 'numeric', | ||
|
@@ -253,6 +320,21 @@ class DatePicker extends Component { | |
} | ||
}; | ||
|
||
parseDate = (textDate) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be great to allow users using a different date format. |
||
// regex to parse date is based on ISO 8601 (YYYY-MM-DD) | ||
const reg = /^(\d{4})-(\d{2})-(\d{2})$/; | ||
const match = reg.exec(textDate); | ||
if (!match) { | ||
return; | ||
} | ||
|
||
const year = match[1]; | ||
const month = match[2] === 0 ? 11 : (match[2] - 1); | ||
const day = match[3]; | ||
|
||
return new Date(year, month, day); | ||
}; | ||
|
||
render() { | ||
const { | ||
DateTimeFormat, | ||
|
@@ -264,7 +346,8 @@ class DatePicker extends Component { | |
dialogContainerStyle, | ||
disableYearSelection, | ||
firstDayOfWeek, | ||
formatDate: formatDateProp, | ||
formatDate, // eslint-disable-line no-unused-vars | ||
iconButtonStyle, | ||
locale, | ||
maxDate, | ||
minDate, | ||
|
@@ -277,22 +360,36 @@ class DatePicker extends Component { | |
shouldDisableDate, | ||
style, | ||
textFieldStyle, | ||
keyboardEnabled, // eslint-disable-line no-unused-vars | ||
errorText, | ||
...other | ||
} = this.props; | ||
|
||
const {prepareStyles} = this.context.muiTheme; | ||
const formatDate = formatDateProp || this.formatDate; | ||
const styles = getStyles(this.props, this.context); | ||
|
||
return ( | ||
<div className={className} style={prepareStyles(Object.assign({}, style))}> | ||
<TextField | ||
{...other} | ||
onBlur={this.handleBlur} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same, that's a breaking change. |
||
onChange={this.handleTextChange} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same, that's a breaking change. |
||
onFocus={this.handleFocus} | ||
onTouchTap={this.handleTouchTap} | ||
onTouchTap={!this.shouldHandleKeyboard() && this.handleTouchTap} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same, that's a breaking change. |
||
ref="input" | ||
style={textFieldStyle} | ||
value={this.state.date ? formatDate(this.state.date) : ''} | ||
errorText={this.state.hasError && errorText} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's a breaking change as users might be using this |
||
value={this.state.textDate} | ||
/> | ||
{this.shouldHandleKeyboard() && | ||
<IconButton | ||
onTouchTap={this.handleTouchTap} | ||
style={Object.assign({}, styles.iconButtonStyle, iconButtonStyle)} | ||
touch={true} | ||
> | ||
<DateRangeIcon /> | ||
</IconButton> | ||
} | ||
<DatePickerDialog | ||
DateTimeFormat={DateTimeFormat} | ||
autoOk={autoOk} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why exporting this function?