react-native-controlled-input 0.9.1 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -3
- package/android/src/main/java/com/controlledinput/ControlledInputView.kt +8 -3
- package/android/src/main/java/com/controlledinput/ControlledInputViewManager.kt +35 -0
- package/android/src/main/java/com/controlledinput/JetpackComposeView.kt +151 -54
- package/android/src/main/java/com/controlledinput/JetpackComposeViewModel.kt +98 -0
- package/ios/ControlledInputView.mm +28 -0
- package/ios/RNControlledInput.swift +204 -0
- package/lib/module/ControlledInputViewNativeComponent.ts +13 -6
- package/lib/module/index.js +35 -25
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/ControlledInputViewNativeComponent.d.ts +12 -5
- package/lib/typescript/src/ControlledInputViewNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +4 -2
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/ControlledInputViewNativeComponent.ts +13 -6
- package/src/index.tsx +118 -87
package/README.md
CHANGED
|
@@ -68,9 +68,15 @@ inputRef.current?.blur();
|
|
|
68
68
|
|------|------|-------------|
|
|
69
69
|
| `value` | `string` | Current input value. |
|
|
70
70
|
| `onTextChange` | `(value: string) => void` | Called with the next text value. Filter it and update `value`. |
|
|
71
|
-
| `onFocus` | `() => void` | Called
|
|
72
|
-
| `onBlur` | `() => void` | Called
|
|
73
|
-
| `
|
|
71
|
+
| `onFocus` | `() => void` | Called when the text input is focused. |
|
|
72
|
+
| `onBlur` | `() => void` | Called when the text input is blurred. |
|
|
73
|
+
| `autoComplete` | `string` | Specifies autocomplete hints for the system. Same as React Native [`TextInput`](https://reactnative.dev/docs/textinput#autocomplete). |
|
|
74
|
+
| `autoCapitalize` | `string` | Can be `none`, `sentences`, `words`, `characters`. Same as React Native [`TextInput`](https://reactnative.dev/docs/textinput#autocapitalize). |
|
|
75
|
+
| `keyboardType` | `string` | Determines which keyboard to open, e.g. `numeric`. Same as React Native [`TextInput`](https://reactnative.dev/docs/textinput#keyboardtype). |
|
|
76
|
+
| `returnKeyType` | `string` | Determines how the return key should look. Same as React Native [`TextInput`](https://reactnative.dev/docs/textinput#returnkeytype). |
|
|
77
|
+
| `placeholder` | `string` | The string that will be rendered before text input has been entered. |
|
|
78
|
+
| `placeholderTextColor` | `ColorValue` | The text color of the placeholder string. |
|
|
79
|
+
| `selectionColor` | `ColorValue` | The highlight and cursor color of the text input. |
|
|
74
80
|
|
|
75
81
|
## Style support
|
|
76
82
|
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
package com.controlledinput
|
|
2
2
|
|
|
3
|
-
import android.annotation.SuppressLint
|
|
4
3
|
import android.content.Context
|
|
5
4
|
import android.util.AttributeSet
|
|
6
5
|
import android.view.inputmethod.InputMethodManager
|
|
7
6
|
import android.widget.LinearLayout
|
|
8
|
-
import androidx.compose.material3.Text
|
|
9
7
|
import androidx.compose.runtime.LaunchedEffect
|
|
10
8
|
import androidx.compose.runtime.collectAsState
|
|
11
9
|
import androidx.compose.runtime.getValue
|
|
@@ -96,6 +94,13 @@ class ControlledInputView : LinearLayout {
|
|
|
96
94
|
JetpackComposeView(
|
|
97
95
|
value = value,
|
|
98
96
|
inputStyle = viewModel.inputStyle,
|
|
97
|
+
autoComplete = viewModel.autoComplete,
|
|
98
|
+
placeholder = viewModel.placeholder,
|
|
99
|
+
placeholderTextColor = viewModel.placeholderTextColor,
|
|
100
|
+
selectionColor = viewModel.selectionColor,
|
|
101
|
+
autoCapitalize = viewModel.autoCapitalize,
|
|
102
|
+
keyboardType = viewModel.keyboardType,
|
|
103
|
+
returnKeyType = viewModel.returnKeyType,
|
|
99
104
|
onTextChange = { value ->
|
|
100
105
|
val surfaceId = UIManagerHelper.getSurfaceId(context)
|
|
101
106
|
val viewId = this.id
|
|
@@ -140,4 +145,4 @@ class ControlledInputView : LinearLayout {
|
|
|
140
145
|
|
|
141
146
|
}
|
|
142
147
|
}
|
|
143
|
-
}
|
|
148
|
+
}
|
|
@@ -38,6 +38,41 @@ class ControlledInputViewManager : SimpleViewManager<ControlledInputView>(),
|
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
@ReactProp(name = "placeholder")
|
|
42
|
+
override fun setPlaceholder(view: ControlledInputView, placeholder: String?) {
|
|
43
|
+
view.viewModel.setPlaceholder(placeholder)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@ReactProp(name = "placeholderTextColor", customType = "Color")
|
|
47
|
+
override fun setPlaceholderTextColor(view: ControlledInputView, placeholderTextColor: Int?) {
|
|
48
|
+
view.viewModel.setPlaceholderTextColor(placeholderTextColor)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@ReactProp(name = "selectionColor", customType = "Color")
|
|
52
|
+
override fun setSelectionColor(view: ControlledInputView, selectionColor: Int?) {
|
|
53
|
+
view.viewModel.setSelectionColor(selectionColor)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@ReactProp(name = "autoComplete")
|
|
57
|
+
override fun setAutoComplete(view: ControlledInputView, autoComplete: String?) {
|
|
58
|
+
view.viewModel.setAutoCompleteWithAutofill(view, autoComplete)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
@ReactProp(name = "autoCapitalize")
|
|
62
|
+
override fun setAutoCapitalize(view: ControlledInputView, autoCapitalize: String?) {
|
|
63
|
+
view.viewModel.setAutoCapitalize(autoCapitalize)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@ReactProp(name = "keyboardType")
|
|
67
|
+
override fun setKeyboardType(view: ControlledInputView, keyboardType: String?) {
|
|
68
|
+
view.viewModel.setKeyboardType(keyboardType)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
@ReactProp(name = "returnKeyType")
|
|
72
|
+
override fun setReturnKeyType(view: ControlledInputView, returnKeyType: String?) {
|
|
73
|
+
view.viewModel.setReturnKeyType(returnKeyType)
|
|
74
|
+
}
|
|
75
|
+
|
|
41
76
|
@ReactProp(name = "inputStyle")
|
|
42
77
|
override fun setInputStyle(view: ControlledInputView, inputStyle: ReadableMap?) {
|
|
43
78
|
val style = if (inputStyle == null) {
|
|
@@ -2,6 +2,7 @@ package com.controlledinput
|
|
|
2
2
|
|
|
3
3
|
import androidx.compose.foundation.background
|
|
4
4
|
import androidx.compose.foundation.border
|
|
5
|
+
import androidx.compose.foundation.text.KeyboardOptions
|
|
5
6
|
import androidx.compose.foundation.layout.Box
|
|
6
7
|
import androidx.compose.foundation.layout.fillMaxSize
|
|
7
8
|
import androidx.compose.foundation.layout.padding
|
|
@@ -25,16 +26,26 @@ import androidx.compose.ui.Modifier
|
|
|
25
26
|
import androidx.compose.ui.focus.FocusRequester
|
|
26
27
|
import androidx.compose.ui.focus.focusRequester
|
|
27
28
|
import android.graphics.Typeface
|
|
29
|
+
import androidx.compose.foundation.text.selection.LocalTextSelectionColors
|
|
30
|
+
import androidx.compose.foundation.text.selection.TextSelectionColors
|
|
31
|
+
import androidx.compose.runtime.CompositionLocalProvider
|
|
28
32
|
import androidx.compose.ui.graphics.Color
|
|
29
33
|
import androidx.compose.ui.platform.LocalContext
|
|
30
34
|
import androidx.compose.ui.text.TextStyle
|
|
31
35
|
import androidx.compose.ui.text.font.FontFamily
|
|
36
|
+
import androidx.compose.ui.text.input.ImeAction
|
|
37
|
+
import androidx.compose.ui.text.input.KeyboardCapitalization
|
|
38
|
+
import androidx.compose.ui.text.input.KeyboardType
|
|
39
|
+
import androidx.compose.ui.autofill.AutofillNode
|
|
40
|
+
import androidx.compose.ui.autofill.AutofillType
|
|
41
|
+
import androidx.compose.ui.layout.onGloballyPositioned
|
|
42
|
+
import androidx.compose.ui.layout.boundsInWindow
|
|
43
|
+
import androidx.compose.ui.platform.LocalAutofill
|
|
44
|
+
import androidx.compose.ui.platform.LocalAutofillTree
|
|
32
45
|
import androidx.compose.ui.unit.sp
|
|
33
|
-
import androidx.lifecycle.ViewModel
|
|
34
46
|
import com.facebook.react.bridge.Arguments
|
|
35
47
|
import com.facebook.react.bridge.WritableMap
|
|
36
48
|
import com.facebook.react.uimanager.events.Event
|
|
37
|
-
import kotlinx.coroutines.flow.MutableStateFlow
|
|
38
49
|
import kotlinx.coroutines.flow.StateFlow
|
|
39
50
|
|
|
40
51
|
data class InputStyle(
|
|
@@ -55,6 +66,13 @@ data class InputStyle(
|
|
|
55
66
|
fun JetpackComposeView(
|
|
56
67
|
value: String,
|
|
57
68
|
inputStyle: StateFlow<InputStyle?>,
|
|
69
|
+
autoComplete: StateFlow<String?>,
|
|
70
|
+
placeholder: StateFlow<String?>,
|
|
71
|
+
placeholderTextColor: StateFlow<Int?>,
|
|
72
|
+
selectionColor: StateFlow<Int?>,
|
|
73
|
+
autoCapitalize: StateFlow<String?>,
|
|
74
|
+
keyboardType: StateFlow<String?>,
|
|
75
|
+
returnKeyType: StateFlow<String?>,
|
|
58
76
|
onTextChange: (value: String) -> Unit,
|
|
59
77
|
onFocus: (() -> Unit)? = null,
|
|
60
78
|
onBlur: (() -> Unit)? = null,
|
|
@@ -62,8 +80,24 @@ fun JetpackComposeView(
|
|
|
62
80
|
) {
|
|
63
81
|
val state = remember { TextFieldState(value) }
|
|
64
82
|
val style by inputStyle.collectAsState()
|
|
83
|
+
val keyboardTypeValue by keyboardType.collectAsState()
|
|
84
|
+
val autoCapitalizeValue by autoCapitalize.collectAsState()
|
|
85
|
+
val returnKeyTypeValue by returnKeyType.collectAsState()
|
|
86
|
+
val autoCompleteValue by autoComplete.collectAsState()
|
|
87
|
+
val placeholderValue by placeholder.collectAsState()
|
|
88
|
+
val placeholderTextColorValue by placeholderTextColor.collectAsState()
|
|
89
|
+
val selectionColorValue by selectionColor.collectAsState()
|
|
65
90
|
val interactionSource = remember { MutableInteractionSource() }
|
|
66
91
|
|
|
92
|
+
val autofill = LocalAutofill.current
|
|
93
|
+
val autofillNode = remember {
|
|
94
|
+
AutofillNode(
|
|
95
|
+
autofillTypes = toAutofillTypes(autoCompleteValue),
|
|
96
|
+
onFill = { onTextChange(it) }
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
LocalAutofillTree.current += autofillNode
|
|
100
|
+
|
|
67
101
|
if (state.text.toString() != value) {
|
|
68
102
|
state.setTextAndPlaceCursorAtEnd(value)
|
|
69
103
|
}
|
|
@@ -73,9 +107,11 @@ fun JetpackComposeView(
|
|
|
73
107
|
when (interaction) {
|
|
74
108
|
is FocusInteraction.Focus -> {
|
|
75
109
|
onFocus?.invoke()
|
|
110
|
+
autofill?.requestAutofillForNode(autofillNode)
|
|
76
111
|
}
|
|
77
112
|
is FocusInteraction.Unfocus -> {
|
|
78
113
|
onBlur?.invoke()
|
|
114
|
+
autofill?.cancelAutofillForNode(autofillNode)
|
|
79
115
|
}
|
|
80
116
|
}
|
|
81
117
|
}
|
|
@@ -110,46 +146,124 @@ fun JetpackComposeView(
|
|
|
110
146
|
?: Color.Transparent
|
|
111
147
|
val shape = RoundedCornerShape(borderRadius)
|
|
112
148
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
.
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
onTextChange(proposed.toString())
|
|
124
|
-
proposed
|
|
125
|
-
},
|
|
149
|
+
val cursorColor = selectionColorValue?.let { Color(it) } ?: textColor
|
|
150
|
+
val textSelectionColors = remember(cursorColor) {
|
|
151
|
+
TextSelectionColors(
|
|
152
|
+
handleColor = cursorColor,
|
|
153
|
+
backgroundColor = cursorColor.copy(alpha = 0.4f)
|
|
154
|
+
)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
CompositionLocalProvider(LocalTextSelectionColors provides textSelectionColors) {
|
|
158
|
+
Box(
|
|
126
159
|
modifier = Modifier
|
|
127
160
|
.fillMaxSize()
|
|
128
|
-
.
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
.
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
161
|
+
.clip(shape)
|
|
162
|
+
.background(backgroundColor)
|
|
163
|
+
.border(borderWidth, borderColor, shape),
|
|
164
|
+
) {
|
|
165
|
+
BasicTextField(
|
|
166
|
+
state,
|
|
167
|
+
inputTransformation = InputTransformation.byValue { _, proposed ->
|
|
168
|
+
onTextChange(proposed.toString())
|
|
169
|
+
proposed
|
|
170
|
+
},
|
|
171
|
+
modifier = Modifier
|
|
172
|
+
.fillMaxSize()
|
|
173
|
+
.padding(
|
|
174
|
+
start = paddingLeft,
|
|
175
|
+
top = paddingTop,
|
|
176
|
+
end = paddingRight,
|
|
177
|
+
bottom = paddingBottom,
|
|
178
|
+
)
|
|
179
|
+
.onGloballyPositioned {
|
|
180
|
+
autofillNode.boundingBox = it.boundsInWindow()
|
|
181
|
+
}
|
|
182
|
+
.focusRequester(focusRequester),
|
|
183
|
+
textStyle = TextStyle(
|
|
184
|
+
color = textColor,
|
|
185
|
+
fontSize = fontSize,
|
|
186
|
+
fontFamily = fontFamily,
|
|
187
|
+
),
|
|
188
|
+
keyboardOptions = KeyboardOptions(
|
|
189
|
+
capitalization = toComposeCapitalization(autoCapitalizeValue),
|
|
190
|
+
keyboardType = toComposeKeyboardType(keyboardTypeValue),
|
|
191
|
+
imeAction = toComposeImeAction(returnKeyTypeValue),
|
|
192
|
+
),
|
|
193
|
+
interactionSource = interactionSource,
|
|
194
|
+
cursorBrush = androidx.compose.ui.graphics.SolidColor(cursorColor),
|
|
195
|
+
decorator = { innerTextField ->
|
|
196
|
+
Box(
|
|
197
|
+
modifier = Modifier.fillMaxSize(),
|
|
198
|
+
contentAlignment = Alignment.CenterStart,
|
|
199
|
+
) {
|
|
200
|
+
if (state.text.isEmpty() && !placeholderValue.isNullOrEmpty()) {
|
|
201
|
+
val finalPlaceholderColor = placeholderTextColorValue?.let { Color(it) }
|
|
202
|
+
?: textColor.copy(alpha = 0.5f)
|
|
203
|
+
androidx.compose.material3.Text(
|
|
204
|
+
text = placeholderValue!!,
|
|
205
|
+
style = TextStyle(
|
|
206
|
+
color = finalPlaceholderColor,
|
|
207
|
+
fontSize = fontSize,
|
|
208
|
+
fontFamily = fontFamily,
|
|
209
|
+
)
|
|
210
|
+
)
|
|
211
|
+
}
|
|
212
|
+
innerTextField()
|
|
213
|
+
}
|
|
214
|
+
},
|
|
215
|
+
)
|
|
216
|
+
}
|
|
150
217
|
}
|
|
151
218
|
}
|
|
152
219
|
|
|
220
|
+
private fun toComposeKeyboardType(value: String?): KeyboardType = when (value) {
|
|
221
|
+
"ascii-capable" -> KeyboardType.Ascii
|
|
222
|
+
"numbers-and-punctuation" -> KeyboardType.Text
|
|
223
|
+
"url" -> KeyboardType.Uri
|
|
224
|
+
"number-pad", "numeric" -> KeyboardType.Number
|
|
225
|
+
"phone-pad" -> KeyboardType.Phone
|
|
226
|
+
"email-address" -> KeyboardType.Email
|
|
227
|
+
"decimal-pad" -> KeyboardType.Decimal
|
|
228
|
+
"visible-password" -> KeyboardType.Password
|
|
229
|
+
else -> KeyboardType.Text
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
private fun toComposeCapitalization(value: String?): KeyboardCapitalization = when (value) {
|
|
233
|
+
"none" -> KeyboardCapitalization.None
|
|
234
|
+
"characters" -> KeyboardCapitalization.Characters
|
|
235
|
+
"words" -> KeyboardCapitalization.Words
|
|
236
|
+
"sentences" -> KeyboardCapitalization.Sentences
|
|
237
|
+
else -> KeyboardCapitalization.Sentences
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
private fun toComposeImeAction(value: String?): ImeAction = when (value) {
|
|
241
|
+
"go" -> ImeAction.Go
|
|
242
|
+
"next" -> ImeAction.Next
|
|
243
|
+
"search" -> ImeAction.Search
|
|
244
|
+
"send" -> ImeAction.Send
|
|
245
|
+
"done" -> ImeAction.Done
|
|
246
|
+
"none" -> ImeAction.None
|
|
247
|
+
"previous" -> ImeAction.Previous
|
|
248
|
+
else -> ImeAction.Default
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
private fun toAutofillTypes(autoComplete: String?): List<AutofillType> = when (autoComplete) {
|
|
252
|
+
"email" -> listOf(AutofillType.EmailAddress)
|
|
253
|
+
"name", "given-name", "family-name", "additional-name" -> listOf(AutofillType.PersonFullName)
|
|
254
|
+
"username" -> listOf(AutofillType.Username)
|
|
255
|
+
"password", "new-password" -> listOf(AutofillType.Password)
|
|
256
|
+
"tel" -> listOf(AutofillType.PhoneNumber)
|
|
257
|
+
"postal-code" -> listOf(AutofillType.PostalCode)
|
|
258
|
+
"street-address" -> listOf(AutofillType.AddressStreet)
|
|
259
|
+
"cc-number" -> listOf(AutofillType.CreditCardNumber)
|
|
260
|
+
"cc-exp" -> listOf(AutofillType.CreditCardExpirationDate)
|
|
261
|
+
"cc-exp-month" -> listOf(AutofillType.CreditCardExpirationMonth)
|
|
262
|
+
"cc-exp-year" -> listOf(AutofillType.CreditCardExpirationYear)
|
|
263
|
+
"cc-csc" -> listOf(AutofillType.CreditCardSecurityCode)
|
|
264
|
+
else -> emptyList()
|
|
265
|
+
}
|
|
266
|
+
|
|
153
267
|
class TextChangeEvent(
|
|
154
268
|
surfaceId: Int,
|
|
155
269
|
viewId: Int,
|
|
@@ -198,20 +312,3 @@ class BlurEvent(
|
|
|
198
312
|
const val EVENT_NAME = "onBlur"
|
|
199
313
|
}
|
|
200
314
|
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
class JetpackComposeViewModel : ViewModel() {
|
|
204
|
-
private val _value = MutableStateFlow("")
|
|
205
|
-
private val _inputStyle = MutableStateFlow<InputStyle?>(null)
|
|
206
|
-
|
|
207
|
-
val value: StateFlow<String> get() = _value
|
|
208
|
-
val inputStyle: StateFlow<InputStyle?> get() = _inputStyle
|
|
209
|
-
|
|
210
|
-
fun setValue(newValue: String) {
|
|
211
|
-
_value.value = newValue
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
fun setInputStyle(style: InputStyle?) {
|
|
215
|
-
_inputStyle.value = style
|
|
216
|
-
}
|
|
217
|
-
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
package com.controlledinput
|
|
2
|
+
|
|
3
|
+
import android.os.Build
|
|
4
|
+
import android.view.View
|
|
5
|
+
import androidx.lifecycle.ViewModel
|
|
6
|
+
import kotlinx.coroutines.flow.MutableStateFlow
|
|
7
|
+
import kotlinx.coroutines.flow.StateFlow
|
|
8
|
+
|
|
9
|
+
class JetpackComposeViewModel : ViewModel() {
|
|
10
|
+
private val _value = MutableStateFlow("")
|
|
11
|
+
private val _inputStyle = MutableStateFlow<InputStyle?>(null)
|
|
12
|
+
private val _autoComplete = MutableStateFlow<String?>(null)
|
|
13
|
+
private val _placeholder = MutableStateFlow<String?>(null)
|
|
14
|
+
private val _placeholderTextColor = MutableStateFlow<Int?>(null)
|
|
15
|
+
private val _selectionColor = MutableStateFlow<Int?>(null)
|
|
16
|
+
private val _autoCapitalize = MutableStateFlow<String?>(null)
|
|
17
|
+
private val _keyboardType = MutableStateFlow<String?>(null)
|
|
18
|
+
private val _returnKeyType = MutableStateFlow<String?>(null)
|
|
19
|
+
|
|
20
|
+
val value: StateFlow<String> get() = _value
|
|
21
|
+
val inputStyle: StateFlow<InputStyle?> get() = _inputStyle
|
|
22
|
+
val autoComplete: StateFlow<String?> get() = _autoComplete
|
|
23
|
+
val placeholder: StateFlow<String?> get() = _placeholder
|
|
24
|
+
val placeholderTextColor: StateFlow<Int?> get() = _placeholderTextColor
|
|
25
|
+
val selectionColor: StateFlow<Int?> get() = _selectionColor
|
|
26
|
+
val autoCapitalize: StateFlow<String?> get() = _autoCapitalize
|
|
27
|
+
val keyboardType: StateFlow<String?> get() = _keyboardType
|
|
28
|
+
val returnKeyType: StateFlow<String?> get() = _returnKeyType
|
|
29
|
+
|
|
30
|
+
fun setValue(newValue: String) {
|
|
31
|
+
_value.value = newValue
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
fun setInputStyle(style: InputStyle?) {
|
|
35
|
+
_inputStyle.value = style
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
fun setAutoComplete(newValue: String?) {
|
|
39
|
+
_autoComplete.value = newValue
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
fun setPlaceholder(newValue: String?) {
|
|
43
|
+
_placeholder.value = newValue
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
fun setPlaceholderTextColor(newValue: Int?) {
|
|
47
|
+
_placeholderTextColor.value = newValue
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
fun setSelectionColor(newValue: Int?) {
|
|
51
|
+
_selectionColor.value = newValue
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
fun setAutoCompleteWithAutofill(hostView: View, newValue: String?) {
|
|
55
|
+
setAutoComplete(newValue)
|
|
56
|
+
|
|
57
|
+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
|
58
|
+
return
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
val hint = when (newValue) {
|
|
62
|
+
"email" -> View.AUTOFILL_HINT_EMAIL_ADDRESS
|
|
63
|
+
"name", "given-name", "family-name" -> View.AUTOFILL_HINT_NAME
|
|
64
|
+
"username" -> View.AUTOFILL_HINT_USERNAME
|
|
65
|
+
"password", "new-password" -> View.AUTOFILL_HINT_PASSWORD
|
|
66
|
+
"tel" -> View.AUTOFILL_HINT_PHONE
|
|
67
|
+
"postal-code" -> View.AUTOFILL_HINT_POSTAL_CODE
|
|
68
|
+
"street-address" -> View.AUTOFILL_HINT_POSTAL_ADDRESS
|
|
69
|
+
"cc-number" -> View.AUTOFILL_HINT_CREDIT_CARD_NUMBER
|
|
70
|
+
"cc-exp" -> View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE
|
|
71
|
+
"cc-exp-month" -> View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH
|
|
72
|
+
"cc-exp-year" -> View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR
|
|
73
|
+
"cc-csc" -> View.AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE
|
|
74
|
+
"additional-name" -> View.AUTOFILL_HINT_NAME
|
|
75
|
+
else -> null
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (hint == null || newValue == "off" || newValue.isNullOrEmpty()) {
|
|
79
|
+
hostView.setAutofillHints(*emptyArray())
|
|
80
|
+
return
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
hostView.importantForAutofill = View.IMPORTANT_FOR_AUTOFILL_YES
|
|
84
|
+
hostView.setAutofillHints(hint)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
fun setAutoCapitalize(newValue: String?) {
|
|
88
|
+
_autoCapitalize.value = newValue
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
fun setKeyboardType(newValue: String?) {
|
|
92
|
+
_keyboardType.value = newValue
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
fun setReturnKeyType(newValue: String?) {
|
|
96
|
+
_returnKeyType.value = newValue
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -52,6 +52,34 @@ using namespace facebook::react;
|
|
|
52
52
|
_inputView.value = [NSString stringWithUTF8String:newViewProps.value.c_str()];
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
if (oldViewProps.autoComplete != newViewProps.autoComplete) {
|
|
56
|
+
_inputView.autoComplete = newViewProps.autoComplete.empty() ? nil : [NSString stringWithUTF8String:newViewProps.autoComplete.c_str()];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (oldViewProps.keyboardType != newViewProps.keyboardType) {
|
|
60
|
+
_inputView.keyboardType = newViewProps.keyboardType.empty() ? nil : [NSString stringWithUTF8String:newViewProps.keyboardType.c_str()];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (oldViewProps.returnKeyType != newViewProps.returnKeyType) {
|
|
64
|
+
_inputView.returnKeyType = newViewProps.returnKeyType.empty() ? nil : [NSString stringWithUTF8String:newViewProps.returnKeyType.c_str()];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (oldViewProps.autoCapitalize != newViewProps.autoCapitalize) {
|
|
68
|
+
_inputView.autoCapitalize = newViewProps.autoCapitalize.empty() ? nil : [NSString stringWithUTF8String:newViewProps.autoCapitalize.c_str()];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (oldViewProps.placeholder != newViewProps.placeholder) {
|
|
72
|
+
_inputView.placeholder = newViewProps.placeholder.empty() ? nil : [NSString stringWithUTF8String:newViewProps.placeholder.c_str()];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (oldViewProps.placeholderTextColor != newViewProps.placeholderTextColor) {
|
|
76
|
+
_inputView.placeholderTextColor = RCTUIColorFromSharedColor(newViewProps.placeholderTextColor);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (oldViewProps.selectionColor != newViewProps.selectionColor) {
|
|
80
|
+
_inputView.selectionColor = RCTUIColorFromSharedColor(newViewProps.selectionColor);
|
|
81
|
+
}
|
|
82
|
+
|
|
55
83
|
const auto &style = newViewProps.inputStyle;
|
|
56
84
|
const auto &oldStyle = oldViewProps.inputStyle;
|
|
57
85
|
|
|
@@ -32,6 +32,34 @@ public class RNControlledInput: UIView, UITextFieldDelegate {
|
|
|
32
32
|
didSet { applyFont() }
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
@objc public var autoComplete: String? {
|
|
36
|
+
didSet { applyAutoComplete() }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@objc public var autoCapitalize: String? {
|
|
40
|
+
didSet { applyAutoCapitalize() }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@objc public var keyboardType: String? {
|
|
44
|
+
didSet { applyKeyboardType() }
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@objc public var returnKeyType: String? {
|
|
48
|
+
didSet { applyReturnKeyType() }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@objc public var placeholder: String? {
|
|
52
|
+
didSet { applyPlaceholder() }
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@objc public var placeholderTextColor: UIColor? {
|
|
56
|
+
didSet { applyPlaceholder() }
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@objc public var selectionColor: UIColor? {
|
|
60
|
+
didSet { textField.tintColor = selectionColor }
|
|
61
|
+
}
|
|
62
|
+
|
|
35
63
|
public override var canBecomeFirstResponder: Bool { true }
|
|
36
64
|
|
|
37
65
|
@objc public func focus() {
|
|
@@ -55,6 +83,7 @@ public class RNControlledInput: UIView, UITextFieldDelegate {
|
|
|
55
83
|
textField.translatesAutoresizingMaskIntoConstraints = false
|
|
56
84
|
textField.borderStyle = .none
|
|
57
85
|
textField.delegate = self
|
|
86
|
+
textField.textColor = textColor
|
|
58
87
|
addSubview(textField)
|
|
59
88
|
|
|
60
89
|
NSLayoutConstraint.activate([
|
|
@@ -65,6 +94,29 @@ public class RNControlledInput: UIView, UITextFieldDelegate {
|
|
|
65
94
|
])
|
|
66
95
|
|
|
67
96
|
applyFont()
|
|
97
|
+
applyAutoComplete()
|
|
98
|
+
applyAutoCapitalize()
|
|
99
|
+
applyKeyboardType()
|
|
100
|
+
applyReturnKeyType()
|
|
101
|
+
applyPlaceholder()
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private func applyPlaceholder() {
|
|
105
|
+
guard let placeholder = placeholder else {
|
|
106
|
+
textField.attributedPlaceholder = nil
|
|
107
|
+
return
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
var attributes: [NSAttributedString.Key: Any] = [:]
|
|
111
|
+
if let placeholderTextColor = placeholderTextColor {
|
|
112
|
+
attributes[.foregroundColor] = placeholderTextColor
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
textField.attributedPlaceholder = NSAttributedString(string: placeholder, attributes: attributes)
|
|
116
|
+
|
|
117
|
+
if let color = textColor {
|
|
118
|
+
textField.textColor = color
|
|
119
|
+
}
|
|
68
120
|
}
|
|
69
121
|
|
|
70
122
|
public func textFieldDidBeginEditing(_ textField: UITextField) {
|
|
@@ -82,6 +134,12 @@ public class RNControlledInput: UIView, UITextFieldDelegate {
|
|
|
82
134
|
|
|
83
135
|
delegate?.controlledInputDidChangeText(self, value: newText)
|
|
84
136
|
|
|
137
|
+
// If the replacement string is more than one character, it's likely an autocomplete/paste
|
|
138
|
+
// In this case, we should let the UITextField update itself to handle it correctly
|
|
139
|
+
if string.count > 1 {
|
|
140
|
+
return true
|
|
141
|
+
}
|
|
142
|
+
|
|
85
143
|
return false
|
|
86
144
|
}
|
|
87
145
|
|
|
@@ -92,4 +150,150 @@ public class RNControlledInput: UIView, UITextFieldDelegate {
|
|
|
92
150
|
textField.font = UIFont.systemFont(ofSize: fontSize)
|
|
93
151
|
}
|
|
94
152
|
}
|
|
153
|
+
|
|
154
|
+
private func applyAutoComplete() {
|
|
155
|
+
guard let autoComplete else {
|
|
156
|
+
textField.textContentType = nil
|
|
157
|
+
return
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if autoComplete == "off" || autoComplete.isEmpty {
|
|
161
|
+
textField.textContentType = nil
|
|
162
|
+
return
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
let mappedValue: String
|
|
166
|
+
switch autoComplete {
|
|
167
|
+
case "email":
|
|
168
|
+
mappedValue = "emailAddress"
|
|
169
|
+
case "tel":
|
|
170
|
+
mappedValue = "telephoneNumber"
|
|
171
|
+
case "name":
|
|
172
|
+
mappedValue = "name"
|
|
173
|
+
case "given-name":
|
|
174
|
+
mappedValue = "givenName"
|
|
175
|
+
case "middle-name":
|
|
176
|
+
mappedValue = "middleName"
|
|
177
|
+
case "family-name":
|
|
178
|
+
mappedValue = "familyName"
|
|
179
|
+
case "username":
|
|
180
|
+
mappedValue = "username"
|
|
181
|
+
case "password":
|
|
182
|
+
mappedValue = "password"
|
|
183
|
+
case "new-password":
|
|
184
|
+
mappedValue = "newPassword"
|
|
185
|
+
case "one-time-code":
|
|
186
|
+
mappedValue = "oneTimeCode"
|
|
187
|
+
case "postal-code":
|
|
188
|
+
mappedValue = "postalCode"
|
|
189
|
+
case "street-address":
|
|
190
|
+
mappedValue = "fullStreetAddress"
|
|
191
|
+
case "country":
|
|
192
|
+
mappedValue = "countryName"
|
|
193
|
+
case "cc-number":
|
|
194
|
+
mappedValue = "creditCardNumber"
|
|
195
|
+
case "cc-csc":
|
|
196
|
+
mappedValue = "creditCardSecurityCode"
|
|
197
|
+
case "cc-exp":
|
|
198
|
+
mappedValue = "creditCardExpiration"
|
|
199
|
+
case "cc-exp-month":
|
|
200
|
+
mappedValue = "creditCardExpirationMonth"
|
|
201
|
+
case "cc-exp-year":
|
|
202
|
+
mappedValue = "creditCardExpirationYear"
|
|
203
|
+
case "birthdate-day":
|
|
204
|
+
mappedValue = "birthdateDay"
|
|
205
|
+
case "birthdate-month":
|
|
206
|
+
mappedValue = "birthdateMonth"
|
|
207
|
+
case "birthdate-year":
|
|
208
|
+
mappedValue = "birthdateYear"
|
|
209
|
+
case "url":
|
|
210
|
+
mappedValue = "URL"
|
|
211
|
+
default:
|
|
212
|
+
mappedValue = autoComplete
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
textField.textContentType = UITextContentType(rawValue: mappedValue)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
private func applyAutoCapitalize() {
|
|
219
|
+
guard let autoCapitalize else {
|
|
220
|
+
textField.autocapitalizationType = .sentences
|
|
221
|
+
return
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
switch autoCapitalize {
|
|
225
|
+
case "none":
|
|
226
|
+
textField.autocapitalizationType = .none
|
|
227
|
+
case "words":
|
|
228
|
+
textField.autocapitalizationType = .words
|
|
229
|
+
case "sentences":
|
|
230
|
+
textField.autocapitalizationType = .sentences
|
|
231
|
+
case "characters":
|
|
232
|
+
textField.autocapitalizationType = .allCharacters
|
|
233
|
+
default:
|
|
234
|
+
textField.autocapitalizationType = .sentences
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
private func applyKeyboardType() {
|
|
239
|
+
switch keyboardType {
|
|
240
|
+
case "ascii-capable":
|
|
241
|
+
textField.keyboardType = .asciiCapable
|
|
242
|
+
case "numbers-and-punctuation":
|
|
243
|
+
textField.keyboardType = .numbersAndPunctuation
|
|
244
|
+
case "url":
|
|
245
|
+
textField.keyboardType = .URL
|
|
246
|
+
case "number-pad":
|
|
247
|
+
textField.keyboardType = .numberPad
|
|
248
|
+
case "phone-pad":
|
|
249
|
+
textField.keyboardType = .phonePad
|
|
250
|
+
case "name-phone-pad":
|
|
251
|
+
textField.keyboardType = .namePhonePad
|
|
252
|
+
case "email-address":
|
|
253
|
+
textField.keyboardType = .emailAddress
|
|
254
|
+
case "decimal-pad":
|
|
255
|
+
textField.keyboardType = .decimalPad
|
|
256
|
+
case "twitter":
|
|
257
|
+
textField.keyboardType = .twitter
|
|
258
|
+
case "web-search":
|
|
259
|
+
textField.keyboardType = .webSearch
|
|
260
|
+
case "visible-password":
|
|
261
|
+
textField.keyboardType = .asciiCapable
|
|
262
|
+
case "numeric":
|
|
263
|
+
textField.keyboardType = .numbersAndPunctuation
|
|
264
|
+
default:
|
|
265
|
+
textField.keyboardType = .default
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
private func applyReturnKeyType() {
|
|
270
|
+
switch returnKeyType {
|
|
271
|
+
case "done":
|
|
272
|
+
textField.returnKeyType = .done
|
|
273
|
+
case "go":
|
|
274
|
+
textField.returnKeyType = .go
|
|
275
|
+
case "next":
|
|
276
|
+
textField.returnKeyType = .next
|
|
277
|
+
case "search":
|
|
278
|
+
textField.returnKeyType = .search
|
|
279
|
+
case "send":
|
|
280
|
+
textField.returnKeyType = .send
|
|
281
|
+
case "none":
|
|
282
|
+
textField.returnKeyType = .default
|
|
283
|
+
case "previous":
|
|
284
|
+
textField.returnKeyType = .default
|
|
285
|
+
case "route":
|
|
286
|
+
textField.returnKeyType = .route
|
|
287
|
+
case "yahoo":
|
|
288
|
+
textField.returnKeyType = .yahoo
|
|
289
|
+
case "emergency-call":
|
|
290
|
+
textField.returnKeyType = .emergencyCall
|
|
291
|
+
case "google":
|
|
292
|
+
textField.returnKeyType = .google
|
|
293
|
+
case "join":
|
|
294
|
+
textField.returnKeyType = .join
|
|
295
|
+
default:
|
|
296
|
+
textField.returnKeyType = .default
|
|
297
|
+
}
|
|
298
|
+
}
|
|
95
299
|
}
|
|
@@ -1,28 +1,28 @@
|
|
|
1
1
|
import {
|
|
2
2
|
codegenNativeComponent,
|
|
3
|
-
type ViewProps,
|
|
4
3
|
type ColorValue,
|
|
5
4
|
type HostComponent,
|
|
5
|
+
type ViewProps,
|
|
6
6
|
} from 'react-native';
|
|
7
|
-
import codegenNativeCommands from 'react-native
|
|
7
|
+
import { codegenNativeCommands } from 'react-native';
|
|
8
8
|
import type {
|
|
9
9
|
BubblingEventHandler,
|
|
10
10
|
Double,
|
|
11
11
|
} from 'react-native/Libraries/Types/CodegenTypes';
|
|
12
12
|
|
|
13
|
-
interface TextChangeEvent {
|
|
13
|
+
export interface TextChangeEvent {
|
|
14
14
|
value: string;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
interface FocusEvent {
|
|
17
|
+
export interface FocusEvent {
|
|
18
18
|
// Empty event
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
interface BlurEvent {
|
|
21
|
+
export interface BlurEvent {
|
|
22
22
|
// Empty event
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
interface InputStyle {
|
|
25
|
+
export interface InputStyle {
|
|
26
26
|
color?: ColorValue;
|
|
27
27
|
fontSize?: Double;
|
|
28
28
|
fontFamily?: string;
|
|
@@ -38,6 +38,13 @@ interface InputStyle {
|
|
|
38
38
|
|
|
39
39
|
export interface NativeProps extends ViewProps {
|
|
40
40
|
value?: string;
|
|
41
|
+
placeholder?: string;
|
|
42
|
+
placeholderTextColor?: ColorValue;
|
|
43
|
+
selectionColor?: ColorValue;
|
|
44
|
+
autoComplete?: string;
|
|
45
|
+
autoCapitalize?: string;
|
|
46
|
+
keyboardType?: string;
|
|
47
|
+
returnKeyType?: string;
|
|
41
48
|
inputStyle?: InputStyle;
|
|
42
49
|
onTextChange?: BubblingEventHandler<Readonly<TextChangeEvent>>;
|
|
43
50
|
onFocus?: BubblingEventHandler<Readonly<FocusEvent>>;
|
package/lib/module/index.js
CHANGED
|
@@ -1,50 +1,52 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
import { forwardRef, useImperativeHandle, useRef } from 'react';
|
|
3
|
+
import { forwardRef, memo, useImperativeHandle, useRef } from 'react';
|
|
4
4
|
import { Platform, processColor, StyleSheet } from 'react-native';
|
|
5
5
|
import ControlledInputViewNativeComponent, { Commands } from './ControlledInputViewNativeComponent';
|
|
6
6
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
7
7
|
// All style props that Android handles via Compose instead of the native View layer
|
|
8
|
-
const
|
|
9
|
-
function
|
|
10
|
-
const
|
|
8
|
+
const androidComposeHandledKeys = ['color', 'fontSize', 'fontFamily', 'padding', 'paddingVertical', 'paddingHorizontal', 'paddingTop', 'paddingBottom', 'paddingLeft', 'paddingRight', 'borderWidth', 'borderRadius', 'borderColor', 'backgroundColor'];
|
|
9
|
+
function resolveAndroidComposeViewPadding(flat) {
|
|
10
|
+
const padding = flat.padding ?? 0;
|
|
11
11
|
return {
|
|
12
|
-
paddingTop: flat.paddingTop ?? flat.paddingVertical ??
|
|
13
|
-
paddingBottom: flat.paddingBottom ?? flat.paddingVertical ??
|
|
14
|
-
paddingLeft: flat.paddingLeft ?? flat.
|
|
15
|
-
paddingRight: flat.paddingRight ?? flat.
|
|
12
|
+
paddingTop: flat.paddingTop ?? flat.paddingVertical ?? padding,
|
|
13
|
+
paddingBottom: flat.paddingBottom ?? flat.paddingVertical ?? padding,
|
|
14
|
+
paddingLeft: flat.paddingLeft ?? flat.paddingHorizontal ?? padding,
|
|
15
|
+
paddingRight: flat.paddingRight ?? flat.paddingHorizontal ?? padding
|
|
16
16
|
};
|
|
17
17
|
}
|
|
18
|
-
export const ControlledInputView = /*#__PURE__*/forwardRef(({
|
|
18
|
+
export const ControlledInputView = /*#__PURE__*/memo(/*#__PURE__*/forwardRef(({
|
|
19
19
|
style,
|
|
20
20
|
onTextChange,
|
|
21
|
+
selectionColor,
|
|
22
|
+
placeholderTextColor,
|
|
21
23
|
...rest
|
|
22
24
|
}, ref) => {
|
|
23
25
|
const nativeRef = useRef(null);
|
|
24
|
-
const
|
|
26
|
+
const flattenedStyle = StyleSheet.flatten(style) ?? {};
|
|
25
27
|
let viewStyle;
|
|
26
28
|
let inputStyle;
|
|
27
29
|
if (Platform.OS === 'android') {
|
|
28
|
-
viewStyle = Object.fromEntries(Object.entries(
|
|
29
|
-
const hasPadding =
|
|
30
|
+
viewStyle = Object.fromEntries(Object.entries(flattenedStyle).filter(([k]) => !androidComposeHandledKeys.includes(k)));
|
|
31
|
+
const hasPadding = Object.entries(flattenedStyle).some(([k, v]) => k.includes('padding') && v != null);
|
|
30
32
|
inputStyle = {
|
|
31
|
-
color:
|
|
32
|
-
fontSize:
|
|
33
|
-
fontFamily:
|
|
34
|
-
...(hasPadding ?
|
|
35
|
-
borderWidth:
|
|
36
|
-
borderRadius:
|
|
37
|
-
borderColor:
|
|
38
|
-
backgroundColor:
|
|
33
|
+
color: flattenedStyle.color,
|
|
34
|
+
fontSize: flattenedStyle.fontSize,
|
|
35
|
+
fontFamily: flattenedStyle.fontFamily,
|
|
36
|
+
...(hasPadding ? resolveAndroidComposeViewPadding(flattenedStyle) : {}),
|
|
37
|
+
borderWidth: flattenedStyle.borderWidth,
|
|
38
|
+
borderRadius: flattenedStyle.borderRadius,
|
|
39
|
+
borderColor: flattenedStyle.borderColor,
|
|
40
|
+
backgroundColor: flattenedStyle.backgroundColor
|
|
39
41
|
};
|
|
40
42
|
} else {
|
|
41
43
|
const {
|
|
42
44
|
color,
|
|
43
45
|
fontSize,
|
|
44
46
|
fontFamily,
|
|
45
|
-
...
|
|
46
|
-
} =
|
|
47
|
-
viewStyle =
|
|
47
|
+
...iosViewStyle
|
|
48
|
+
} = flattenedStyle;
|
|
49
|
+
viewStyle = iosViewStyle;
|
|
48
50
|
const hasTextStyle = color != null || fontSize != null || fontFamily != null;
|
|
49
51
|
inputStyle = hasTextStyle ? {
|
|
50
52
|
color: color != null ? processColor(color) : undefined,
|
|
@@ -52,6 +54,11 @@ export const ControlledInputView = /*#__PURE__*/forwardRef(({
|
|
|
52
54
|
fontFamily
|
|
53
55
|
} : undefined;
|
|
54
56
|
}
|
|
57
|
+
const handleTextChange = e => {
|
|
58
|
+
if (onTextChange) {
|
|
59
|
+
onTextChange(e.nativeEvent.value);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
55
62
|
useImperativeHandle(ref, () => ({
|
|
56
63
|
blur: () => {
|
|
57
64
|
if (!nativeRef.current) return;
|
|
@@ -68,11 +75,14 @@ export const ControlledInputView = /*#__PURE__*/forwardRef(({
|
|
|
68
75
|
}));
|
|
69
76
|
return /*#__PURE__*/_jsx(ControlledInputViewNativeComponent, {
|
|
70
77
|
...rest,
|
|
78
|
+
placeholderTextColor: placeholderTextColor,
|
|
79
|
+
selectionColor: selectionColor,
|
|
71
80
|
style: viewStyle,
|
|
72
81
|
inputStyle: inputStyle,
|
|
73
|
-
onTextChange:
|
|
82
|
+
onTextChange: handleTextChange,
|
|
74
83
|
ref: nativeRef
|
|
75
84
|
});
|
|
76
|
-
});
|
|
85
|
+
}));
|
|
86
|
+
ControlledInputView.displayName = 'ControlledInputView';
|
|
77
87
|
export * from './ControlledInputViewNativeComponent';
|
|
78
88
|
//# sourceMappingURL=index.js.map
|
package/lib/module/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["forwardRef","useImperativeHandle","useRef","Platform","processColor","StyleSheet","ControlledInputViewNativeComponent","Commands","jsx","_jsx","
|
|
1
|
+
{"version":3,"names":["forwardRef","memo","useImperativeHandle","useRef","Platform","processColor","StyleSheet","ControlledInputViewNativeComponent","Commands","jsx","_jsx","androidComposeHandledKeys","resolveAndroidComposeViewPadding","flat","padding","paddingTop","paddingVertical","paddingBottom","paddingLeft","paddingHorizontal","paddingRight","ControlledInputView","style","onTextChange","selectionColor","placeholderTextColor","rest","ref","nativeRef","flattenedStyle","flatten","viewStyle","inputStyle","OS","Object","fromEntries","entries","filter","k","includes","hasPadding","some","v","color","fontSize","fontFamily","borderWidth","borderRadius","borderColor","backgroundColor","iosViewStyle","hasTextStyle","undefined","handleTextChange","e","nativeEvent","value","blur","current","focus","displayName"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type ColorValue, type HostComponent, type ViewProps } from 'react-native';
|
|
2
2
|
import type { BubblingEventHandler, Double } from 'react-native/Libraries/Types/CodegenTypes';
|
|
3
|
-
interface TextChangeEvent {
|
|
3
|
+
export interface TextChangeEvent {
|
|
4
4
|
value: string;
|
|
5
5
|
}
|
|
6
|
-
interface FocusEvent {
|
|
6
|
+
export interface FocusEvent {
|
|
7
7
|
}
|
|
8
|
-
interface BlurEvent {
|
|
8
|
+
export interface BlurEvent {
|
|
9
9
|
}
|
|
10
|
-
interface InputStyle {
|
|
10
|
+
export interface InputStyle {
|
|
11
11
|
color?: ColorValue;
|
|
12
12
|
fontSize?: Double;
|
|
13
13
|
fontFamily?: string;
|
|
@@ -22,6 +22,13 @@ interface InputStyle {
|
|
|
22
22
|
}
|
|
23
23
|
export interface NativeProps extends ViewProps {
|
|
24
24
|
value?: string;
|
|
25
|
+
placeholder?: string;
|
|
26
|
+
placeholderTextColor?: ColorValue;
|
|
27
|
+
selectionColor?: ColorValue;
|
|
28
|
+
autoComplete?: string;
|
|
29
|
+
autoCapitalize?: string;
|
|
30
|
+
keyboardType?: string;
|
|
31
|
+
returnKeyType?: string;
|
|
25
32
|
inputStyle?: InputStyle;
|
|
26
33
|
onTextChange?: BubblingEventHandler<Readonly<TextChangeEvent>>;
|
|
27
34
|
onFocus?: BubblingEventHandler<Readonly<FocusEvent>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ControlledInputViewNativeComponent.d.ts","sourceRoot":"","sources":["../../../src/ControlledInputViewNativeComponent.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,
|
|
1
|
+
{"version":3,"file":"ControlledInputViewNativeComponent.d.ts","sourceRoot":"","sources":["../../../src/ControlledInputViewNativeComponent.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAEtB,OAAO,KAAK,EACV,oBAAoB,EACpB,MAAM,EACP,MAAM,2CAA2C,CAAC;AAEnD,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,UAAU;CAE1B;AAED,MAAM,WAAW,SAAS;CAEzB;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,WAAY,SAAQ,SAAS;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oBAAoB,CAAC,EAAE,UAAU,CAAC;IAClC,cAAc,CAAC,EAAE,UAAU,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,YAAY,CAAC,EAAE,oBAAoB,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC,CAAC;IAC/D,OAAO,CAAC,EAAE,oBAAoB,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC;IACrD,MAAM,CAAC,EAAE,oBAAoB,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;CACpD;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,UAAU,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,KAAK,IAAI,CAAC;IACvE,IAAI,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,UAAU,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,KAAK,IAAI,CAAC;CACvE;AAED,eAAO,MAAM,QAAQ,EAAE,cAErB,CAAC;;AAEH,wBAA0E"}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
+
import { type TextInputProps } from 'react-native';
|
|
1
2
|
import { type NativeProps } from './ControlledInputViewNativeComponent';
|
|
2
3
|
export interface ControlledInputViewRef {
|
|
3
4
|
blur: () => void;
|
|
4
5
|
focus: () => void;
|
|
5
6
|
}
|
|
6
|
-
|
|
7
|
+
type ForwardedTextInputProps = Pick<TextInputProps, 'autoComplete' | 'autoCapitalize' | 'keyboardType' | 'returnKeyType' | 'placeholder' | 'placeholderTextColor' | 'selectionColor'>;
|
|
8
|
+
export type ControlledInputViewProps = Omit<NativeProps, 'inputStyle' | 'onTextChange' | keyof ForwardedTextInputProps> & ForwardedTextInputProps & {
|
|
7
9
|
onTextChange?: (value: string) => void;
|
|
8
10
|
};
|
|
9
|
-
export declare const ControlledInputView: import("react").
|
|
11
|
+
export declare const ControlledInputView: import("react").NamedExoticComponent<Omit<NativeProps, "placeholder" | "placeholderTextColor" | "selectionColor" | "autoComplete" | "autoCapitalize" | "keyboardType" | "returnKeyType" | "inputStyle" | "onTextChange"> & ForwardedTextInputProps & {
|
|
10
12
|
onTextChange?: (value: string) => void;
|
|
11
13
|
} & import("react").RefAttributes<ControlledInputViewRef>>;
|
|
12
14
|
export * from './ControlledInputViewNativeComponent';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAOA,OAAO,EAIL,KAAK,cAAc,EAGpB,MAAM,cAAc,CAAC;AACtB,OAA2C,EAEzC,KAAK,WAAW,EAEjB,MAAM,sCAAsC,CAAC;AAE9C,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAED,KAAK,uBAAuB,GAAG,IAAI,CACjC,cAAc,EACZ,cAAc,GACd,gBAAgB,GAChB,cAAc,GACd,eAAe,GACf,aAAa,GACb,sBAAsB,GACtB,gBAAgB,CACnB,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG,IAAI,CACzC,WAAW,EACX,YAAY,GAAG,cAAc,GAAG,MAAM,uBAAuB,CAC9D,GACC,uBAAuB,GAAG;IACxB,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACxC,CAAC;AA+BJ,eAAO,MAAM,mBAAmB;mBAhCb,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI;0DAwHzC,CAAC;AAIF,cAAc,sCAAsC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,28 +1,28 @@
|
|
|
1
1
|
import {
|
|
2
2
|
codegenNativeComponent,
|
|
3
|
-
type ViewProps,
|
|
4
3
|
type ColorValue,
|
|
5
4
|
type HostComponent,
|
|
5
|
+
type ViewProps,
|
|
6
6
|
} from 'react-native';
|
|
7
|
-
import codegenNativeCommands from 'react-native
|
|
7
|
+
import { codegenNativeCommands } from 'react-native';
|
|
8
8
|
import type {
|
|
9
9
|
BubblingEventHandler,
|
|
10
10
|
Double,
|
|
11
11
|
} from 'react-native/Libraries/Types/CodegenTypes';
|
|
12
12
|
|
|
13
|
-
interface TextChangeEvent {
|
|
13
|
+
export interface TextChangeEvent {
|
|
14
14
|
value: string;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
interface FocusEvent {
|
|
17
|
+
export interface FocusEvent {
|
|
18
18
|
// Empty event
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
interface BlurEvent {
|
|
21
|
+
export interface BlurEvent {
|
|
22
22
|
// Empty event
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
interface InputStyle {
|
|
25
|
+
export interface InputStyle {
|
|
26
26
|
color?: ColorValue;
|
|
27
27
|
fontSize?: Double;
|
|
28
28
|
fontFamily?: string;
|
|
@@ -38,6 +38,13 @@ interface InputStyle {
|
|
|
38
38
|
|
|
39
39
|
export interface NativeProps extends ViewProps {
|
|
40
40
|
value?: string;
|
|
41
|
+
placeholder?: string;
|
|
42
|
+
placeholderTextColor?: ColorValue;
|
|
43
|
+
selectionColor?: ColorValue;
|
|
44
|
+
autoComplete?: string;
|
|
45
|
+
autoCapitalize?: string;
|
|
46
|
+
keyboardType?: string;
|
|
47
|
+
returnKeyType?: string;
|
|
41
48
|
inputStyle?: InputStyle;
|
|
42
49
|
onTextChange?: BubblingEventHandler<Readonly<TextChangeEvent>>;
|
|
43
50
|
onFocus?: BubblingEventHandler<Readonly<FocusEvent>>;
|
package/src/index.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
forwardRef,
|
|
3
|
+
memo,
|
|
3
4
|
useImperativeHandle,
|
|
4
5
|
useRef,
|
|
5
6
|
type ElementRef,
|
|
@@ -8,11 +9,14 @@ import {
|
|
|
8
9
|
Platform,
|
|
9
10
|
processColor,
|
|
10
11
|
StyleSheet,
|
|
12
|
+
type TextInputProps,
|
|
11
13
|
type ViewStyle,
|
|
14
|
+
type TextStyle,
|
|
12
15
|
} from 'react-native';
|
|
13
16
|
import ControlledInputViewNativeComponent, {
|
|
14
17
|
Commands,
|
|
15
18
|
type NativeProps,
|
|
19
|
+
type TextChangeEvent,
|
|
16
20
|
} from './ControlledInputViewNativeComponent';
|
|
17
21
|
|
|
18
22
|
export interface ControlledInputViewRef {
|
|
@@ -20,15 +24,27 @@ export interface ControlledInputViewRef {
|
|
|
20
24
|
focus: () => void;
|
|
21
25
|
}
|
|
22
26
|
|
|
27
|
+
type ForwardedTextInputProps = Pick<
|
|
28
|
+
TextInputProps,
|
|
29
|
+
| 'autoComplete'
|
|
30
|
+
| 'autoCapitalize'
|
|
31
|
+
| 'keyboardType'
|
|
32
|
+
| 'returnKeyType'
|
|
33
|
+
| 'placeholder'
|
|
34
|
+
| 'placeholderTextColor'
|
|
35
|
+
| 'selectionColor'
|
|
36
|
+
>;
|
|
37
|
+
|
|
23
38
|
export type ControlledInputViewProps = Omit<
|
|
24
39
|
NativeProps,
|
|
25
|
-
'inputStyle' | 'onTextChange'
|
|
26
|
-
> &
|
|
27
|
-
|
|
28
|
-
|
|
40
|
+
'inputStyle' | 'onTextChange' | keyof ForwardedTextInputProps
|
|
41
|
+
> &
|
|
42
|
+
ForwardedTextInputProps & {
|
|
43
|
+
onTextChange?: (value: string) => void;
|
|
44
|
+
};
|
|
29
45
|
|
|
30
46
|
// All style props that Android handles via Compose instead of the native View layer
|
|
31
|
-
const
|
|
47
|
+
const androidComposeHandledKeys = [
|
|
32
48
|
'color',
|
|
33
49
|
'fontSize',
|
|
34
50
|
'fontFamily',
|
|
@@ -39,98 +55,113 @@ const ANDROID_HANDLED_KEYS = [
|
|
|
39
55
|
'paddingBottom',
|
|
40
56
|
'paddingLeft',
|
|
41
57
|
'paddingRight',
|
|
42
|
-
'paddingStart',
|
|
43
|
-
'paddingEnd',
|
|
44
58
|
'borderWidth',
|
|
45
59
|
'borderRadius',
|
|
46
60
|
'borderColor',
|
|
47
61
|
'backgroundColor',
|
|
48
62
|
];
|
|
49
63
|
|
|
50
|
-
function
|
|
51
|
-
const
|
|
64
|
+
function resolveAndroidComposeViewPadding(flat: Record<string, any>) {
|
|
65
|
+
const padding = flat.padding ?? 0;
|
|
66
|
+
|
|
52
67
|
return {
|
|
53
|
-
paddingTop: flat.paddingTop ?? flat.paddingVertical ??
|
|
54
|
-
paddingBottom: flat.paddingBottom ?? flat.paddingVertical ??
|
|
55
|
-
paddingLeft:
|
|
56
|
-
|
|
57
|
-
paddingRight:
|
|
58
|
-
flat.paddingRight ?? flat.paddingEnd ?? flat.paddingHorizontal ?? base,
|
|
68
|
+
paddingTop: flat.paddingTop ?? flat.paddingVertical ?? padding,
|
|
69
|
+
paddingBottom: flat.paddingBottom ?? flat.paddingVertical ?? padding,
|
|
70
|
+
paddingLeft: flat.paddingLeft ?? flat.paddingHorizontal ?? padding,
|
|
71
|
+
paddingRight: flat.paddingRight ?? flat.paddingHorizontal ?? padding,
|
|
59
72
|
};
|
|
60
73
|
}
|
|
61
74
|
|
|
62
|
-
export const ControlledInputView =
|
|
63
|
-
ControlledInputViewRef,
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
Commands.blur(nativeRef.current);
|
|
113
|
-
}
|
|
114
|
-
},
|
|
115
|
-
focus: () => {
|
|
116
|
-
if (!nativeRef.current) return;
|
|
117
|
-
if (Platform.OS === 'ios' || Platform.OS === 'android') {
|
|
118
|
-
Commands.focus(nativeRef.current);
|
|
119
|
-
}
|
|
120
|
-
},
|
|
121
|
-
}));
|
|
122
|
-
|
|
123
|
-
return (
|
|
124
|
-
<ControlledInputViewNativeComponent
|
|
125
|
-
{...rest}
|
|
126
|
-
style={viewStyle}
|
|
127
|
-
inputStyle={inputStyle as NativeProps['inputStyle']}
|
|
128
|
-
onTextChange={
|
|
129
|
-
onTextChange ? (e) => onTextChange(e.nativeEvent.value) : undefined
|
|
75
|
+
export const ControlledInputView = memo(
|
|
76
|
+
forwardRef<ControlledInputViewRef, ControlledInputViewProps>(
|
|
77
|
+
(
|
|
78
|
+
{ style, onTextChange, selectionColor, placeholderTextColor, ...rest },
|
|
79
|
+
ref
|
|
80
|
+
) => {
|
|
81
|
+
const nativeRef =
|
|
82
|
+
useRef<ElementRef<typeof ControlledInputViewNativeComponent>>(null);
|
|
83
|
+
|
|
84
|
+
const flattenedStyle = (StyleSheet.flatten(style) ?? {}) as TextStyle;
|
|
85
|
+
|
|
86
|
+
let viewStyle: ViewStyle;
|
|
87
|
+
let inputStyle: Record<string, any> | undefined;
|
|
88
|
+
|
|
89
|
+
if (Platform.OS === 'android') {
|
|
90
|
+
viewStyle = Object.fromEntries(
|
|
91
|
+
Object.entries(flattenedStyle).filter(
|
|
92
|
+
([k]) => !androidComposeHandledKeys.includes(k)
|
|
93
|
+
)
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
const hasPadding = Object.entries(flattenedStyle).some(
|
|
97
|
+
([k, v]) => k.includes('padding') && v != null
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
inputStyle = {
|
|
101
|
+
color: flattenedStyle.color,
|
|
102
|
+
fontSize: flattenedStyle.fontSize,
|
|
103
|
+
fontFamily: flattenedStyle.fontFamily,
|
|
104
|
+
...(hasPadding
|
|
105
|
+
? resolveAndroidComposeViewPadding(flattenedStyle)
|
|
106
|
+
: {}),
|
|
107
|
+
borderWidth: flattenedStyle.borderWidth,
|
|
108
|
+
borderRadius: flattenedStyle.borderRadius,
|
|
109
|
+
borderColor: flattenedStyle.borderColor,
|
|
110
|
+
backgroundColor: flattenedStyle.backgroundColor,
|
|
111
|
+
};
|
|
112
|
+
} else {
|
|
113
|
+
const { color, fontSize, fontFamily, ...iosViewStyle } = flattenedStyle;
|
|
114
|
+
viewStyle = iosViewStyle;
|
|
115
|
+
|
|
116
|
+
const hasTextStyle =
|
|
117
|
+
color != null || fontSize != null || fontFamily != null;
|
|
118
|
+
inputStyle = hasTextStyle
|
|
119
|
+
? {
|
|
120
|
+
color: color != null ? processColor(color) : undefined,
|
|
121
|
+
fontSize,
|
|
122
|
+
fontFamily,
|
|
123
|
+
}
|
|
124
|
+
: undefined;
|
|
130
125
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
})
|
|
126
|
+
|
|
127
|
+
const handleTextChange = (e: {
|
|
128
|
+
nativeEvent: Readonly<TextChangeEvent>;
|
|
129
|
+
}) => {
|
|
130
|
+
if (onTextChange) {
|
|
131
|
+
onTextChange(e.nativeEvent.value);
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
useImperativeHandle(ref, () => ({
|
|
136
|
+
blur: () => {
|
|
137
|
+
if (!nativeRef.current) return;
|
|
138
|
+
if (Platform.OS === 'ios' || Platform.OS === 'android') {
|
|
139
|
+
Commands.blur(nativeRef.current);
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
focus: () => {
|
|
143
|
+
if (!nativeRef.current) return;
|
|
144
|
+
if (Platform.OS === 'ios' || Platform.OS === 'android') {
|
|
145
|
+
Commands.focus(nativeRef.current);
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
}));
|
|
149
|
+
|
|
150
|
+
return (
|
|
151
|
+
<ControlledInputViewNativeComponent
|
|
152
|
+
{...rest}
|
|
153
|
+
placeholderTextColor={placeholderTextColor}
|
|
154
|
+
selectionColor={selectionColor}
|
|
155
|
+
style={viewStyle}
|
|
156
|
+
inputStyle={inputStyle}
|
|
157
|
+
onTextChange={handleTextChange}
|
|
158
|
+
ref={nativeRef}
|
|
159
|
+
/>
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
)
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
ControlledInputView.displayName = 'ControlledInputView';
|
|
135
166
|
|
|
136
167
|
export * from './ControlledInputViewNativeComponent';
|