Skip to content

Commit

Permalink
autoGrow for RN TextInput
Browse files Browse the repository at this point in the history
Reviewed By: sahrens

Differential Revision: D5527855

fbshipit-source-id: 1dad11851495a0b8b432903537a5a281840dc681
  • Loading branch information
sumkit authored and facebook-github-bot committed Aug 11, 2017
1 parent 64e9b24 commit 21b1ed3
Showing 1 changed file with 39 additions and 5 deletions.
44 changes: 39 additions & 5 deletions Libraries/Components/TextInput/TextInput.js
Expand Up @@ -168,6 +168,7 @@ const DataDetectorTypes = [
* or control this param programmatically with native code.
*
*/

// $FlowFixMe(>=0.41.0)
const TextInput = createReactClass({
displayName: 'TextInput',
Expand Down Expand Up @@ -207,6 +208,12 @@ const TextInput = createReactClass({
* The default value is `false`.
*/
autoFocus: PropTypes.bool,
/**
* If true, will increase the height of the textbox if need be. If false,
* the textbox will become scrollable once the height is reached. The
* default value is false.
*/
autoGrow: PropTypes.bool,
/**
* If `false`, text is not editable. The default value is `true`.
*/
Expand Down Expand Up @@ -306,6 +313,11 @@ const TextInput = createReactClass({
* instead of implementing the logic in JS to avoid flicker.
*/
maxLength: PropTypes.number,
/**
* If autogrow is `true`, limits the height that the TextInput box can grow
* to. Once it reaches this height, the TextInput becomes scrollable.
*/

This comment has been minimized.

Copy link
@chirag04

chirag04 Aug 11, 2017

Contributor

there is maxHeight style prop also. thought it would be confusing as style and input prop. cc @sahrens

This comment has been minimized.

Copy link
@shergin

shergin Aug 11, 2017

Contributor

cc @sumkit

This comment has been minimized.

Copy link
@ide

ide Aug 11, 2017

Contributor

How about maxAutoGrowHeight so it's clear this value is coupled to the auto-grow behavior?

This comment has been minimized.

Copy link
@fkgozali

fkgozali Aug 11, 2017

Contributor

The problem here is that we control maxHeight logic in JS (it's technically possible to make it work directly in Yoga/native android). When taking the value off this.props.style, it's not always inspectable, because StyleSheet.create() produces a bunch of IDs (for fast lookup of style), hence this.props.style.maxHeight is not always computable. For that reason maxHeight is its own prop for now.

This comment has been minimized.

Copy link
@sahrens

sahrens Aug 12, 2017

Contributor

Hmm, good point. styles actually are inspectable if you use flattenStyle...maybe we should use that. Although I'm not sure what happens if you set both height and maxHeight - maybe we don't even need special logic and we can just rely on yoga? In any case, we should change this to be more clear.

This comment has been minimized.

Copy link
@fkgozali

fkgozali Aug 12, 2017

Contributor

maybe we don't even need special logic and we can just rely on yoga?

Yeah, this seems like the most ideal approach, though we'll need to do some native android plumbing.

maxHeight: PropTypes.number,
/**
* Sets the number of lines for a `TextInput`. Use it with multiline set to
* `true` to be able to fill the lines.
Expand Down Expand Up @@ -541,6 +553,10 @@ const TextInput = createReactClass({
*/
mixins: [NativeMethodsMixin, TimerMixin],

getInitialState: function() {
return {nativeHeight: this._originalNativeHeight};
},

/**
* Returns `true` if the input is currently focused; `false` otherwise.
*/
Expand All @@ -558,6 +574,7 @@ const TextInput = createReactClass({
_focusSubscription: (undefined: ?Function),
_lastNativeText: (undefined: ?string),
_lastNativeSelection: (undefined: ?Selection),
_originalNativeHeight: (-1: number),

componentDidMount: function() {
this._lastNativeText = this.props.value;
Expand Down Expand Up @@ -673,6 +690,7 @@ const TextInput = createReactClass({
children = [children, props.inputView];
}
props.style.unshift(styles.multilineInput);
props.style.push({height: this.state.nativeHeight});
textContainer =
<RCTTextView
ref={this._setNativeRef}
Expand All @@ -681,7 +699,7 @@ const TextInput = createReactClass({
onFocus={this._onFocus}
onBlur={this._onBlur}
onChange={this._onChange}
onContentSizeChange={this.props.onContentSizeChange}
onContentSizeChange={this._onContentSizeChange}
onSelectionChange={this._onSelectionChange}
onTextInput={this._onTextInput}
onSelectionChangeShouldSetResponder={emptyFunction.thatReturnsTrue}
Expand All @@ -690,7 +708,6 @@ const TextInput = createReactClass({
onScroll={this._onScroll}
/>;
}

return (
<TouchableWithoutFeedback
onLayout={props.onLayout}
Expand All @@ -708,7 +725,7 @@ const TextInput = createReactClass({

_renderAndroid: function() {
const props = Object.assign({}, this.props);
props.style = [this.props.style];
props.style = [{height: this.state.nativeHeight}, this.props.style];
props.autoCapitalize =
UIManager.AndroidTextInput.Constants.AutoCapitalizationType[this.props.autoCapitalize];
var children = this.props.children;
Expand All @@ -721,11 +738,9 @@ const TextInput = createReactClass({
if (childCount > 1) {
children = <Text>{children}</Text>;
}

if (props.selection && props.selection.end == null) {
props.selection = {start: props.selection.start, end: props.selection.start};
}

const textContainer =
<AndroidTextInput
ref={this._setNativeRef}
Expand All @@ -734,6 +749,7 @@ const TextInput = createReactClass({
onFocus={this._onFocus}
onBlur={this._onBlur}
onChange={this._onChange}
onContentSizeChange={this._onContentSizeChange}
onSelectionChange={this._onSelectionChange}
onTextInput={this._onTextInput}
text={this._getText()}
Expand Down Expand Up @@ -796,6 +812,24 @@ const TextInput = createReactClass({
this.forceUpdate();
},

_onContentSizeChange: function(event: Event) {
const height = event.nativeEvent.contentSize.height;
if (this._originalNativeHeight < 0) {
this._originalNativeHeight = height;
}
if (this.props.autoGrow) {
if (this.props.maxHeight) {
this.setState({nativeHeight:
Math.min(this.props.maxHeight, height)});
} else if (Platform.OS === 'android') {
this.setState({nativeHeight: height});
}
} else {
this.setState({nativeHeight: this._originalNativeHeight});
}
this.props.onContentSizeChange && this.props.onContentSizeChange(event);
},

_onSelectionChange: function(event: Event) {
this.props.onSelectionChange && this.props.onSelectionChange(event);

Expand Down

0 comments on commit 21b1ed3

Please sign in to comment.