Last active
January 12, 2017 02:29
-
-
Save eugene1g/5dbaa7d35d0c7d5c2c56 to your computer and use it in GitHub Desktop.
React component for auto-expanding textareas
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use strict"; | |
import React, {Component} from 'react'; | |
import calcContentHeight from './dom-textarea-measure'; | |
class AutogrowingTextarea extends Component { | |
constructor(props) { | |
this.props = props; | |
this.state = {}; | |
this.textareaProps = sanitizeChildProps(props); | |
} | |
render() { | |
this.textareaProps.style.height = this.state.height; | |
return <textarea {...this.textareaProps} onChange={this.onChange.bind(this)}/> | |
} | |
componentDidMount() { | |
this.resizeComponent(); | |
} | |
/** | |
* Update textrarea props in case this component is updated from the outside | |
*/ | |
componentWillReceiveProps(nextProps) { | |
this.textareaProps = sanitizeChildProps(nextProps); | |
this.resizeComponent(); | |
} | |
onChange(e) { | |
this.resizeComponent(); | |
var changeCallback = this.props.onChange, | |
valueLink = this.props.valueLink; | |
if (typeof valueLink == 'object' && typeof valueLink.requestChange == 'function') { | |
changeCallback = ev => valueLink.requestChange(ev.target.value); | |
} | |
if (changeCallback) changeCallback(e); | |
} | |
resizeComponent() { | |
var height = calcContentHeight(React.findDOMNode(this), this.textareaProps.value || this.textareaProps.defaultValue); | |
if (this.props.minHeight && this.props.minHeight > height) { | |
height = this.props.minHeight; | |
} | |
if (this.props.maxHeight && this.props.maxHeight < height) { | |
height = this.props.maxHeight; | |
} | |
this.setState({height}); | |
} | |
getValue() { | |
return React.findDOMNode(this).value; | |
} | |
focus() { | |
React.findDOMNode(this).focus(); | |
} | |
} | |
export default AutogrowingTextarea; | |
AutogrowingTextarea.displayName = 'TextareaAutosize'; | |
AutogrowingTextarea.propTypes = { | |
minHeight: React.PropTypes.number, | |
maxHeight: React.PropTypes.number | |
}; | |
AutogrowingTextarea.defaultProps = { | |
minHeight: 40 | |
}; | |
/** | |
* Removes propertie that are relevant only for this wrapper and should not be passed down to the actual textarea | |
*/ | |
function sanitizeChildProps(allProps) { | |
var childProps = objectWithout(allProps, ['valueLink', 'onChange', 'minHeight', 'maxHeight']); | |
if (typeof allProps.valueLink == 'object') childProps.value = allProps.valueLink.value; | |
if (typeof childProps.style != 'object') childProps.style = {}; | |
//required to hide overflow so that there is no scrollbar | |
childProps.style.overflow = 'hidden'; | |
return childProps; | |
} | |
function objectWithout(object, fieldNames) { | |
var copy = {}; | |
for (let field in object) { | |
if (fieldNames.indexOf(field) === -1) copy[field] = object[field]; | |
} | |
return copy; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use strict"; | |
var hiddenTextarea, | |
computedStyleCache = {}, | |
hiddenTextareaStyle = "height:0;visibility:hidden;overflow:hidden;position:absolute;z-index:-1000;top:0;right:0"; | |
export default function (uiTextNode, newContent) { | |
if (!hiddenTextarea) { | |
hiddenTextarea = document.createElement('textarea'); | |
document.body.appendChild(hiddenTextarea); | |
} | |
var {baseUiStyling, sumVerticalPaddings} = calcNodeStyling(uiTextNode); | |
hiddenTextarea.setAttribute('style', baseUiStyling + ";" + hiddenTextareaStyle); | |
hiddenTextarea.value = typeof newContent == "string" ? newContent : uiTextNode.value; | |
return (hiddenTextarea.scrollHeight - sumVerticalPaddings); | |
} | |
function calcNodeStyling(node) { | |
const nodeRef = node.getAttribute('data-reactid') || node.id || node.name; | |
if (!computedStyleCache[nodeRef]) { | |
let compStyle = window.getComputedStyle(node), | |
sumPaddings = 0, | |
stylesToCopy = ['line-height', 'padding-top', 'padding-bottom', 'font-size', 'width', 'padding-left', 'padding-right']; | |
// If the textarea is set to border-box, it's not necessary to | |
// subtract the padding. | |
if (compStyle.getPropertyValue('box-sizing') !== "border-box" && | |
compStyle.getPropertyValue('-moz-box-sizing') !== "border-box" && | |
compStyle.getPropertyValue('-webkit-box-sizing') !== "border-box") { | |
sumPaddings = ( | |
parseFloat(compStyle.getPropertyValue('padding-bottom')) + | |
parseFloat(compStyle.getPropertyValue('padding-top')) | |
); | |
} | |
computedStyleCache[nodeRef] = { | |
baseUiStyling: stylesToCopy.map(styleName => styleName + ':' + compStyle[styleName]).join(';'), | |
sumVerticalPaddings: sumPaddings | |
} | |
} | |
return computedStyleCache[nodeRef]; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment