UNPKG

d2-ui

Version:
190 lines (156 loc) 4.18 kB
import React from 'react'; import ReactDOM from 'react-dom'; import StylePropable from './mixins/style-propable'; import getMuiTheme from './styles/getMuiTheme'; const rowsHeight = 24; const styles = { textarea: { width: '100%', resize: 'none', font: 'inherit', padding: 0, }, shadow: { width: '100%', resize: 'none', // Overflow also needed to here to remove the extra row // added to textareas in Firefox. overflow: 'hidden', // Visibility needed to hide the extra text area on ipads visibility: 'hidden', font: 'inherit', padding: 0, position: 'absolute', }, }; const EnhancedTextarea = React.createClass({ propTypes: { defaultValue: React.PropTypes.any, disabled: React.PropTypes.bool, onChange: React.PropTypes.func, onHeightChange: React.PropTypes.func, rows: React.PropTypes.number, rowsMax: React.PropTypes.number, /** * Override the inline-styles of the root element. */ style: React.PropTypes.object, textareaStyle: React.PropTypes.object, value: React.PropTypes.string, valueLink: React.PropTypes.object, }, contextTypes: { muiTheme: React.PropTypes.object, }, //for passing default theme context to children childContextTypes: { muiTheme: React.PropTypes.object, }, mixins: [ StylePropable, ], getDefaultProps() { return { rows: 1, }; }, getInitialState() { return { height: this.props.rows * rowsHeight, muiTheme: this.context.muiTheme || getMuiTheme(), }; }, getChildContext() { return { muiTheme: this.state.muiTheme, }; }, componentDidMount() { this._syncHeightWithShadow(); }, componentWillReceiveProps(nextProps, nextContext) { if (nextProps.value !== this.props.value) { this._syncHeightWithShadow(nextProps.value); } let newState = {}; newState.muiTheme = nextContext.muiTheme ? nextContext.muiTheme : this.state.muiTheme; }, getInputNode() { return ReactDOM.findDOMNode(this.refs.input); }, setValue(value) { this.getInputNode().value = value; this._syncHeightWithShadow(value); }, _syncHeightWithShadow(newValue, e) { let shadow = ReactDOM.findDOMNode(this.refs.shadow); if (newValue !== undefined) { shadow.value = newValue; } let newHeight = shadow.scrollHeight; if (this.props.rowsMax >= this.props.rows) { newHeight = Math.min(this.props.rowsMax * rowsHeight, newHeight); } newHeight = Math.max(newHeight, rowsHeight); if (this.state.height !== newHeight) { this.setState({ height: newHeight, }); if (this.props.onHeightChange) { this.props.onHeightChange(e, newHeight); } } }, _handleChange(e) { this._syncHeightWithShadow(e.target.value); if (this.props.hasOwnProperty('valueLink')) { this.props.valueLink.requestChange(e.target.value); } if (this.props.onChange) { this.props.onChange(e); } }, render() { let { onChange, onHeightChange, rows, style, textareaStyle, valueLink, ...other, } = this.props; const textareaStyles = this.mergeStyles(styles.textarea, textareaStyle, { height: this.state.height, }); const shadowStyles = styles.shadow; if (this.props.hasOwnProperty('valueLink')) { other.value = this.props.valueLink.value; } if (this.props.disabled) { style.cursor = 'default'; } return ( <div style={this.prepareStyles(this.props.style)}> <textarea ref="shadow" style={this.prepareStyles(shadowStyles)} tabIndex="-1" rows={this.props.rows} defaultValue={this.props.defaultValue} readOnly={true} value={this.props.value} valueLink={this.props.valueLink} /> <textarea {...other} ref="input" rows={this.props.rows} style={this.prepareStyles(textareaStyles)} onChange={this._handleChange} /> </div> ); }, }); export default EnhancedTextarea;