diff --git a/docs/components/TextInput.md b/docs/components/TextInput.md index aec937eb..769d9658 100644 --- a/docs/components/TextInput.md +++ b/docs/components/TextInput.md @@ -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. diff --git a/src/components/TextInput/index.js b/src/components/TextInput/index.js index ab1e46a0..6f8d4877 100644 --- a/src/components/TextInput/index.js +++ b/src/components/TextInput/index.js @@ -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) => {