Merge branch 'multiline-maxlength-fix' of https://github.com/SimonSelg/react-toolbox into SimonSelg-multiline-maxlength-fix

* 'multiline-maxlength-fix' of https://github.com/SimonSelg/react-toolbox:
  feat(dist): add newest build
  doc(input): add note about handleChange implementation
  fix(input): don't remove the maxLenght attribute from inputs
  feat(input): prevent insertion of characters if maxLength is reached
  fix(input): always recalculate size
  feat(input): set the default row amount for multiline inputs to 1
  feat(input): implement maxLength in js rather then using the maxlength prop of the input field
  refactor(input): move autoresize to didUpdate
  fix(input): only remove resize event listerner if it exists
  docs(input): fix multiline example and add maxLength to it
  fix(multiline input): autoresize after initial render too
old
Javi Velasco 2016-08-06 18:25:17 +02:00
commit beb0ca4465
3 changed files with 114 additions and 22 deletions

View File

@ -57,6 +57,7 @@ const factory = (FontIcon) => {
componentDidMount () {
if (this.props.multiline) {
window.addEventListener('resize', this.handleAutoresize);
this.handleAutoresize();
}
}
@ -68,15 +69,27 @@ const factory = (FontIcon) => {
}
}
componentDidUpdate () {
// resize the textarea, if nessesary
if (this.props.multiline) this.handleAutoresize();
}
componentWillUnmount () {
window.removeEventListener('resize', this.handleAutoresize);
if (this.props.multiline) window.removeEventListener('resize', this.handleAutoresize);
}
handleChange = (event) => {
if (this.props.multiline) {
this.handleAutoresize();
}
if (this.props.onChange) this.props.onChange(event.target.value, event);
const { onChange, multiline, maxLength } = this.props;
const valueFromEvent = event.target.value;
// Trim value to maxLength if that exists (only on multiline inputs).
// Note that this is still required even tho we have the onKeyPress filter
// because the user could paste smt in the textarea.
const haveToTrim = (multiline && maxLength && event.target.value.length > maxLength);
const value = haveToTrim ? valueFromEvent.substr(0, maxLength) : valueFromEvent;
// propagate to to store and therefore to the input
if (onChange) onChange(value, event);
};
handleAutoresize = () => {
@ -100,10 +113,31 @@ const factory = (FontIcon) => {
this.refs.input.focus();
}
onKeyPress = (event) => {
// prevent insertion of more characters if we're a multiline input
// and maxLength exists
const { multiline, maxLength, onKeyPress } = this.props;
if (multiline && maxLength) {
// check if smt is selected, in which case the newly added charcter would
// replace the selected characters, so the length of value doesn't actually
// increase.
const isReplacing = event.target.selectionEnd - event.target.selectionStart;
const value = event.target.value;
if (!isReplacing && value.length === maxLength) {
event.preventDefault();
event.stopPropagation();
return;
}
}
if (onKeyPress) onKeyPress(event);
}
render () {
const { children, disabled, error, floating, hint, icon,
name, label: labelText, maxLength, multiline, required,
theme, type, value, ...others} = this.props;
theme, type, value, onKeyPress, ...others} = this.props;
const length = maxLength && value ? value.length : 0;
const labelClassName = classnames(theme.label, {[theme.fixed]: !floating});
@ -119,7 +153,7 @@ const factory = (FontIcon) => {
&& value !== ''
&& !(typeof value === Number && isNaN(value));
const InputElement = React.createElement(multiline ? 'textarea' : 'input', {
const inputElementProps = {
...others,
className: classnames(theme.inputElement, {[theme.filled]: valuePresent}),
onChange: this.handleChange,
@ -129,9 +163,17 @@ const factory = (FontIcon) => {
disabled,
required,
type,
value,
maxLength
});
value
};
if (!multiline) {
inputElementProps.maxLength = maxLength;
inputElementProps.onKeyPress = onKeyPress;
} else {
inputElementProps.rows = 1;
inputElementProps.onKeyPress = this.onKeyPress;
}
const InputElement = React.createElement(multiline ? 'textarea' : 'input', inputElementProps);
return (
<div data-react-toolbox='input' className={className}>

View File

@ -8,9 +8,9 @@ class InputTest extends React.Component {
render () {
return (
<section>
<Input type='text' label='Name' name='name' value={this.state.name} onChange={this.handleChange.bind(this, 'name')} maxLength={16 } />
<Input type='text' label='Name' name='name' value={this.state.name} onChange={this.handleChange.bind(this, 'name')} maxLength={16} />
<Input type='text' label='Disabled field' disabled />
<Input type='text' multiline label='Multiline' onChange={this.handleChange.bind(this, 'multiline')} />
<Input type='text' multiline label='Multiline' maxLength={20} value={this.state.multiline} onChange={this.handleChange.bind(this, 'multiline')} />
<Input type='email' label='Email address' icon='email' value={this.state.email} onChange={this.handleChange.bind(this, 'email')} />
<Input type='tel' label='Phone' name='phone' icon='phone' value={this.state.phone} onChange={this.handleChange.bind(this, 'phone')} />
<Input type='text' value={this.state.hint} label='Required Field' hint='With Hint' required onChange={this.handleChange.bind(this, 'hint')} icon={<span>J</span>} />

View File

@ -55,10 +55,21 @@ var factory = function factory(FontIcon) {
}
return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_Object$getPrototypeO = Object.getPrototypeOf(Input)).call.apply(_Object$getPrototypeO, [this].concat(args))), _this), _this.handleChange = function (event) {
if (_this.props.multiline) {
_this.handleAutoresize();
}
if (_this.props.onChange) _this.props.onChange(event.target.value, event);
var _this$props = _this.props;
var onChange = _this$props.onChange;
var multiline = _this$props.multiline;
var maxLength = _this$props.maxLength;
var valueFromEvent = event.target.value;
// Trim value to maxLength if that exists (only on multiline inputs).
// Note that this is still required even tho we have the onKeyPress filter
// because the user could paste smt in the textarea.
var haveToTrim = multiline && maxLength && event.target.value.length > maxLength;
var value = haveToTrim ? valueFromEvent.substr(0, maxLength) : valueFromEvent;
// propagate to to store and therefore to the input
if (onChange) onChange(value, event);
}, _this.handleAutoresize = function () {
var element = _this.refs.input;
// compute the height difference between inner height and outer height
@ -68,6 +79,29 @@ var factory = function factory(FontIcon) {
// resize the input to its content size
element.style.height = 'auto';
element.style.height = element.scrollHeight + heightOffset + 'px';
}, _this.onKeyPress = function (event) {
// prevent insertion of more characters if we're a multiline input
// and maxLength exists
var _this$props2 = _this.props;
var multiline = _this$props2.multiline;
var maxLength = _this$props2.maxLength;
var onKeyPress = _this$props2.onKeyPress;
if (multiline && maxLength) {
// check if smt is selected, in which case the newly added charcter would
// replace the selected characters, so the length of value doesn't actually
// increase.
var isReplacing = event.target.selectionEnd - event.target.selectionStart;
var value = event.target.value;
if (!isReplacing && value.length === maxLength) {
event.preventDefault();
event.stopPropagation();
return;
}
}
if (onKeyPress) onKeyPress(event);
}, _temp), _possibleConstructorReturn(_this, _ret);
}
@ -76,6 +110,7 @@ var factory = function factory(FontIcon) {
value: function componentDidMount() {
if (this.props.multiline) {
window.addEventListener('resize', this.handleAutoresize);
this.handleAutoresize();
}
}
}, {
@ -87,10 +122,16 @@ var factory = function factory(FontIcon) {
window.removeEventListener('resize', this.handleAutoresize);
}
}
}, {
key: 'componentDidUpdate',
value: function componentDidUpdate() {
// resize the textarea, if nessesary
if (this.props.multiline) this.handleAutoresize();
}
}, {
key: 'componentWillUnmount',
value: function componentWillUnmount() {
window.removeEventListener('resize', this.handleAutoresize);
if (this.props.multiline) window.removeEventListener('resize', this.handleAutoresize);
}
}, {
key: 'blur',
@ -122,8 +163,9 @@ var factory = function factory(FontIcon) {
var theme = _props.theme;
var type = _props.type;
var value = _props.value;
var onKeyPress = _props.onKeyPress;
var others = _objectWithoutProperties(_props, ['children', 'disabled', 'error', 'floating', 'hint', 'icon', 'name', 'label', 'maxLength', 'multiline', 'required', 'theme', 'type', 'value']);
var others = _objectWithoutProperties(_props, ['children', 'disabled', 'error', 'floating', 'hint', 'icon', 'name', 'label', 'maxLength', 'multiline', 'required', 'theme', 'type', 'value', 'onKeyPress']);
var length = maxLength && value ? value.length : 0;
var labelClassName = (0, _classnames5.default)(theme.label, _defineProperty({}, theme.fixed, !floating));
@ -132,7 +174,7 @@ var factory = function factory(FontIcon) {
var valuePresent = value !== null && value !== undefined && value !== '' && !((typeof value === 'undefined' ? 'undefined' : _typeof(value)) === Number && isNaN(value));
var InputElement = _react2.default.createElement(multiline ? 'textarea' : 'input', _extends({}, others, {
var inputElementProps = _extends({}, others, {
className: (0, _classnames5.default)(theme.inputElement, _defineProperty({}, theme.filled, valuePresent)),
onChange: this.handleChange,
ref: 'input',
@ -141,9 +183,17 @@ var factory = function factory(FontIcon) {
disabled: disabled,
required: required,
type: type,
value: value,
maxLength: maxLength
}));
value: value
});
if (!multiline) {
inputElementProps.maxLength = maxLength;
inputElementProps.onKeyPress = onKeyPress;
} else {
inputElementProps.rows = 1;
inputElementProps.onKeyPress = this.onKeyPress;
}
var InputElement = _react2.default.createElement(multiline ? 'textarea' : 'input', inputElementProps);
return _react2.default.createElement(
'div',