backpack-ui
Version:
Lonely Planet's Components
151 lines (126 loc) • 3.7 kB
JSX
import React from "react";
import PropTypes from "prop-types";
import radium from "radium";
import propTypes from "../../utils/propTypes";
import Input from "../input";
import { validReactAttributes } from "../../utils/validReactAttributes";
const styles = Object.assign({}, Input.styles, {
resize: "vertical",
});
class Textarea extends React.Component {
constructor(props) {
super(props);
this.state = {
height: Input.height,
hideOverflow: true,
currentValue: props.value,
};
this.onInput = this.onInput.bind(this);
this.disableEnterKey = this.disableEnterKey.bind(this);
this.autoGrow = this.autoGrow.bind(this);
this.handleChange = this.handleChange.bind(this);
}
componentDidMount() {
if (this.props.autofocus) {
this.textarea.focus();
}
if (this.props.autogrow) {
this.autoGrow();
}
if (this.props.disableEnter && this.props.autogrow) {
document.addEventListener("keydown", this.disableEnterKey);
}
}
shouldComponentUpdate(nextProps, nextState) { // eslint-disable-line class-methods-use-this
return nextState.height !== nextState.maxHeight;
}
componentDidUpdate() {
if (this.props.disableEnter && this.props.autogrow) {
document.addEventListener("keydown", this.disableEnterKey);
} else {
document.removeEventListener("keydown", this.disableEnterKey);
}
}
componentWillUnmount() {
if (this.props.autofocus) {
this.textarea.blur();
}
if (this.props.disableEnter && this.props.autogrow) {
document.removeEventListener("keydown", this.disableEnterKey);
}
}
onInput() {
if (this.props.onInput && typeof this.props.onInput === "function") {
this.props.onInput();
}
if (this.props.autogrow) {
this.autoGrow();
}
}
disableEnterKey(event) {
const hasFocus = (this.textarea === document.activeElement) || false;
if (hasFocus && event.keyCode === 13) {
event.preventDefault();
}
}
handleChange(e) {
if (this.props.onChange && typeof this.props.onChange === "function") {
this.props.onChange(e);
}
this.setState({ currentValue: e.target.value });
}
autoGrow() {
const maxHeight = (this.props.maxLines * Input.lineHeight) + Input.height;
this.textarea.style.height = Input.styles.height;
this.setState({
height: Math.min(this.textarea.scrollHeight, maxHeight),
hideOverflow: Math.min(this.textarea.scrollHeight, maxHeight) < maxHeight,
}, () => {
this.textarea.style.height = `${this.state.height}px`;
this.textarea.style.overflow = this.state.hideOverflow ? "hidden" : null;
});
}
render() {
const sanitizedProps = validReactAttributes(this.props);
return (
<textarea
{...sanitizedProps}
value={this.state.currentValue}
ref={node => { this.textarea = node; }}
onInput={this.onInput}
onChange={this.handleChange}
style={[
styles,
!this.props.autogrow && {
height: "auto",
},
this.props.autogrow && {
overflow: "hidden",
resize: "none",
},
this.props.style,
]}
/>
);
}
}
Textarea.propTypes = {
autogrow: PropTypes.bool,
autofocus: PropTypes.bool,
maxLines: PropTypes.number,
onInput: PropTypes.func,
disableEnter: PropTypes.bool,
style: propTypes.style,
onChange: PropTypes.func,
value: PropTypes.string,
};
Textarea.defaultProps = {
autogrow: false,
autofocus: false,
maxLines: 3,
onInput: null,
disableEnter: false,
style: null,
onChange: null,
};
export default radium(Textarea);