diff --git a/src/Form/Input.js b/src/Form/Input.js
index 67008ca..5eba758 100644
--- a/src/Form/Input.js
+++ b/src/Form/Input.js
@@ -52,6 +52,15 @@ const StyledInput = createComponent({
const StyledTextArea = StyledInput.withComponent('textarea');
+const AutogrowShadow = createComponent({
+ name: 'AutogrowShadow',
+ tag: 'textarea',
+ style: css`
+ position: absolute;
+ left: -9999px;
+ `,
+});
+
const validateValueProp = (props, propName, componentName) => {
if (props.type === 'number' && typeof props[propName] !== 'number') {
return new Error(`Invalid prop ${propName} supplied to ${componentName} with type="number", expected Number`);
@@ -77,6 +86,7 @@ export default class Input extends Component {
rows: PropTypes.number,
maxRows: PropTypes.number,
rowHeight: PropTypes.number,
+ lineHeight: PropTypes.number,
autogrow: PropTypes.bool,
size: PropTypes.string,
floating: PropTypes.bool,
@@ -89,7 +99,8 @@ export default class Input extends Component {
minRows: 2,
rows: 3,
maxRows: 6,
- rowHeight: 14 * 1.5,
+ rowHeight: 14,
+ lineHeight: 1.5,
autogrow: false,
disabled: false,
size: 'md',
@@ -99,8 +110,16 @@ export default class Input extends Component {
floating: false,
};
+ static getDerivedStateFromProps(props, state) {
+ if (props.value !== undefined && props.value !== state.value) {
+ return {
+ value: props.value,
+ };
+ }
+ return null;
+ }
+
state = {
- value: this.props.value || '',
focused: false,
};
@@ -111,33 +130,27 @@ export default class Input extends Component {
}
componentDidMount() {
- if (this.props.autogrow) {
- this.createShadowElement();
- this.autogrow();
- }
-
- if (this.props.autofocus) {
- if (this.ref.current) {
- this.ref.current.focus();
- }
+ if (this.props.autofocus && this.ref.current) {
+ this.ref.current.focus();
}
- }
- componentWillReceiveProps(props) {
- if (props.value !== this.props.value) {
- this.setState({ value: props.value });
+ if (this.props.multiline) {
+ /* eslint-disable-next-line react/no-did-mount-set-state */
+ this.setState({
+ height: this.props.rows * this.props.rowHeight * this.props.lineHeight,
+ });
}
}
- componentDidUpdate(prevProps) {
- if (prevProps.value !== this.props.value) {
+ componentDidUpdate(oldProps, oldState) {
+ if (oldState.value !== this.state.value) {
this.autogrow();
}
}
componentWillUnmount() {
- if (this.shadow) {
- this.shadow.remove();
+ if (this.autogrowShadowNode && this.autogrowShadowNode.parentNode) {
+ this.autogrowShadowNode.parentNode.removeChild(this.autogrowShadowNode);
}
}
@@ -163,30 +176,30 @@ export default class Input extends Component {
};
onChange = e => {
- this.autogrow();
this.setState({ value: e.target.value });
this.props.onChange(e.target.name, e.target.value);
};
- createShadowElement() {
- this.shadow = document.createElement('textarea');
- this.shadow.style.position = 'absolute';
- this.shadow.style.left = '-9000px';
- document.body.appendChild(this.shadow);
- }
+ handleAutogrowRef = node => {
+ this.autogrowShadowNode = node;
+ this.autogrow();
+ };
autogrow() {
- const { multiline, autogrow, minRows, maxRows, rowHeight } = this.props;
- if (!multiline || !autogrow) {
+ if (!this.props.autogrow || !this.autogrowShadowNode) {
return;
}
- const minHeight = minRows * rowHeight;
- const maxHeight = maxRows * rowHeight;
+ const { minRows, maxRows, rowHeight, lineHeight } = this.props;
+
+ const minHeight = minRows * rowHeight * lineHeight;
+ const maxHeight = maxRows * rowHeight * lineHeight;
+
+ this.autogrowShadowNode.style.width = `${this.ref.current.clientWidth}px`;
+ this.autogrowShadowNode.value = this.state.value;
+
+ let height = this.autogrowShadowNode.scrollHeight + rowHeight * lineHeight;
- this.shadow.style.width = `${this.ref.current.clientWidth}px`;
- this.shadow.value = this.props.value || this.state.value;
- let height = this.shadow.scrollHeight + 14;
if (height < minHeight) {
height = minHeight;
} else if (height > maxHeight) {
@@ -235,7 +248,7 @@ export default class Input extends Component {
onChange: this.onChange,
onFocus: this.onFocus,
onBlur: this.onBlur,
- style: autogrow ? { ...style, height: this.state.height } : style,
+ style: multiline ? { ...style, height: this.state.height } : style,
placeholder,
isFloatable: floating,
isFloating,
@@ -257,6 +270,8 @@ export default class Input extends Component {
{multiline ? : }
+ {autogrow && }
+
{!this.state.focused && error ? {error} : null}
);
diff --git a/src/Form/Input.mdx b/src/Form/Input.mdx
index 84ffcb4..97d9d5f 100644
--- a/src/Form/Input.mdx
+++ b/src/Form/Input.mdx
@@ -24,12 +24,12 @@ Form input component. Different sizes are available.
### Multiline Input
-
+
### Autogrow Multiline Input
-
+
### Floating Input