[add] TextInput support for selection

This commit is contained in:
Nicolas Gallagher
2016-10-28 23:37:19 -07:00
parent b46acd4f50
commit 2b826dc7f4
2 changed files with 58 additions and 24 deletions

View File

@@ -116,22 +116,13 @@ an argument to the callback handler. Fires before `onChange` callbacks.
**onSelectionChange**: function
Callback that is called when the text input's selection changes. The following
object is passed as an argument to the callback handler.
Callback that is called when the text input's selection changes. This will be called with
`{ nativeEvent: { selection: { start, end } } }`.
**onSubmitEditing**: function
Callback that is called when the keyboard's submit button is pressed.
```js
{
selectionDirection,
selectionEnd,
selectionStart,
nativeEvent
}
```
**placeholder**: string
The string that will be rendered in an empty `TextInput` before text has been
@@ -148,6 +139,10 @@ passwords stay secure.
(Not available when `multiline` is `true`.)
**selection**: { start: number, end: ?number }
The start and end of the text input's selection. Set start and end to the same value to position the cursor.
**selectTextOnFocus**: bool = false
If `true`, all text will automatically be selected on focus.

View File

@@ -14,6 +14,9 @@ import React, { Component, PropTypes } from 'react';
const viewStyleProps = Object.keys(ViewStylePropTypes);
/**
* React Native events differ from W3C events.
*/
const normalizeEventHandler = (handler) => (e) => {
if (handler) {
e.nativeEvent.text = e.target.value;
@@ -21,6 +24,32 @@ const normalizeEventHandler = (handler) => (e) => {
}
};
/**
* Determins whether a 'selection' prop differs from a node's existing
* selection state.
*/
const isSelectionStale = (node, selection) => {
if (node && selection) {
const { selectionEnd, selectionStart } = node;
const { start, end } = selection;
return start !== selectionStart || end !== selectionEnd;
}
return false;
};
/**
* Certain input types do no support 'selectSelectionRange' and will throw an
* error.
*/
const setSelection = (node, selection) => {
try {
if (isSelectionStale(node, selection)) {
const { start, end } = selection;
node.setSelectionRange(start, end || start);
}
} catch (e) {}
};
class TextInput extends Component {
static displayName = 'TextInput';
@@ -50,6 +79,10 @@ class TextInput extends Component {
placeholder: PropTypes.string,
placeholderTextColor: PropTypes.string,
secureTextEntry: PropTypes.bool,
selection: PropTypes.shape({
start: PropTypes.number.isRequired,
end: PropTypes.number
}),
selectTextOnFocus: PropTypes.bool,
style: Text.propTypes.style,
testID: Text.propTypes.testID,
@@ -93,6 +126,14 @@ class TextInput extends Component {
UIManager.updateView(this._inputRef, props, this);
}
componentDidMount() {
setSelection(findNodeHandle(this._inputRef), this.props.selection);
}
componentDidUpdate() {
setSelection(findNodeHandle(this._inputRef), this.props.selection);
}
render() {
const {
accessibilityLabel, // eslint-disable-line
@@ -107,9 +148,7 @@ class TextInput extends Component {
maxNumberOfLines,
multiline,
numberOfLines,
onKeyPress,
onLayout,
onSelectionChange,
placeholder,
placeholderTextColor,
secureTextEntry,
@@ -251,17 +290,17 @@ class TextInput extends Component {
}
_handleSelectionChange = (e) => {
const { onSelectionChange } = this.props;
try {
const { selectionDirection, selectionEnd, selectionStart } = e.target;
const event = {
selectionDirection,
selectionEnd,
selectionStart,
nativeEvent: e.nativeEvent
};
if (onSelectionChange) { onSelectionChange(event); }
} catch (e) {}
const { onSelectionChange, selection = {} } = this.props;
if (onSelectionChange) {
try {
const node = e.target;
if (isSelectionStale(node, selection)) {
const { selectionStart, selectionEnd } = node;
e.nativeEvent.selection = { start: selectionStart, end: selectionEnd };
onSelectionChange(e);
}
} catch (e) {}
}
}
_setInputRef = (component) => {