2015-02-20 04:10:52 +00:00
/ * *
2017-05-06 03:50:47 +00:00
* Copyright ( c ) 2015 - present , Facebook , Inc .
2016-07-12 12:51:57 +00:00
*
2018-02-17 02:24:55 +00:00
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree .
2016-07-12 12:51:57 +00:00
*
2018-05-11 20:32:37 +00:00
* @ format
2015-03-23 18:36:57 +00:00
* @ flow
2015-02-20 04:10:52 +00:00
* /
2018-05-11 20:32:37 +00:00
2015-02-20 04:10:52 +00:00
'use strict' ;
2018-02-27 18:42:44 +00:00
const Button = require ( 'Button' ) ;
const InputAccessoryView = require ( 'InputAccessoryView' ) ;
2016-04-09 03:36:40 +00:00
var React = require ( 'react' ) ;
var ReactNative = require ( 'react-native' ) ;
2018-05-11 20:32:37 +00:00
var { Text , TextInput , View , StyleSheet , Slider , Switch } = ReactNative ;
2015-02-20 04:10:52 +00:00
2017-08-18 01:36:54 +00:00
class WithLabel extends React . Component < $FlowFixMeProps > {
2016-07-26 08:00:02 +00:00
render ( ) {
2015-02-20 04:10:52 +00:00
return (
< View style = { styles . labelContainer } >
< View style = { styles . label } >
< Text > { this . props . label } < / T e x t >
< / V i e w >
{ this . props . children }
< / V i e w >
) ;
2016-07-26 08:00:02 +00:00
}
}
2015-02-20 04:10:52 +00:00
2017-08-18 01:36:54 +00:00
class TextEventsExample extends React . Component < { } , $FlowFixMeState > {
2016-07-26 08:00:02 +00:00
state = {
curText : '<No Event>' ,
prevText : '<No Event>' ,
prev2Text : '<No Event>' ,
prev3Text : '<No Event>' ,
} ;
2015-02-20 04:10:52 +00:00
2018-05-11 20:32:37 +00:00
updateText = text => {
this . setState ( state => {
2015-07-21 19:37:24 +00:00
return {
curText : text ,
prevText : state . curText ,
prev2Text : state . prevText ,
2015-11-02 17:13:41 +00:00
prev3Text : state . prev2Text ,
2015-07-21 19:37:24 +00:00
} ;
2015-02-20 04:10:52 +00:00
} ) ;
2016-07-26 08:00:02 +00:00
} ;
2015-02-20 04:10:52 +00:00
2016-07-26 08:00:02 +00:00
render ( ) {
2015-02-20 04:10:52 +00:00
return (
< View >
< TextInput
2015-03-04 22:04:52 +00:00
autoCapitalize = "none"
2015-02-20 04:10:52 +00:00
placeholder = "Enter text to see events"
autoCorrect = { false }
onFocus = { ( ) => this . updateText ( 'onFocus' ) }
onBlur = { ( ) => this . updateText ( 'onBlur' ) }
2018-05-11 20:32:37 +00:00
onChange = { event =>
this . updateText ( 'onChange text: ' + event . nativeEvent . text )
}
onEndEditing = { event =>
this . updateText ( 'onEndEditing text: ' + event . nativeEvent . text )
}
onSubmitEditing = { event =>
this . updateText ( 'onSubmitEditing text: ' + event . nativeEvent . text )
}
onSelectionChange = { event =>
this . updateText (
'onSelectionChange range: ' +
event . nativeEvent . selection . start +
',' +
event . nativeEvent . selection . end ,
)
}
onKeyPress = { event => {
2015-11-02 17:13:41 +00:00
this . updateText ( 'onKeyPress key: ' + event . nativeEvent . key ) ;
} }
2015-02-20 04:10:52 +00:00
style = { styles . default }
/ >
< Text style = { styles . eventLabel } >
2018-05-11 20:32:37 +00:00
{ this . state . curText }
{ '\n' }
2015-07-21 19:37:24 +00:00
( prev : { this . state . prevText } ) { '\n' }
2015-11-02 17:13:41 +00:00
( prev2 : { this . state . prev2Text } ) { '\n' }
( prev3 : { this . state . prev3Text } )
2015-02-20 04:10:52 +00:00
< / T e x t >
< / V i e w >
) ;
}
2016-07-26 08:00:02 +00:00
}
2015-02-20 04:10:52 +00:00
2018-02-27 18:42:44 +00:00
class TextInputAccessoryViewExample extends React . Component < { } , * > {
constructor ( props ) {
super ( props ) ;
this . state = { text : 'Placeholder Text' } ;
}
render ( ) {
const inputAccessoryViewID = 'inputAccessoryView1' ;
return (
< View >
< TextInput
style = { styles . default }
inputAccessoryViewID = { inputAccessoryViewID }
onChangeText = { text => this . setState ( { text } ) }
value = { this . state . text }
/ >
< InputAccessoryView nativeID = { inputAccessoryViewID } >
< View style = { { backgroundColor : 'white' } } >
< Button
onPress = { ( ) => this . setState ( { text : 'Placeholder Text' } ) }
title = "Reset Text"
/ >
< / V i e w >
< / I n p u t A c c e s s o r y V i e w >
< / V i e w >
) ;
}
}
2017-08-18 01:36:54 +00:00
class RewriteExample extends React . Component < $FlowFixMeProps , any > {
2015-07-21 19:37:24 +00:00
constructor ( props ) {
super ( props ) ;
this . state = { text : '' } ;
}
render ( ) {
var limit = 20 ;
var remainder = limit - this . state . text . length ;
var remainderColor = remainder > 5 ? 'blue' : 'red' ;
return (
< View style = { styles . rewriteContainer } >
< TextInput
multiline = { false }
maxLength = { limit }
2018-05-11 20:32:37 +00:00
onChangeText = { text => {
2015-07-21 19:37:24 +00:00
text = text . replace ( / /g , '_' ) ;
this . setState ( { text } ) ;
} }
style = { styles . default }
value = { this . state . text }
/ >
< Text style = { [ styles . remainder , { color : remainderColor } ] } >
{ remainder }
< / T e x t >
< / V i e w >
) ;
}
}
2018-05-11 20:32:37 +00:00
class RewriteExampleInvalidCharacters extends React . Component <
$FlowFixMeProps ,
any ,
> {
2015-12-14 14:41:45 +00:00
constructor ( props ) {
super ( props ) ;
this . state = { text : '' } ;
}
render ( ) {
return (
< View style = { styles . rewriteContainer } >
< TextInput
multiline = { false }
2018-05-11 20:32:37 +00:00
onChangeText = { text => {
2015-12-14 14:41:45 +00:00
this . setState ( { text : text . replace ( /\s/g , '' ) } ) ;
} }
style = { styles . default }
value = { this . state . text }
/ >
< / V i e w >
) ;
}
}
Fix controlled <TextInput> on iOS when inputting in Chinese/Japanese
Summary:
@public
This should fix #18403.
When the user is inputting in Chinese/Japanese with <TextInput> in a controlled manner, the RCTBaseTextInputView will compare the JS-generated attributed string against the TextInputView attributed string and repeatedly overwrite the TextInputView one. This is because the native TextInputView will provide extra styling to show that some text is provisional.
My solution is to do a plain text string comparison at this point, like how we do for dictation.
Expected behavior when typing in a language that has "multistage" text input: For instance, in Chinese/Japanese it's common to type out the pronunciation for a word and then choose the appropriate word from above the keyboard. In this model, the "pronunciation" shows up in the text box first and then is replaced with the chosen word.
Using the word Japan which is written 日本 but first typed as にほん. It takes 4 key-presses to get to 日本, since に, ほ, ん, are all typed and then 日本 is selected. So here is what should happen:
1. enter に, onChange fires with 'に', markedTextRange covers 'に'
2. enter ほ, onChange fires with 'にほ', markedTextRange covers 'にほ'
3. enter ん, onChange fires with 'にほん', markedTextRange covers 'にほん'
4. user selects 日本 from the menu above the keyboard (provided by the keyboard/OS), onChange fires with '日本', markedTextRange is removed
previously we were overwriting the attributed text which would remove the markedTextRange, preventing the user from selecting 日本 from above the keyboard.
Cheekily, I've also fixed an issue with secure text entry as it's the same type of problem.
Reviewed By: PeteTheHeat
Differential Revision: D9002295
fbshipit-source-id: 7304ede055f301dab9ce1ea70f65308f2a4b4a8f
2018-07-30 14:54:19 +00:00
class RewriteExampleKana extends React . Component < $FlowFixMeProps , any > {
constructor ( props ) {
super ( props ) ;
this . state = { text : '' } ;
}
render ( ) {
return (
< View style = { styles . rewriteContainer } >
< TextInput
multiline = { false }
onChangeText = { text => {
this . setState ( { text : text . replace ( /ひ/g , '日' ) } ) ;
} }
style = { styles . default }
value = { this . state . text }
/ >
< / V i e w >
) ;
}
}
class SecureEntryExample extends React . Component < $FlowFixMeProps , any > {
constructor ( props ) {
super ( props ) ;
this . state = { text : '' } ;
}
render ( ) {
return (
< View >
< TextInput
secureTextEntry = { true }
style = { styles . default }
defaultValue = "abc"
onChangeText = { text => this . setState ( { text } ) }
value = { this . state . text }
/ >
< Text > Current text is : { this . state . text } < / T e x t >
< / V i e w >
) ;
}
}
2017-08-18 01:36:54 +00:00
class TokenizedTextExample extends React . Component < $FlowFixMeProps , any > {
2015-11-06 15:25:19 +00:00
constructor ( props ) {
super ( props ) ;
this . state = { text : 'Hello #World' } ;
}
render ( ) {
//define delimiter
let delimiter = /\s+/ ;
//split string
let _text = this . state . text ;
2018-05-11 20:32:37 +00:00
let token ,
index ,
parts = [ ] ;
2015-11-06 15:25:19 +00:00
while ( _text ) {
delimiter . lastIndex = 0 ;
token = delimiter . exec ( _text ) ;
if ( token === null ) {
break ;
}
index = token . index ;
if ( token [ 0 ] . length === 0 ) {
index = 1 ;
}
parts . push ( _text . substr ( 0 , index ) ) ;
parts . push ( token [ 0 ] ) ;
index = index + token [ 0 ] . length ;
_text = _text . slice ( index ) ;
}
parts . push ( _text ) ;
//highlight hashtags
2018-05-11 20:32:37 +00:00
parts = parts . map ( text => {
2015-11-06 15:25:19 +00:00
if ( /^#/ . test ( text ) ) {
2018-05-11 20:32:37 +00:00
return (
< Text key = { text } style = { styles . hashtag } >
{ text }
< / T e x t >
) ;
2015-11-06 15:25:19 +00:00
} else {
return text ;
}
} ) ;
return (
< View >
< TextInput
multiline = { true }
style = { styles . multiline }
2018-05-11 20:32:37 +00:00
onChangeText = { text => {
2015-11-06 15:25:19 +00:00
this . setState ( { text } ) ;
} } >
< Text > { parts } < / T e x t >
< / T e x t I n p u t >
< / V i e w >
) ;
}
}
2017-08-18 01:36:54 +00:00
class BlurOnSubmitExample extends React . Component < { } > {
2018-05-11 20:32:37 +00:00
focusNextField = nextField => {
2016-01-12 16:17:49 +00:00
this . refs [ nextField ] . focus ( ) ;
2016-07-26 08:00:02 +00:00
} ;
2015-11-05 05:01:49 +00:00
2016-07-26 08:00:02 +00:00
render ( ) {
2015-11-05 05:01:49 +00:00
return (
< View >
< TextInput
2016-01-12 16:17:49 +00:00
ref = "1"
2015-11-05 05:01:49 +00:00
style = { styles . default }
2016-01-12 16:17:49 +00:00
placeholder = "blurOnSubmit = false"
returnKeyType = "next"
2015-11-05 05:01:49 +00:00
blurOnSubmit = { false }
onSubmitEditing = { ( ) => this . focusNextField ( '2' ) }
/ >
< TextInput
2016-01-12 16:17:49 +00:00
ref = "2"
2015-11-05 05:01:49 +00:00
style = { styles . default }
2016-01-12 16:17:49 +00:00
keyboardType = "email-address"
placeholder = "blurOnSubmit = false"
returnKeyType = "next"
2015-11-05 05:01:49 +00:00
blurOnSubmit = { false }
onSubmitEditing = { ( ) => this . focusNextField ( '3' ) }
/ >
< TextInput
2016-01-12 16:17:49 +00:00
ref = "3"
2015-11-05 05:01:49 +00:00
style = { styles . default }
2016-01-12 16:17:49 +00:00
keyboardType = "url"
placeholder = "blurOnSubmit = false"
returnKeyType = "next"
2015-11-05 05:01:49 +00:00
blurOnSubmit = { false }
onSubmitEditing = { ( ) => this . focusNextField ( '4' ) }
/ >
< TextInput
2016-01-12 16:17:49 +00:00
ref = "4"
2015-11-05 05:01:49 +00:00
style = { styles . default }
2016-01-12 16:17:49 +00:00
keyboardType = "numeric"
2017-06-27 23:05:11 +00:00
returnKeyType = "done"
2016-01-12 16:17:49 +00:00
placeholder = "blurOnSubmit = false"
2015-11-05 05:01:49 +00:00
blurOnSubmit = { false }
onSubmitEditing = { ( ) => this . focusNextField ( '5' ) }
/ >
< TextInput
2016-01-12 16:17:49 +00:00
ref = "5"
2015-11-05 05:01:49 +00:00
style = { styles . default }
2016-01-12 16:17:49 +00:00
keyboardType = "numbers-and-punctuation"
placeholder = "blurOnSubmit = true"
returnKeyType = "done"
2015-11-05 05:01:49 +00:00
/ >
< / V i e w >
) ;
}
2016-07-26 08:00:02 +00:00
}
2015-11-05 05:01:49 +00:00
2016-08-26 00:18:05 +00:00
type SelectionExampleState = {
2018-05-14 07:08:58 +00:00
selection : { |
2018-05-11 20:32:37 +00:00
start : number ,
end ? : number ,
2018-05-14 07:08:58 +00:00
| } ,
2018-05-11 20:32:37 +00:00
value : string ,
2016-08-26 00:18:05 +00:00
} ;
2018-05-11 20:32:37 +00:00
class SelectionExample extends React . Component <
$FlowFixMeProps ,
SelectionExampleState ,
> {
2016-08-26 00:18:05 +00:00
_textInput : any ;
constructor ( props ) {
super ( props ) ;
this . state = {
selection : { start : 0 , end : 0 } ,
2018-05-11 20:32:37 +00:00
value : props . value ,
2016-08-26 00:18:05 +00:00
} ;
}
onSelectionChange ( { nativeEvent : { selection } } ) {
this . setState ( { selection } ) ;
}
getRandomPosition ( ) {
var length = this . state . value . length ;
return Math . round ( Math . random ( ) * length ) ;
}
select ( start , end ) {
this . _textInput . focus ( ) ;
this . setState ( { selection : { start , end } } ) ;
}
selectRandom ( ) {
2018-05-11 20:32:37 +00:00
var positions = [ this . getRandomPosition ( ) , this . getRandomPosition ( ) ] . sort (
( a , b ) => a - b ,
) ;
2016-08-26 00:18:05 +00:00
this . select ( ... positions ) ;
}
placeAt ( position ) {
this . select ( position , position ) ;
}
placeAtRandom ( ) {
this . placeAt ( this . getRandomPosition ( ) ) ;
}
render ( ) {
var length = this . state . value . length ;
return (
< View >
< TextInput
multiline = { this . props . multiline }
2018-05-11 20:32:37 +00:00
onChangeText = { value => this . setState ( { value } ) }
2016-08-26 00:18:05 +00:00
onSelectionChange = { this . onSelectionChange . bind ( this ) }
ref = { textInput => ( this . _textInput = textInput ) }
selection = { this . state . selection }
style = { this . props . style }
value = { this . state . value }
/ >
< View >
2018-05-11 20:32:37 +00:00
< Text > selection = { JSON . stringify ( this . state . selection ) } < / T e x t >
2016-08-26 00:18:05 +00:00
< Text onPress = { this . placeAt . bind ( this , 0 ) } >
Place at Start ( 0 , 0 )
< / T e x t >
< Text onPress = { this . placeAt . bind ( this , length ) } >
Place at End ( { length } , { length } )
< / T e x t >
2018-05-11 20:32:37 +00:00
< Text onPress = { this . placeAtRandom . bind ( this ) } > Place at Random < / T e x t >
< Text onPress = { this . select . bind ( this , 0 , length ) } > Select All < / T e x t >
< Text onPress = { this . selectRandom . bind ( this ) } > Select Random < / T e x t >
2016-08-26 00:18:05 +00:00
< / V i e w >
< / V i e w >
) ;
}
}
2018-05-11 20:32:37 +00:00
class AutogrowingTextInputExample extends React . Component <
$FlowFixMeProps ,
$FlowFixMeState ,
> {
2018-01-24 07:17:57 +00:00
constructor ( props ) {
super ( props ) ;
this . state = {
width : 100 ,
multiline : true ,
text : '' ,
contentSize : {
width : 0 ,
height : 0 ,
} ,
} ;
}
2018-02-08 18:26:45 +00:00
UNSAFE _componentWillReceiveProps ( props ) {
2018-01-24 07:17:57 +00:00
this . setState ( {
multiline : props . multiline ,
} ) ;
}
render ( ) {
var { style , multiline , ... props } = this . props ;
return (
< View >
< Text > Width : < / T e x t >
< Slider
value = { 100 }
minimumValue = { 0 }
maximumValue = { 100 }
step = { 10 }
2018-05-11 20:32:37 +00:00
onValueChange = { value => this . setState ( { width : value } ) }
2018-01-24 07:17:57 +00:00
/ >
< Text > Multiline : < / T e x t >
< Switch
value = { this . state . multiline }
2018-05-11 20:32:37 +00:00
onValueChange = { value => this . setState ( { multiline : value } ) }
2018-01-24 07:17:57 +00:00
/ >
< Text > TextInput : < / T e x t >
< TextInput
value = "prop"
multiline = { this . state . multiline }
style = { [ style , { width : this . state . width + '%' } ] }
2018-05-11 20:32:37 +00:00
onChangeText = { value => this . setState ( { text : value } ) }
onContentSizeChange = { event =>
this . setState ( { contentSize : event . nativeEvent . contentSize } )
}
2018-01-24 07:17:57 +00:00
{ ... props }
/ >
< Text > Plain text value representation : < / T e x t >
< Text > { this . state . text } < / T e x t >
< Text > Content Size : { JSON . stringify ( this . state . contentSize ) } < / T e x t >
< / V i e w >
) ;
}
}
2015-02-20 04:10:52 +00:00
var styles = StyleSheet . create ( {
page : {
paddingBottom : 300 ,
} ,
default : {
2017-10-09 16:28:59 +00:00
borderWidth : StyleSheet . hairlineWidth ,
2015-02-20 04:10:52 +00:00
borderColor : '#0f0f0f' ,
flex : 1 ,
fontSize : 13 ,
2015-04-29 08:29:00 +00:00
padding : 4 ,
2015-02-20 04:10:52 +00:00
} ,
multiline : {
2017-10-09 16:28:59 +00:00
borderWidth : StyleSheet . hairlineWidth ,
2015-02-20 04:10:52 +00:00
borderColor : '#0f0f0f' ,
flex : 1 ,
fontSize : 13 ,
height : 50 ,
2015-04-29 08:29:00 +00:00
padding : 4 ,
2015-04-29 22:55:36 +00:00
marginBottom : 4 ,
2015-04-29 08:29:00 +00:00
} ,
2017-03-20 07:00:21 +00:00
multilineExpandable : {
height : 'auto' ,
maxHeight : 100 ,
} ,
2015-04-29 08:29:00 +00:00
multilineWithFontStyles : {
color : 'blue' ,
fontWeight : 'bold' ,
fontSize : 18 ,
fontFamily : 'Cochin' ,
2015-04-29 22:55:36 +00:00
height : 60 ,
} ,
multilineChild : {
width : 50 ,
height : 40 ,
position : 'absolute' ,
right : 5 ,
backgroundColor : 'red' ,
2015-02-20 04:10:52 +00:00
} ,
eventLabel : {
margin : 3 ,
fontSize : 12 ,
} ,
labelContainer : {
flexDirection : 'row' ,
marginVertical : 2 ,
flex : 1 ,
} ,
label : {
2015-07-21 19:37:24 +00:00
width : 115 ,
alignItems : 'flex-end' ,
2015-02-20 04:10:52 +00:00
marginRight : 10 ,
paddingTop : 2 ,
} ,
2015-07-21 19:37:24 +00:00
rewriteContainer : {
flexDirection : 'row' ,
alignItems : 'center' ,
} ,
remainder : {
textAlign : 'right' ,
width : 24 ,
} ,
2015-11-06 15:25:19 +00:00
hashtag : {
color : 'blue' ,
fontWeight : 'bold' ,
} ,
2015-02-20 04:10:52 +00:00
} ) ;
2015-06-22 16:43:30 +00:00
exports . displayName = ( undefined : ? string ) ;
2015-02-20 04:10:52 +00:00
exports . title = '<TextInput>' ;
2015-04-29 08:29:00 +00:00
exports . description = 'Single and multi-line text inputs.' ;
2015-02-20 04:10:52 +00:00
exports . examples = [
{
title : 'Auto-focus' ,
render : function ( ) {
2016-02-04 13:12:36 +00:00
return (
< TextInput
autoFocus = { true }
style = { styles . default }
accessibilityLabel = "I am the accessibility label for text input"
/ >
) ;
2018-05-11 20:32:37 +00:00
} ,
2015-02-20 04:10:52 +00:00
} ,
2015-07-21 19:37:24 +00:00
{
title : "Live Re-Write (<sp> -> '_') + maxLength" ,
render : function ( ) {
return < RewriteExample / > ;
2018-05-11 20:32:37 +00:00
} ,
2015-07-21 19:37:24 +00:00
} ,
2015-12-14 14:41:45 +00:00
{
2016-01-12 16:17:49 +00:00
title : 'Live Re-Write (no spaces allowed)' ,
2015-12-14 14:41:45 +00:00
render : function ( ) {
return < RewriteExampleInvalidCharacters / > ;
2018-05-11 20:32:37 +00:00
} ,
2015-12-14 14:41:45 +00:00
} ,
Fix controlled <TextInput> on iOS when inputting in Chinese/Japanese
Summary:
@public
This should fix #18403.
When the user is inputting in Chinese/Japanese with <TextInput> in a controlled manner, the RCTBaseTextInputView will compare the JS-generated attributed string against the TextInputView attributed string and repeatedly overwrite the TextInputView one. This is because the native TextInputView will provide extra styling to show that some text is provisional.
My solution is to do a plain text string comparison at this point, like how we do for dictation.
Expected behavior when typing in a language that has "multistage" text input: For instance, in Chinese/Japanese it's common to type out the pronunciation for a word and then choose the appropriate word from above the keyboard. In this model, the "pronunciation" shows up in the text box first and then is replaced with the chosen word.
Using the word Japan which is written 日本 but first typed as にほん. It takes 4 key-presses to get to 日本, since に, ほ, ん, are all typed and then 日本 is selected. So here is what should happen:
1. enter に, onChange fires with 'に', markedTextRange covers 'に'
2. enter ほ, onChange fires with 'にほ', markedTextRange covers 'にほ'
3. enter ん, onChange fires with 'にほん', markedTextRange covers 'にほん'
4. user selects 日本 from the menu above the keyboard (provided by the keyboard/OS), onChange fires with '日本', markedTextRange is removed
previously we were overwriting the attributed text which would remove the markedTextRange, preventing the user from selecting 日本 from above the keyboard.
Cheekily, I've also fixed an issue with secure text entry as it's the same type of problem.
Reviewed By: PeteTheHeat
Differential Revision: D9002295
fbshipit-source-id: 7304ede055f301dab9ce1ea70f65308f2a4b4a8f
2018-07-30 14:54:19 +00:00
{
title : 'Live Re-Write (ひ -> 日)' ,
render : function ( ) {
return < RewriteExampleKana / > ;
} ,
} ,
2018-02-27 18:42:44 +00:00
{
title : 'Keyboard Accessory View' ,
render : function ( ) {
return < TextInputAccessoryViewExample / > ;
2018-05-11 20:32:37 +00:00
} ,
2018-02-27 18:42:44 +00:00
} ,
2015-02-20 04:10:52 +00:00
{
title : 'Auto-capitalize' ,
render : function ( ) {
return (
< View >
< WithLabel label = "none" >
2018-05-11 20:32:37 +00:00
< TextInput autoCapitalize = "none" style = { styles . default } / >
2015-02-20 04:10:52 +00:00
< / W i t h L a b e l >
< WithLabel label = "sentences" >
2018-05-11 20:32:37 +00:00
< TextInput autoCapitalize = "sentences" style = { styles . default } / >
2015-02-20 04:10:52 +00:00
< / W i t h L a b e l >
< WithLabel label = "words" >
2018-05-11 20:32:37 +00:00
< TextInput autoCapitalize = "words" style = { styles . default } / >
2015-02-20 04:10:52 +00:00
< / W i t h L a b e l >
< WithLabel label = "characters" >
2018-05-11 20:32:37 +00:00
< TextInput autoCapitalize = "characters" style = { styles . default } / >
2015-02-20 04:10:52 +00:00
< / W i t h L a b e l >
< / V i e w >
) ;
2018-05-11 20:32:37 +00:00
} ,
2015-02-20 04:10:52 +00:00
} ,
{
title : 'Auto-correct' ,
render : function ( ) {
return (
< View >
< WithLabel label = "true" >
< TextInput autoCorrect = { true } style = { styles . default } / >
< / W i t h L a b e l >
< WithLabel label = "false" >
< TextInput autoCorrect = { false } style = { styles . default } / >
< / W i t h L a b e l >
< / V i e w >
) ;
2018-05-11 20:32:37 +00:00
} ,
2015-02-20 04:10:52 +00:00
} ,
2018-01-24 07:17:57 +00:00
{
title : 'Nested content and `value` property' ,
render : function ( ) {
return (
< View >
< WithLabel label = "singleline" >
< TextInput style = { styles . default } value = "(value property)" >
( first raw text node )
< Text color = "red" > ( internal raw text node ) < / T e x t >
( last raw text node )
< / T e x t I n p u t >
< / W i t h L a b e l >
< WithLabel label = "multiline" >
2018-05-11 20:32:37 +00:00
< TextInput
style = { styles . default }
multiline = { true }
value = "(value property)" >
2018-01-24 07:17:57 +00:00
( first raw text node )
< Text color = "red" > ( internal raw text node ) < / T e x t >
( last raw text node )
< / T e x t I n p u t >
< / W i t h L a b e l >
< / V i e w >
) ;
2018-05-11 20:32:37 +00:00
} ,
2018-01-24 07:17:57 +00:00
} ,
2015-03-31 01:25:30 +00:00
{
title : 'Keyboard types' ,
render : function ( ) {
var keyboardTypes = [
'default' ,
'ascii-capable' ,
'numbers-and-punctuation' ,
'url' ,
'number-pad' ,
'phone-pad' ,
'name-phone-pad' ,
'email-address' ,
'decimal-pad' ,
'twitter' ,
'web-search' ,
'numeric' ,
] ;
2018-05-11 20:32:37 +00:00
var examples = keyboardTypes . map ( type => {
2015-03-31 01:25:30 +00:00
return (
< WithLabel key = { type } label = { type } >
2018-05-11 20:32:37 +00:00
< TextInput keyboardType = { type } style = { styles . default } / >
2015-03-31 01:25:30 +00:00
< / W i t h L a b e l >
) ;
} ) ;
return < View > { examples } < / V i e w > ;
2018-05-11 20:32:37 +00:00
} ,
2015-03-31 01:25:30 +00:00
} ,
2015-11-11 13:31:18 +00:00
{
title : 'Keyboard appearance' ,
render : function ( ) {
2018-05-11 20:32:37 +00:00
var keyboardAppearance = [ 'default' , 'light' , 'dark' ] ;
var examples = keyboardAppearance . map ( type => {
2015-11-11 13:31:18 +00:00
return (
< WithLabel key = { type } label = { type } >
2018-05-11 20:32:37 +00:00
< TextInput keyboardAppearance = { type } style = { styles . default } / >
2015-11-11 13:31:18 +00:00
< / W i t h L a b e l >
) ;
} ) ;
return < View > { examples } < / V i e w > ;
2018-05-11 20:32:37 +00:00
} ,
2015-11-11 13:31:18 +00:00
} ,
2015-03-31 01:25:30 +00:00
{
title : 'Return key types' ,
render : function ( ) {
var returnKeyTypes = [
'default' ,
'go' ,
'google' ,
'join' ,
'next' ,
'route' ,
'search' ,
'send' ,
'yahoo' ,
'done' ,
'emergency-call' ,
] ;
2018-05-11 20:32:37 +00:00
var examples = returnKeyTypes . map ( type => {
2015-03-31 01:25:30 +00:00
return (
< WithLabel key = { type } label = { type } >
2018-05-11 20:32:37 +00:00
< TextInput returnKeyType = { type } style = { styles . default } / >
2015-03-31 01:25:30 +00:00
< / W i t h L a b e l >
) ;
} ) ;
return < View > { examples } < / V i e w > ;
2018-05-11 20:32:37 +00:00
} ,
2015-03-31 01:25:30 +00:00
} ,
{
title : 'Enable return key automatically' ,
render : function ( ) {
return (
< View >
< WithLabel label = "true" >
2018-05-11 20:32:37 +00:00
< TextInput
enablesReturnKeyAutomatically = { true }
style = { styles . default }
/ >
2015-03-31 01:25:30 +00:00
< / W i t h L a b e l >
< / V i e w >
) ;
2018-05-11 20:32:37 +00:00
} ,
2015-03-31 01:25:30 +00:00
} ,
{
title : 'Secure text entry' ,
render : function ( ) {
Fix controlled <TextInput> on iOS when inputting in Chinese/Japanese
Summary:
@public
This should fix #18403.
When the user is inputting in Chinese/Japanese with <TextInput> in a controlled manner, the RCTBaseTextInputView will compare the JS-generated attributed string against the TextInputView attributed string and repeatedly overwrite the TextInputView one. This is because the native TextInputView will provide extra styling to show that some text is provisional.
My solution is to do a plain text string comparison at this point, like how we do for dictation.
Expected behavior when typing in a language that has "multistage" text input: For instance, in Chinese/Japanese it's common to type out the pronunciation for a word and then choose the appropriate word from above the keyboard. In this model, the "pronunciation" shows up in the text box first and then is replaced with the chosen word.
Using the word Japan which is written 日本 but first typed as にほん. It takes 4 key-presses to get to 日本, since に, ほ, ん, are all typed and then 日本 is selected. So here is what should happen:
1. enter に, onChange fires with 'に', markedTextRange covers 'に'
2. enter ほ, onChange fires with 'にほ', markedTextRange covers 'にほ'
3. enter ん, onChange fires with 'にほん', markedTextRange covers 'にほん'
4. user selects 日本 from the menu above the keyboard (provided by the keyboard/OS), onChange fires with '日本', markedTextRange is removed
previously we were overwriting the attributed text which would remove the markedTextRange, preventing the user from selecting 日本 from above the keyboard.
Cheekily, I've also fixed an issue with secure text entry as it's the same type of problem.
Reviewed By: PeteTheHeat
Differential Revision: D9002295
fbshipit-source-id: 7304ede055f301dab9ce1ea70f65308f2a4b4a8f
2018-07-30 14:54:19 +00:00
return < SecureEntryExample / > ;
2018-05-11 20:32:37 +00:00
} ,
2015-03-31 01:25:30 +00:00
} ,
2015-02-20 04:10:52 +00:00
{
title : 'Event handling' ,
2018-05-11 20:32:37 +00:00
render : function ( ) : React . Element < any > {
return < TextEventsExample / > ;
} ,
2015-02-20 04:10:52 +00:00
} ,
{
title : 'Colored input text' ,
render : function ( ) {
return (
< View >
< TextInput
style = { [ styles . default , { color : 'blue' } ] }
2015-07-22 21:03:32 +00:00
defaultValue = "Blue"
2015-02-20 04:10:52 +00:00
/ >
< TextInput
style = { [ styles . default , { color : 'green' } ] }
2015-07-22 21:03:32 +00:00
defaultValue = "Green"
2015-02-20 04:10:52 +00:00
/ >
< / V i e w >
) ;
2018-05-11 20:32:37 +00:00
} ,
2015-02-20 04:10:52 +00:00
} ,
2016-02-03 08:21:30 +00:00
{
title : 'Colored highlight/cursor for text input' ,
render : function ( ) {
return (
< View >
< TextInput
style = { styles . default }
2017-10-10 00:37:08 +00:00
selectionColor = { 'green' }
2016-02-03 08:21:30 +00:00
defaultValue = "Highlight me"
/ >
< TextInput
style = { styles . default }
2017-10-10 00:37:08 +00:00
selectionColor = { 'rgba(86, 76, 205, 1)' }
2016-02-03 08:21:30 +00:00
defaultValue = "Highlight me"
/ >
< / V i e w >
) ;
2018-05-11 20:32:37 +00:00
} ,
2016-02-03 08:21:30 +00:00
} ,
2015-02-27 16:40:52 +00:00
{
title : 'Clear button mode' ,
2018-05-11 20:32:37 +00:00
render : function ( ) {
2015-02-27 16:40:52 +00:00
return (
< View >
< WithLabel label = "never" >
2018-05-11 20:32:37 +00:00
< TextInput style = { styles . default } clearButtonMode = "never" / >
2015-02-27 16:40:52 +00:00
< / W i t h L a b e l >
< WithLabel label = "while editing" >
2018-05-11 20:32:37 +00:00
< TextInput style = { styles . default } clearButtonMode = "while-editing" / >
2015-02-27 16:40:52 +00:00
< / W i t h L a b e l >
< WithLabel label = "unless editing" >
< TextInput
style = { styles . default }
2015-03-04 22:04:52 +00:00
clearButtonMode = "unless-editing"
2015-02-27 16:40:52 +00:00
/ >
< / W i t h L a b e l >
< WithLabel label = "always" >
2018-05-11 20:32:37 +00:00
< TextInput style = { styles . default } clearButtonMode = "always" / >
2015-02-27 16:40:52 +00:00
< / W i t h L a b e l >
< / V i e w >
) ;
2018-05-11 20:32:37 +00:00
} ,
2015-02-27 16:40:52 +00:00
} ,
2015-04-15 00:51:28 +00:00
{
title : 'Clear and select' ,
2015-04-29 08:29:00 +00:00
render : function ( ) {
2015-04-15 00:51:28 +00:00
return (
< View >
< WithLabel label = "clearTextOnFocus" >
< TextInput
placeholder = "text is cleared on focus"
2015-07-22 21:03:32 +00:00
defaultValue = "text is cleared on focus"
2015-04-15 00:51:28 +00:00
style = { styles . default }
clearTextOnFocus = { true }
/ >
< / W i t h L a b e l >
< WithLabel label = "selectTextOnFocus" >
< TextInput
placeholder = "text is selected on focus"
2015-07-22 21:03:32 +00:00
defaultValue = "text is selected on focus"
2015-04-15 00:51:28 +00:00
style = { styles . default }
selectTextOnFocus = { true }
/ >
< / W i t h L a b e l >
2017-07-18 21:33:37 +00:00
< WithLabel label = "clearTextOnFocus (multiline)" >
< TextInput
placeholder = "text is cleared on focus"
defaultValue = "text is cleared on focus"
style = { styles . default }
clearTextOnFocus = { true }
multiline = { true }
/ >
< / W i t h L a b e l >
< WithLabel label = "selectTextOnFocus (multiline)" >
< TextInput
placeholder = "text is selected on focus"
defaultValue = "text is selected on focus"
style = { styles . default }
selectTextOnFocus = { true }
multiline = { true }
/ >
< / W i t h L a b e l >
2015-04-15 00:51:28 +00:00
< / V i e w >
) ;
2018-05-11 20:32:37 +00:00
} ,
2015-04-15 00:51:28 +00:00
} ,
2015-11-05 05:01:49 +00:00
{
title : 'Blur on submit' ,
2018-05-11 20:32:37 +00:00
render : function ( ) : React . Element < any > {
return < BlurOnSubmitExample / > ;
} ,
2015-11-05 05:01:49 +00:00
} ,
2015-12-02 15:11:20 +00:00
{
title : 'Multiline blur on submit' ,
render : function ( ) {
return (
< View >
< TextInput
style = { styles . multiline }
2016-01-12 16:17:49 +00:00
placeholder = "blurOnSubmit = true"
returnKeyType = "next"
2015-12-02 15:11:20 +00:00
blurOnSubmit = { true }
multiline = { true }
onSubmitEditing = { event => alert ( event . nativeEvent . text ) }
/ >
< / V i e w >
) ;
2018-05-11 20:32:37 +00:00
} ,
2015-12-02 15:11:20 +00:00
} ,
2015-11-27 14:52:29 +00:00
{
title : 'Multiline' ,
render : function ( ) {
return (
< View >
< TextInput
placeholder = "multiline text input"
multiline = { true }
style = { styles . multiline }
/ >
< TextInput
placeholder = "multiline text input with font styles and placeholder"
multiline = { true }
clearTextOnFocus = { true }
autoCorrect = { true }
autoCapitalize = "words"
placeholderTextColor = "red"
keyboardType = "url"
style = { [ styles . multiline , styles . multilineWithFontStyles ] }
/ >
2016-07-14 14:39:18 +00:00
< TextInput
placeholder = "multiline text input with max length"
maxLength = { 5 }
multiline = { true }
style = { styles . multiline }
/ >
2015-11-27 14:52:29 +00:00
< TextInput
placeholder = "uneditable multiline text input"
editable = { false }
multiline = { true }
style = { styles . multiline }
/ >
2016-07-31 21:20:13 +00:00
< TextInput
defaultValue = "uneditable multiline text input with phone number detection: 88888888."
editable = { false }
multiline = { true }
style = { styles . multiline }
dataDetectorTypes = "phoneNumber"
/ >
2015-11-27 14:52:29 +00:00
< / V i e w >
) ;
2018-05-11 20:32:37 +00:00
} ,
2015-11-27 14:52:29 +00:00
} ,
2017-05-29 22:56:38 +00:00
{
title : 'TextInput Intrinsic Size' ,
render : function ( ) {
return (
2017-05-29 22:56:46 +00:00
< View >
< Text > Singleline TextInput < / T e x t >
< View style = { { height : 80 } } >
< TextInput
style = { {
position : 'absolute' ,
fontSize : 16 ,
backgroundColor : '#eeeeee' ,
borderColor : '#666666' ,
borderWidth : 5 ,
2017-05-29 22:56:47 +00:00
borderTopWidth : 20 ,
borderRadius : 10 ,
borderBottomRightRadius : 20 ,
2017-05-29 22:56:46 +00:00
padding : 10 ,
paddingTop : 20 ,
} }
2017-06-02 21:17:56 +00:00
testID = "singleline_textinput"
2017-05-29 22:56:46 +00:00
placeholder = "Placeholder defines intrinsic size"
/ >
< / V i e w >
< Text > Multiline TextInput < / T e x t >
2017-05-29 22:56:47 +00:00
< View style = { { height : 130 } } >
2017-05-29 22:56:46 +00:00
< TextInput
style = { {
position : 'absolute' ,
fontSize : 16 ,
backgroundColor : '#eeeeee' ,
borderColor : '#666666' ,
borderWidth : 5 ,
2017-05-29 22:56:47 +00:00
borderTopWidth : 20 ,
borderRadius : 10 ,
borderBottomRightRadius : 20 ,
2017-05-29 22:56:46 +00:00
padding : 10 ,
paddingTop : 20 ,
2018-05-11 20:32:37 +00:00
maxHeight : 100 ,
2017-05-29 22:56:46 +00:00
} }
2017-06-02 21:17:56 +00:00
testID = "multiline_textinput"
2017-05-29 22:56:46 +00:00
multiline = { true }
placeholder = "Placeholder defines intrinsic size"
/ >
< / V i e w >
2017-07-18 21:33:58 +00:00
< View >
< TextInput
style = { {
fontSize : 16 ,
backgroundColor : '#eeeeee' ,
borderColor : '#666666' ,
borderWidth : 5 ,
borderTopWidth : 20 ,
borderRadius : 10 ,
borderBottomRightRadius : 20 ,
padding : 10 ,
paddingTop : 20 ,
} }
testID = "multiline_textinput_with_flex"
multiline = { true }
placeholder = "Placeholder defines intrinsic size"
/ >
< / V i e w >
2017-05-29 22:56:38 +00:00
< / V i e w >
) ;
2018-05-11 20:32:37 +00:00
} ,
2017-05-29 22:56:38 +00:00
} ,
2016-01-25 13:45:44 +00:00
{
title : 'Auto-expanding' ,
render : function ( ) {
return (
< View >
2017-03-20 07:00:21 +00:00
< TextInput
2016-01-25 13:45:44 +00:00
placeholder = "height increases with content"
2017-03-20 07:00:23 +00:00
defaultValue = "React Native enables you to build world-class application experiences on native platforms using a consistent developer experience based on JavaScript and React. The focus of React Native is on developer efficiency across all the platforms you care about - learn once, write anywhere. Facebook uses React Native in multiple production apps and will continue investing in React Native."
2017-03-20 07:00:21 +00:00
multiline = { true }
2016-01-25 13:45:44 +00:00
enablesReturnKeyAutomatically = { true }
2017-03-20 07:00:21 +00:00
returnKeyType = "go"
style = { [ styles . multiline , styles . multilineExpandable ] }
2016-01-25 13:45:44 +00:00
/ >
< / V i e w >
) ;
2018-05-11 20:32:37 +00:00
} ,
2016-01-25 13:45:44 +00:00
} ,
2018-01-24 07:17:57 +00:00
{
title : 'Auto-expanding' ,
render : function ( ) {
return (
< View >
< AutogrowingTextInputExample
enablesReturnKeyAutomatically = { true }
returnKeyType = "done"
multiline = { true }
2018-05-11 20:32:37 +00:00
style = { {
maxHeight : 400 ,
minHeight : 20 ,
paddingTop : 0 ,
backgroundColor : '#eeeeee' ,
color : 'blue' ,
} } >
< Text style = { { fontSize : 30 , color : 'green' } } > huge < / T e x t >
2018-01-24 07:17:57 +00:00
generic generic generic
< Text style = { { fontSize : 6 , color : 'red' } } >
small small small small small small
< / T e x t >
< Text > regular regular < / T e x t >
< Text style = { { fontSize : 30 , color : 'green' } } >
huge huge huge huge huge
< / T e x t >
generic generic generic
< / A u t o g r o w i n g T e x t I n p u t E x a m p l e >
< / V i e w >
) ;
2018-05-11 20:32:37 +00:00
} ,
2018-01-24 07:17:57 +00:00
} ,
2015-11-27 14:52:29 +00:00
{
title : 'Attributed text' ,
render : function ( ) {
return < TokenizedTextExample / > ;
2018-05-11 20:32:37 +00:00
} ,
2015-11-27 14:52:29 +00:00
} ,
2016-08-26 00:18:05 +00:00
{
title : 'Text selection & cursor placement' ,
render : function ( ) {
return (
< View >
< SelectionExample
style = { styles . default }
value = "text selection can be changed"
/ >
< SelectionExample
multiline
style = { styles . multiline }
2017-10-10 00:37:08 +00:00
value = { 'multiline text selection\ncan also be changed' }
2016-08-26 00:18:05 +00:00
/ >
< / V i e w >
) ;
2018-05-11 20:32:37 +00:00
} ,
2016-10-18 15:01:00 +00:00
} ,
{
title : 'TextInput maxLength' ,
render : function ( ) {
return (
< View >
< WithLabel label = "maxLength: 5" >
2018-05-11 20:32:37 +00:00
< TextInput maxLength = { 5 } style = { styles . default } / >
2016-10-18 15:01:00 +00:00
< / W i t h L a b e l >
< WithLabel label = "maxLength: 5 with placeholder" >
< TextInput
maxLength = { 5 }
placeholder = "ZIP code entry"
style = { styles . default }
/ >
< / W i t h L a b e l >
< WithLabel label = "maxLength: 5 with default value already set" >
< TextInput
maxLength = { 5 }
defaultValue = "94025"
style = { styles . default }
/ >
< / W i t h L a b e l >
< WithLabel label = "maxLength: 5 with very long default value already set" >
< TextInput
maxLength = { 5 }
defaultValue = "9402512345"
style = { styles . default }
/ >
< / W i t h L a b e l >
< / V i e w >
) ;
2018-05-11 20:32:37 +00:00
} ,
2016-10-18 15:01:00 +00:00
} ,
2015-02-20 04:10:52 +00:00
] ;