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
fixes plotly/dash-core_components#169 #277
Changes from 10 commits
2c59df5
1db86aa
44e4eea
71dc849
5c364f5
c3b3f06
d552406
ca10d31
099dac4
18b2846
ff47e95
110de52
0d1274b
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 |
---|---|---|
|
@@ -2,6 +2,21 @@ import React, {Component} from 'react'; | |
import PropTypes from 'prop-types'; | ||
import {omit} from 'ramda'; | ||
|
||
/** | ||
* A function to handle the formating of the `value` prop of the react `input`. | ||
* @param {string} val the `value` prop of the react `input` component. | ||
* @param {string} type the `type` prop of the react `input` component. | ||
* @returns {string} a value formated as a number if `type` is equal to `number` | ||
*/ | ||
function formatValue(val, type) { | ||
if (!val === '') { | ||
if (type === 'number') { | ||
return Number(val); | ||
} | ||
} | ||
return val; | ||
} | ||
|
||
/** | ||
* A basic HTML input control for entering text, numbers, or passwords. | ||
* | ||
|
@@ -12,39 +27,82 @@ import {omit} from 'ramda'; | |
export default class Input extends Component { | ||
constructor(props) { | ||
super(props); | ||
this.state = {value: props.value}; | ||
this.state = { | ||
value: formatValue(props.value, props.type), | ||
}; | ||
} | ||
|
||
componentWillReceiveProps(nextProps) { | ||
this.setState({value: nextProps.value}); | ||
this.setState({value: formatValue(nextProps.value, nextProps.type)}); | ||
} | ||
|
||
render() { | ||
const { | ||
fireEvent, | ||
setProps, | ||
type | ||
} = this.props; | ||
const {fireEvent, setProps, type, updatemode} = this.props; | ||
|
||
const {value} = this.state; | ||
return ( | ||
<input | ||
onBlur={() => { | ||
const newValue = formatValue(this.state.value, type); | ||
if (updatemode === 'blur') { | ||
this.setState({value: newValue}); | ||
if (setProps) { | ||
setProps({value: newValue}); | ||
} | ||
if (fireEvent) { | ||
fireEvent({event: 'blur'}); | ||
} | ||
} | ||
}} | ||
onChange={e => { | ||
this.setState({value: e.target.value}); | ||
if (setProps) { | ||
if (type === 'number') { | ||
setProps({value: Number(e.target.value)}); | ||
const newValue = formatValue(e.target.value, type); | ||
this.setState({value: newValue}); | ||
if (updatemode === 'keypress') { | ||
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. Similarly here - this |
||
if (setProps) { | ||
setProps({value: newValue}); | ||
} | ||
} | ||
if (fireEvent) { | ||
fireEvent({event: 'change'}); | ||
} | ||
}} | ||
onKeyPress={e => { | ||
if (type === 'number') { | ||
const curVal = this.state.value; | ||
const keyCode = e.keyCode || e.which; | ||
const keyValue = String.fromCharCode(keyCode); | ||
const unicodePlus = 43; | ||
const unicodeNine = 57; | ||
if ( | ||
keyCode <= unicodePlus || | ||
keyCode > unicodeNine || | ||
keyValue === '/' | ||
) { | ||
e.preventDefault(); | ||
} | ||
else { | ||
setProps({value: e.target.value}); | ||
if (keyValue === '-' && curVal.length > 0) { | ||
e.preventDefault(); | ||
} | ||
if ( | ||
(keyValue === '.' || keyValue === ',') && | ||
/[.]|,/.test(curVal.value) | ||
) { | ||
e.preventDefault(); | ||
} | ||
} | ||
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. Could you explain a bit about what this code is supposed to do? Is it really necessary? 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. This rejects the |
||
if (fireEvent) {fireEvent({event: 'change'});} | ||
}} | ||
onBlur={() => { | ||
if (fireEvent) {fireEvent({event: 'blur'});} | ||
onClick={e => { | ||
const newValue = formatValue(e.target.value, type); | ||
this.setState({value: newValue}); | ||
if (setProps) { | ||
setProps({value: newValue}); | ||
} | ||
}} | ||
value={value} | ||
{...omit(['fireEvent', 'setProps', 'value'], this.props)} | ||
{...omit( | ||
['fireEvent', 'setProps', 'value', 'updatemode'], | ||
this.props | ||
)} | ||
/> | ||
); | ||
} | ||
|
@@ -78,7 +136,15 @@ Input.propTypes = { | |
*/ | ||
type: PropTypes.oneOf([ | ||
// Only allowing the input types with wide browser compatability | ||
'text', 'number', 'password', 'email', 'range', 'search', 'tel', 'url', 'hidden' | ||
'text', | ||
'number', | ||
'password', | ||
'email', | ||
'range', | ||
'search', | ||
'tel', | ||
'url', | ||
'hidden', | ||
]), | ||
|
||
/** | ||
|
@@ -150,7 +216,7 @@ Input.propTypes = { | |
/** | ||
* URL input. Use type="url" if possible instead. | ||
*/ | ||
'url' | ||
'url', | ||
]), | ||
|
||
/** | ||
|
@@ -173,7 +239,6 @@ Input.propTypes = { | |
*/ | ||
maxlength: PropTypes.string, | ||
|
||
|
||
/** | ||
* The minimum (numeric or date-time) value for this item, which must not be greater than its maximum (max attribute) value. | ||
*/ | ||
|
@@ -244,6 +309,25 @@ Input.propTypes = { | |
*/ | ||
step: PropTypes.string, | ||
|
||
/** | ||
* Sets the number of important digits. It can be the string any or a positive floating point number. If this attribute is not set to any, the default is zero. | ||
*/ | ||
precision: PropTypes.string, | ||
|
||
/** | ||
* Determines when the component should update | ||
* its value. If `keypress`, then the input | ||
* will trigger its value when the user presses on | ||
* a key while the selector is active inside the input. | ||
* If `blur`, then the input will trigger its value when | ||
* the user click outside the input zone or press tab. | ||
* If `enter` ... | ||
*/ | ||
updatemode: PropTypes.oneOfType([ | ||
PropTypes.oneOf(['keypress', 'blur', 'enter']), | ||
PropTypes.arrayOf(PropTypes.oneOf(['keypress', 'blur', 'enter'])), | ||
]), | ||
|
||
/** | ||
* Dash-assigned callback that gets fired when the input changes. | ||
*/ | ||
|
@@ -254,5 +338,10 @@ Input.propTypes = { | |
*/ | ||
setProps: PropTypes.func, | ||
|
||
dashEvents: PropTypes.oneOf(['blur', 'change']) | ||
dashEvents: PropTypes.oneOf(['blur', 'change']), | ||
}; | ||
|
||
Input.defaultProps = { | ||
type: 'text', | ||
updatemode: 'keypress', | ||
}; |
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.
Here, if
updatemode
is an array (i.e.['blur', 'enter']
) this will not trigger. The best way to do this would probably be to first see ifupdatemode
is an array (you could use Ramda'sis
function for that, something likeR.is(Array, updatemode)
) and then check if that array containsblur
(Ramda has a contains function that can do that easily)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.
Or maybe even
if (updatemode === 'blur' || R.contains('blur', updatemode)) {}
will work!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.
actually I implemented with
updatemode
beingblur
(update when pressing tab or clicking away) orkeypress
(update at everykeystrokes), I wasn't sure the if we really need to have['blur', 'enter']
but I kept it in theInput.Props