Permalink
Browse files

autoGrow for RN TextInput

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 21b1ed31152937225e8fc4ffbea8127a28f8f9d2
Showing with 39 additions and 5 deletions.
  1. +39 −5 Libraries/Components/TextInput/TextInput.js
@@ -168,6 +168,7 @@ const DataDetectorTypes = [
* or control this param programmatically with native code.
*
*/
// $FlowFixMe(>=0.41.0)
const TextInput = createReactClass({
displayName: 'TextInput',
@@ -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`.
*/
@@ -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.

Show comment
Hide comment
@chirag04

chirag04 Aug 11, 2017

Collaborator

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

@chirag04

chirag04 Aug 11, 2017

Collaborator

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

This comment has been minimized.

Show comment
Hide comment
@shergin

shergin Aug 11, 2017

Contributor

cc @sumkit

@shergin

This comment has been minimized.

Show comment
Hide comment
@ide

ide Aug 11, 2017

Collaborator

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

@ide

ide Aug 11, 2017

Collaborator

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

This comment has been minimized.

Show comment
Hide comment
@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.

@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.

Show comment
Hide comment
@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.

@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.

Show comment
Hide comment
@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.

@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.
@@ -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.
*/
@@ -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;
@@ -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}
@@ -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}
@@ -690,7 +708,6 @@ const TextInput = createReactClass({
onScroll={this._onScroll}
/>;
}
return (
<TouchableWithoutFeedback
onLayout={props.onLayout}
@@ -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;
@@ -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}
@@ -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()}
@@ -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);

0 comments on commit 21b1ed3

Please sign in to comment.