react-native-controlled-input 0.9.1 → 0.10.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/android/src/main/java/com/controlledinput/ControlledInputView.kt +5 -3
- package/android/src/main/java/com/controlledinput/ControlledInputViewManager.kt +20 -0
- package/android/src/main/java/com/controlledinput/JetpackComposeView.kt +84 -19
- package/android/src/main/java/com/controlledinput/JetpackComposeViewModel.kt +80 -0
- package/ios/ControlledInputView.mm +16 -0
- package/ios/RNControlledInput.swift +172 -0
- package/lib/module/ControlledInputViewNativeComponent.ts +10 -6
- package/lib/module/index.js +31 -25
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/ControlledInputViewNativeComponent.d.ts +9 -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 +10 -6
- package/src/index.tsx +107 -87
|
@@ -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,10 @@ class ControlledInputView : LinearLayout {
|
|
|
96
94
|
JetpackComposeView(
|
|
97
95
|
value = value,
|
|
98
96
|
inputStyle = viewModel.inputStyle,
|
|
97
|
+
autoComplete = viewModel.autoComplete,
|
|
98
|
+
autoCapitalize = viewModel.autoCapitalize,
|
|
99
|
+
keyboardType = viewModel.keyboardType,
|
|
100
|
+
returnKeyType = viewModel.returnKeyType,
|
|
99
101
|
onTextChange = { value ->
|
|
100
102
|
val surfaceId = UIManagerHelper.getSurfaceId(context)
|
|
101
103
|
val viewId = this.id
|
|
@@ -140,4 +142,4 @@ class ControlledInputView : LinearLayout {
|
|
|
140
142
|
|
|
141
143
|
}
|
|
142
144
|
}
|
|
143
|
-
}
|
|
145
|
+
}
|
|
@@ -38,6 +38,26 @@ class ControlledInputViewManager : SimpleViewManager<ControlledInputView>(),
|
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
@ReactProp(name = "autoComplete")
|
|
42
|
+
override fun setAutoComplete(view: ControlledInputView, autoComplete: String?) {
|
|
43
|
+
view.viewModel.setAutoCompleteWithAutofill(view, autoComplete)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@ReactProp(name = "autoCapitalize")
|
|
47
|
+
override fun setAutoCapitalize(view: ControlledInputView, autoCapitalize: String?) {
|
|
48
|
+
view.viewModel.setAutoCapitalize(autoCapitalize)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@ReactProp(name = "keyboardType")
|
|
52
|
+
override fun setKeyboardType(view: ControlledInputView, keyboardType: String?) {
|
|
53
|
+
view.viewModel.setKeyboardType(keyboardType)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@ReactProp(name = "returnKeyType")
|
|
57
|
+
override fun setReturnKeyType(view: ControlledInputView, returnKeyType: String?) {
|
|
58
|
+
view.viewModel.setReturnKeyType(returnKeyType)
|
|
59
|
+
}
|
|
60
|
+
|
|
41
61
|
@ReactProp(name = "inputStyle")
|
|
42
62
|
override fun setInputStyle(view: ControlledInputView, inputStyle: ReadableMap?) {
|
|
43
63
|
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
|
|
@@ -29,12 +30,19 @@ import androidx.compose.ui.graphics.Color
|
|
|
29
30
|
import androidx.compose.ui.platform.LocalContext
|
|
30
31
|
import androidx.compose.ui.text.TextStyle
|
|
31
32
|
import androidx.compose.ui.text.font.FontFamily
|
|
33
|
+
import androidx.compose.ui.text.input.ImeAction
|
|
34
|
+
import androidx.compose.ui.text.input.KeyboardCapitalization
|
|
35
|
+
import androidx.compose.ui.text.input.KeyboardType
|
|
36
|
+
import androidx.compose.ui.autofill.AutofillNode
|
|
37
|
+
import androidx.compose.ui.autofill.AutofillType
|
|
38
|
+
import androidx.compose.ui.layout.onGloballyPositioned
|
|
39
|
+
import androidx.compose.ui.layout.boundsInWindow
|
|
40
|
+
import androidx.compose.ui.platform.LocalAutofill
|
|
41
|
+
import androidx.compose.ui.platform.LocalAutofillTree
|
|
32
42
|
import androidx.compose.ui.unit.sp
|
|
33
|
-
import androidx.lifecycle.ViewModel
|
|
34
43
|
import com.facebook.react.bridge.Arguments
|
|
35
44
|
import com.facebook.react.bridge.WritableMap
|
|
36
45
|
import com.facebook.react.uimanager.events.Event
|
|
37
|
-
import kotlinx.coroutines.flow.MutableStateFlow
|
|
38
46
|
import kotlinx.coroutines.flow.StateFlow
|
|
39
47
|
|
|
40
48
|
data class InputStyle(
|
|
@@ -55,6 +63,10 @@ data class InputStyle(
|
|
|
55
63
|
fun JetpackComposeView(
|
|
56
64
|
value: String,
|
|
57
65
|
inputStyle: StateFlow<InputStyle?>,
|
|
66
|
+
autoComplete: StateFlow<String?>,
|
|
67
|
+
autoCapitalize: StateFlow<String?>,
|
|
68
|
+
keyboardType: StateFlow<String?>,
|
|
69
|
+
returnKeyType: StateFlow<String?>,
|
|
58
70
|
onTextChange: (value: String) -> Unit,
|
|
59
71
|
onFocus: (() -> Unit)? = null,
|
|
60
72
|
onBlur: (() -> Unit)? = null,
|
|
@@ -62,8 +74,21 @@ fun JetpackComposeView(
|
|
|
62
74
|
) {
|
|
63
75
|
val state = remember { TextFieldState(value) }
|
|
64
76
|
val style by inputStyle.collectAsState()
|
|
77
|
+
val keyboardTypeValue by keyboardType.collectAsState()
|
|
78
|
+
val autoCapitalizeValue by autoCapitalize.collectAsState()
|
|
79
|
+
val returnKeyTypeValue by returnKeyType.collectAsState()
|
|
80
|
+
val autoCompleteValue by autoComplete.collectAsState()
|
|
65
81
|
val interactionSource = remember { MutableInteractionSource() }
|
|
66
82
|
|
|
83
|
+
val autofill = LocalAutofill.current
|
|
84
|
+
val autofillNode = remember {
|
|
85
|
+
AutofillNode(
|
|
86
|
+
autofillTypes = toAutofillTypes(autoCompleteValue),
|
|
87
|
+
onFill = { onTextChange(it) }
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
LocalAutofillTree.current += autofillNode
|
|
91
|
+
|
|
67
92
|
if (state.text.toString() != value) {
|
|
68
93
|
state.setTextAndPlaceCursorAtEnd(value)
|
|
69
94
|
}
|
|
@@ -73,9 +98,11 @@ fun JetpackComposeView(
|
|
|
73
98
|
when (interaction) {
|
|
74
99
|
is FocusInteraction.Focus -> {
|
|
75
100
|
onFocus?.invoke()
|
|
101
|
+
autofill?.requestAutofillForNode(autofillNode)
|
|
76
102
|
}
|
|
77
103
|
is FocusInteraction.Unfocus -> {
|
|
78
104
|
onBlur?.invoke()
|
|
105
|
+
autofill?.cancelAutofillForNode(autofillNode)
|
|
79
106
|
}
|
|
80
107
|
}
|
|
81
108
|
}
|
|
@@ -131,12 +158,20 @@ fun JetpackComposeView(
|
|
|
131
158
|
end = paddingRight,
|
|
132
159
|
bottom = paddingBottom,
|
|
133
160
|
)
|
|
161
|
+
.onGloballyPositioned {
|
|
162
|
+
autofillNode.boundingBox = it.boundsInWindow()
|
|
163
|
+
}
|
|
134
164
|
.focusRequester(focusRequester),
|
|
135
165
|
textStyle = TextStyle(
|
|
136
166
|
color = textColor,
|
|
137
167
|
fontSize = fontSize,
|
|
138
168
|
fontFamily = fontFamily,
|
|
139
169
|
),
|
|
170
|
+
keyboardOptions = KeyboardOptions(
|
|
171
|
+
capitalization = toComposeCapitalization(autoCapitalizeValue),
|
|
172
|
+
keyboardType = toComposeKeyboardType(keyboardTypeValue),
|
|
173
|
+
imeAction = toComposeImeAction(returnKeyTypeValue),
|
|
174
|
+
),
|
|
140
175
|
interactionSource = interactionSource,
|
|
141
176
|
decorator = { innerTextField ->
|
|
142
177
|
Box(
|
|
@@ -150,6 +185,53 @@ fun JetpackComposeView(
|
|
|
150
185
|
}
|
|
151
186
|
}
|
|
152
187
|
|
|
188
|
+
private fun toComposeKeyboardType(value: String?): KeyboardType = when (value) {
|
|
189
|
+
"ascii-capable" -> KeyboardType.Ascii
|
|
190
|
+
"numbers-and-punctuation" -> KeyboardType.Text
|
|
191
|
+
"url" -> KeyboardType.Uri
|
|
192
|
+
"number-pad", "numeric" -> KeyboardType.Number
|
|
193
|
+
"phone-pad" -> KeyboardType.Phone
|
|
194
|
+
"email-address" -> KeyboardType.Email
|
|
195
|
+
"decimal-pad" -> KeyboardType.Decimal
|
|
196
|
+
"visible-password" -> KeyboardType.Password
|
|
197
|
+
else -> KeyboardType.Text
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
private fun toComposeCapitalization(value: String?): KeyboardCapitalization = when (value) {
|
|
201
|
+
"none" -> KeyboardCapitalization.None
|
|
202
|
+
"characters" -> KeyboardCapitalization.Characters
|
|
203
|
+
"words" -> KeyboardCapitalization.Words
|
|
204
|
+
"sentences" -> KeyboardCapitalization.Sentences
|
|
205
|
+
else -> KeyboardCapitalization.Sentences
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
private fun toComposeImeAction(value: String?): ImeAction = when (value) {
|
|
209
|
+
"go" -> ImeAction.Go
|
|
210
|
+
"next" -> ImeAction.Next
|
|
211
|
+
"search" -> ImeAction.Search
|
|
212
|
+
"send" -> ImeAction.Send
|
|
213
|
+
"done" -> ImeAction.Done
|
|
214
|
+
"none" -> ImeAction.None
|
|
215
|
+
"previous" -> ImeAction.Previous
|
|
216
|
+
else -> ImeAction.Default
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
private fun toAutofillTypes(autoComplete: String?): List<AutofillType> = when (autoComplete) {
|
|
220
|
+
"email" -> listOf(AutofillType.EmailAddress)
|
|
221
|
+
"name", "given-name", "family-name", "additional-name" -> listOf(AutofillType.PersonFullName)
|
|
222
|
+
"username" -> listOf(AutofillType.Username)
|
|
223
|
+
"password", "new-password" -> listOf(AutofillType.Password)
|
|
224
|
+
"tel" -> listOf(AutofillType.PhoneNumber)
|
|
225
|
+
"postal-code" -> listOf(AutofillType.PostalCode)
|
|
226
|
+
"street-address" -> listOf(AutofillType.AddressStreet)
|
|
227
|
+
"cc-number" -> listOf(AutofillType.CreditCardNumber)
|
|
228
|
+
"cc-exp" -> listOf(AutofillType.CreditCardExpirationDate)
|
|
229
|
+
"cc-exp-month" -> listOf(AutofillType.CreditCardExpirationMonth)
|
|
230
|
+
"cc-exp-year" -> listOf(AutofillType.CreditCardExpirationYear)
|
|
231
|
+
"cc-csc" -> listOf(AutofillType.CreditCardSecurityCode)
|
|
232
|
+
else -> emptyList()
|
|
233
|
+
}
|
|
234
|
+
|
|
153
235
|
class TextChangeEvent(
|
|
154
236
|
surfaceId: Int,
|
|
155
237
|
viewId: Int,
|
|
@@ -198,20 +280,3 @@ class BlurEvent(
|
|
|
198
280
|
const val EVENT_NAME = "onBlur"
|
|
199
281
|
}
|
|
200
282
|
}
|
|
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,80 @@
|
|
|
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 _autoCapitalize = MutableStateFlow<String?>(null)
|
|
14
|
+
private val _keyboardType = MutableStateFlow<String?>(null)
|
|
15
|
+
private val _returnKeyType = MutableStateFlow<String?>(null)
|
|
16
|
+
|
|
17
|
+
val value: StateFlow<String> get() = _value
|
|
18
|
+
val inputStyle: StateFlow<InputStyle?> get() = _inputStyle
|
|
19
|
+
val autoComplete: StateFlow<String?> get() = _autoComplete
|
|
20
|
+
val autoCapitalize: StateFlow<String?> get() = _autoCapitalize
|
|
21
|
+
val keyboardType: StateFlow<String?> get() = _keyboardType
|
|
22
|
+
val returnKeyType: StateFlow<String?> get() = _returnKeyType
|
|
23
|
+
|
|
24
|
+
fun setValue(newValue: String) {
|
|
25
|
+
_value.value = newValue
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
fun setInputStyle(style: InputStyle?) {
|
|
29
|
+
_inputStyle.value = style
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
fun setAutoComplete(newValue: String?) {
|
|
33
|
+
_autoComplete.value = newValue
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
fun setAutoCompleteWithAutofill(hostView: View, newValue: String?) {
|
|
37
|
+
setAutoComplete(newValue)
|
|
38
|
+
|
|
39
|
+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
|
40
|
+
return
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
val hint = when (newValue) {
|
|
44
|
+
"email" -> View.AUTOFILL_HINT_EMAIL_ADDRESS
|
|
45
|
+
"name", "given-name", "family-name" -> View.AUTOFILL_HINT_NAME
|
|
46
|
+
"username" -> View.AUTOFILL_HINT_USERNAME
|
|
47
|
+
"password", "new-password" -> View.AUTOFILL_HINT_PASSWORD
|
|
48
|
+
"tel" -> View.AUTOFILL_HINT_PHONE
|
|
49
|
+
"postal-code" -> View.AUTOFILL_HINT_POSTAL_CODE
|
|
50
|
+
"street-address" -> View.AUTOFILL_HINT_POSTAL_ADDRESS
|
|
51
|
+
"cc-number" -> View.AUTOFILL_HINT_CREDIT_CARD_NUMBER
|
|
52
|
+
"cc-exp" -> View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE
|
|
53
|
+
"cc-exp-month" -> View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH
|
|
54
|
+
"cc-exp-year" -> View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR
|
|
55
|
+
"cc-csc" -> View.AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE
|
|
56
|
+
"additional-name" -> View.AUTOFILL_HINT_NAME
|
|
57
|
+
else -> null
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (hint == null || newValue == "off" || newValue.isNullOrEmpty()) {
|
|
61
|
+
hostView.setAutofillHints(*emptyArray())
|
|
62
|
+
return
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
hostView.importantForAutofill = View.IMPORTANT_FOR_AUTOFILL_YES
|
|
66
|
+
hostView.setAutofillHints(hint)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
fun setAutoCapitalize(newValue: String?) {
|
|
70
|
+
_autoCapitalize.value = newValue
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
fun setKeyboardType(newValue: String?) {
|
|
74
|
+
_keyboardType.value = newValue
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
fun setReturnKeyType(newValue: String?) {
|
|
78
|
+
_returnKeyType.value = newValue
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -52,6 +52,22 @@ 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
|
+
|
|
55
71
|
const auto &style = newViewProps.inputStyle;
|
|
56
72
|
const auto &oldStyle = oldViewProps.inputStyle;
|
|
57
73
|
|
|
@@ -32,6 +32,22 @@ 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
|
+
|
|
35
51
|
public override var canBecomeFirstResponder: Bool { true }
|
|
36
52
|
|
|
37
53
|
@objc public func focus() {
|
|
@@ -65,6 +81,10 @@ public class RNControlledInput: UIView, UITextFieldDelegate {
|
|
|
65
81
|
])
|
|
66
82
|
|
|
67
83
|
applyFont()
|
|
84
|
+
applyAutoComplete()
|
|
85
|
+
applyAutoCapitalize()
|
|
86
|
+
applyKeyboardType()
|
|
87
|
+
applyReturnKeyType()
|
|
68
88
|
}
|
|
69
89
|
|
|
70
90
|
public func textFieldDidBeginEditing(_ textField: UITextField) {
|
|
@@ -82,6 +102,12 @@ public class RNControlledInput: UIView, UITextFieldDelegate {
|
|
|
82
102
|
|
|
83
103
|
delegate?.controlledInputDidChangeText(self, value: newText)
|
|
84
104
|
|
|
105
|
+
// If the replacement string is more than one character, it's likely an autocomplete/paste
|
|
106
|
+
// In this case, we should let the UITextField update itself to handle it correctly
|
|
107
|
+
if string.count > 1 {
|
|
108
|
+
return true
|
|
109
|
+
}
|
|
110
|
+
|
|
85
111
|
return false
|
|
86
112
|
}
|
|
87
113
|
|
|
@@ -92,4 +118,150 @@ public class RNControlledInput: UIView, UITextFieldDelegate {
|
|
|
92
118
|
textField.font = UIFont.systemFont(ofSize: fontSize)
|
|
93
119
|
}
|
|
94
120
|
}
|
|
121
|
+
|
|
122
|
+
private func applyAutoComplete() {
|
|
123
|
+
guard let autoComplete else {
|
|
124
|
+
textField.textContentType = nil
|
|
125
|
+
return
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if autoComplete == "off" || autoComplete.isEmpty {
|
|
129
|
+
textField.textContentType = nil
|
|
130
|
+
return
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
let mappedValue: String
|
|
134
|
+
switch autoComplete {
|
|
135
|
+
case "email":
|
|
136
|
+
mappedValue = "emailAddress"
|
|
137
|
+
case "tel":
|
|
138
|
+
mappedValue = "telephoneNumber"
|
|
139
|
+
case "name":
|
|
140
|
+
mappedValue = "name"
|
|
141
|
+
case "given-name":
|
|
142
|
+
mappedValue = "givenName"
|
|
143
|
+
case "middle-name":
|
|
144
|
+
mappedValue = "middleName"
|
|
145
|
+
case "family-name":
|
|
146
|
+
mappedValue = "familyName"
|
|
147
|
+
case "username":
|
|
148
|
+
mappedValue = "username"
|
|
149
|
+
case "password":
|
|
150
|
+
mappedValue = "password"
|
|
151
|
+
case "new-password":
|
|
152
|
+
mappedValue = "newPassword"
|
|
153
|
+
case "one-time-code":
|
|
154
|
+
mappedValue = "oneTimeCode"
|
|
155
|
+
case "postal-code":
|
|
156
|
+
mappedValue = "postalCode"
|
|
157
|
+
case "street-address":
|
|
158
|
+
mappedValue = "fullStreetAddress"
|
|
159
|
+
case "country":
|
|
160
|
+
mappedValue = "countryName"
|
|
161
|
+
case "cc-number":
|
|
162
|
+
mappedValue = "creditCardNumber"
|
|
163
|
+
case "cc-csc":
|
|
164
|
+
mappedValue = "creditCardSecurityCode"
|
|
165
|
+
case "cc-exp":
|
|
166
|
+
mappedValue = "creditCardExpiration"
|
|
167
|
+
case "cc-exp-month":
|
|
168
|
+
mappedValue = "creditCardExpirationMonth"
|
|
169
|
+
case "cc-exp-year":
|
|
170
|
+
mappedValue = "creditCardExpirationYear"
|
|
171
|
+
case "birthdate-day":
|
|
172
|
+
mappedValue = "birthdateDay"
|
|
173
|
+
case "birthdate-month":
|
|
174
|
+
mappedValue = "birthdateMonth"
|
|
175
|
+
case "birthdate-year":
|
|
176
|
+
mappedValue = "birthdateYear"
|
|
177
|
+
case "url":
|
|
178
|
+
mappedValue = "URL"
|
|
179
|
+
default:
|
|
180
|
+
mappedValue = autoComplete
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
textField.textContentType = UITextContentType(rawValue: mappedValue)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
private func applyAutoCapitalize() {
|
|
187
|
+
guard let autoCapitalize else {
|
|
188
|
+
textField.autocapitalizationType = .sentences
|
|
189
|
+
return
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
switch autoCapitalize {
|
|
193
|
+
case "none":
|
|
194
|
+
textField.autocapitalizationType = .none
|
|
195
|
+
case "words":
|
|
196
|
+
textField.autocapitalizationType = .words
|
|
197
|
+
case "sentences":
|
|
198
|
+
textField.autocapitalizationType = .sentences
|
|
199
|
+
case "characters":
|
|
200
|
+
textField.autocapitalizationType = .allCharacters
|
|
201
|
+
default:
|
|
202
|
+
textField.autocapitalizationType = .sentences
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
private func applyKeyboardType() {
|
|
207
|
+
switch keyboardType {
|
|
208
|
+
case "ascii-capable":
|
|
209
|
+
textField.keyboardType = .asciiCapable
|
|
210
|
+
case "numbers-and-punctuation":
|
|
211
|
+
textField.keyboardType = .numbersAndPunctuation
|
|
212
|
+
case "url":
|
|
213
|
+
textField.keyboardType = .URL
|
|
214
|
+
case "number-pad":
|
|
215
|
+
textField.keyboardType = .numberPad
|
|
216
|
+
case "phone-pad":
|
|
217
|
+
textField.keyboardType = .phonePad
|
|
218
|
+
case "name-phone-pad":
|
|
219
|
+
textField.keyboardType = .namePhonePad
|
|
220
|
+
case "email-address":
|
|
221
|
+
textField.keyboardType = .emailAddress
|
|
222
|
+
case "decimal-pad":
|
|
223
|
+
textField.keyboardType = .decimalPad
|
|
224
|
+
case "twitter":
|
|
225
|
+
textField.keyboardType = .twitter
|
|
226
|
+
case "web-search":
|
|
227
|
+
textField.keyboardType = .webSearch
|
|
228
|
+
case "visible-password":
|
|
229
|
+
textField.keyboardType = .asciiCapable
|
|
230
|
+
case "numeric":
|
|
231
|
+
textField.keyboardType = .numbersAndPunctuation
|
|
232
|
+
default:
|
|
233
|
+
textField.keyboardType = .default
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
private func applyReturnKeyType() {
|
|
238
|
+
switch returnKeyType {
|
|
239
|
+
case "done":
|
|
240
|
+
textField.returnKeyType = .done
|
|
241
|
+
case "go":
|
|
242
|
+
textField.returnKeyType = .go
|
|
243
|
+
case "next":
|
|
244
|
+
textField.returnKeyType = .next
|
|
245
|
+
case "search":
|
|
246
|
+
textField.returnKeyType = .search
|
|
247
|
+
case "send":
|
|
248
|
+
textField.returnKeyType = .send
|
|
249
|
+
case "none":
|
|
250
|
+
textField.returnKeyType = .default
|
|
251
|
+
case "previous":
|
|
252
|
+
textField.returnKeyType = .default
|
|
253
|
+
case "route":
|
|
254
|
+
textField.returnKeyType = .route
|
|
255
|
+
case "yahoo":
|
|
256
|
+
textField.returnKeyType = .yahoo
|
|
257
|
+
case "emergency-call":
|
|
258
|
+
textField.returnKeyType = .emergencyCall
|
|
259
|
+
case "google":
|
|
260
|
+
textField.returnKeyType = .google
|
|
261
|
+
case "join":
|
|
262
|
+
textField.returnKeyType = .join
|
|
263
|
+
default:
|
|
264
|
+
textField.returnKeyType = .default
|
|
265
|
+
}
|
|
266
|
+
}
|
|
95
267
|
}
|
|
@@ -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,10 @@ interface InputStyle {
|
|
|
38
38
|
|
|
39
39
|
export interface NativeProps extends ViewProps {
|
|
40
40
|
value?: string;
|
|
41
|
+
autoComplete?: string;
|
|
42
|
+
autoCapitalize?: string;
|
|
43
|
+
keyboardType?: string;
|
|
44
|
+
returnKeyType?: string;
|
|
41
45
|
inputStyle?: InputStyle;
|
|
42
46
|
onTextChange?: BubblingEventHandler<Readonly<TextChangeEvent>>;
|
|
43
47
|
onFocus?: BubblingEventHandler<Readonly<FocusEvent>>;
|
package/lib/module/index.js
CHANGED
|
@@ -1,50 +1,50 @@
|
|
|
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
21
|
...rest
|
|
22
22
|
}, ref) => {
|
|
23
23
|
const nativeRef = useRef(null);
|
|
24
|
-
const
|
|
24
|
+
const flattenedStyle = StyleSheet.flatten(style) ?? {};
|
|
25
25
|
let viewStyle;
|
|
26
26
|
let inputStyle;
|
|
27
27
|
if (Platform.OS === 'android') {
|
|
28
|
-
viewStyle = Object.fromEntries(Object.entries(
|
|
29
|
-
const hasPadding =
|
|
28
|
+
viewStyle = Object.fromEntries(Object.entries(flattenedStyle).filter(([k]) => !androidComposeHandledKeys.includes(k)));
|
|
29
|
+
const hasPadding = Object.entries(flattenedStyle).some(([k, v]) => k.includes('padding') && v != null);
|
|
30
30
|
inputStyle = {
|
|
31
|
-
color:
|
|
32
|
-
fontSize:
|
|
33
|
-
fontFamily:
|
|
34
|
-
...(hasPadding ?
|
|
35
|
-
borderWidth:
|
|
36
|
-
borderRadius:
|
|
37
|
-
borderColor:
|
|
38
|
-
backgroundColor:
|
|
31
|
+
color: flattenedStyle.color,
|
|
32
|
+
fontSize: flattenedStyle.fontSize,
|
|
33
|
+
fontFamily: flattenedStyle.fontFamily,
|
|
34
|
+
...(hasPadding ? resolveAndroidComposeViewPadding(flattenedStyle) : {}),
|
|
35
|
+
borderWidth: flattenedStyle.borderWidth,
|
|
36
|
+
borderRadius: flattenedStyle.borderRadius,
|
|
37
|
+
borderColor: flattenedStyle.borderColor,
|
|
38
|
+
backgroundColor: flattenedStyle.backgroundColor
|
|
39
39
|
};
|
|
40
40
|
} else {
|
|
41
41
|
const {
|
|
42
42
|
color,
|
|
43
43
|
fontSize,
|
|
44
44
|
fontFamily,
|
|
45
|
-
...
|
|
46
|
-
} =
|
|
47
|
-
viewStyle =
|
|
45
|
+
...iosViewStyle
|
|
46
|
+
} = flattenedStyle;
|
|
47
|
+
viewStyle = iosViewStyle;
|
|
48
48
|
const hasTextStyle = color != null || fontSize != null || fontFamily != null;
|
|
49
49
|
inputStyle = hasTextStyle ? {
|
|
50
50
|
color: color != null ? processColor(color) : undefined,
|
|
@@ -52,6 +52,11 @@ export const ControlledInputView = /*#__PURE__*/forwardRef(({
|
|
|
52
52
|
fontFamily
|
|
53
53
|
} : undefined;
|
|
54
54
|
}
|
|
55
|
+
const handleTextChange = e => {
|
|
56
|
+
if (onTextChange) {
|
|
57
|
+
onTextChange(e.nativeEvent.value);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
55
60
|
useImperativeHandle(ref, () => ({
|
|
56
61
|
blur: () => {
|
|
57
62
|
if (!nativeRef.current) return;
|
|
@@ -70,9 +75,10 @@ export const ControlledInputView = /*#__PURE__*/forwardRef(({
|
|
|
70
75
|
...rest,
|
|
71
76
|
style: viewStyle,
|
|
72
77
|
inputStyle: inputStyle,
|
|
73
|
-
onTextChange:
|
|
78
|
+
onTextChange: handleTextChange,
|
|
74
79
|
ref: nativeRef
|
|
75
80
|
});
|
|
76
|
-
});
|
|
81
|
+
}));
|
|
82
|
+
ControlledInputView.displayName = 'ControlledInputView';
|
|
77
83
|
export * from './ControlledInputViewNativeComponent';
|
|
78
84
|
//# 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","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,10 @@ interface InputStyle {
|
|
|
22
22
|
}
|
|
23
23
|
export interface NativeProps extends ViewProps {
|
|
24
24
|
value?: string;
|
|
25
|
+
autoComplete?: string;
|
|
26
|
+
autoCapitalize?: string;
|
|
27
|
+
keyboardType?: string;
|
|
28
|
+
returnKeyType?: string;
|
|
25
29
|
inputStyle?: InputStyle;
|
|
26
30
|
onTextChange?: BubblingEventHandler<Readonly<TextChangeEvent>>;
|
|
27
31
|
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,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'>;
|
|
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, "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,EACd,cAAc,GAAG,gBAAgB,GAAG,cAAc,GAAG,eAAe,CACrE,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;0DAmHzC,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,10 @@ interface InputStyle {
|
|
|
38
38
|
|
|
39
39
|
export interface NativeProps extends ViewProps {
|
|
40
40
|
value?: string;
|
|
41
|
+
autoComplete?: string;
|
|
42
|
+
autoCapitalize?: string;
|
|
43
|
+
keyboardType?: string;
|
|
44
|
+
returnKeyType?: string;
|
|
41
45
|
inputStyle?: InputStyle;
|
|
42
46
|
onTextChange?: BubblingEventHandler<Readonly<TextChangeEvent>>;
|
|
43
47
|
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,21 @@ export interface ControlledInputViewRef {
|
|
|
20
24
|
focus: () => void;
|
|
21
25
|
}
|
|
22
26
|
|
|
27
|
+
type ForwardedTextInputProps = Pick<
|
|
28
|
+
TextInputProps,
|
|
29
|
+
'autoComplete' | 'autoCapitalize' | 'keyboardType' | 'returnKeyType'
|
|
30
|
+
>;
|
|
31
|
+
|
|
23
32
|
export type ControlledInputViewProps = Omit<
|
|
24
33
|
NativeProps,
|
|
25
|
-
'inputStyle' | 'onTextChange'
|
|
26
|
-
> &
|
|
27
|
-
|
|
28
|
-
|
|
34
|
+
'inputStyle' | 'onTextChange' | keyof ForwardedTextInputProps
|
|
35
|
+
> &
|
|
36
|
+
ForwardedTextInputProps & {
|
|
37
|
+
onTextChange?: (value: string) => void;
|
|
38
|
+
};
|
|
29
39
|
|
|
30
40
|
// All style props that Android handles via Compose instead of the native View layer
|
|
31
|
-
const
|
|
41
|
+
const androidComposeHandledKeys = [
|
|
32
42
|
'color',
|
|
33
43
|
'fontSize',
|
|
34
44
|
'fontFamily',
|
|
@@ -39,98 +49,108 @@ const ANDROID_HANDLED_KEYS = [
|
|
|
39
49
|
'paddingBottom',
|
|
40
50
|
'paddingLeft',
|
|
41
51
|
'paddingRight',
|
|
42
|
-
'paddingStart',
|
|
43
|
-
'paddingEnd',
|
|
44
52
|
'borderWidth',
|
|
45
53
|
'borderRadius',
|
|
46
54
|
'borderColor',
|
|
47
55
|
'backgroundColor',
|
|
48
56
|
];
|
|
49
57
|
|
|
50
|
-
function
|
|
51
|
-
const
|
|
58
|
+
function resolveAndroidComposeViewPadding(flat: Record<string, any>) {
|
|
59
|
+
const padding = flat.padding ?? 0;
|
|
60
|
+
|
|
52
61
|
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,
|
|
62
|
+
paddingTop: flat.paddingTop ?? flat.paddingVertical ?? padding,
|
|
63
|
+
paddingBottom: flat.paddingBottom ?? flat.paddingVertical ?? padding,
|
|
64
|
+
paddingLeft: flat.paddingLeft ?? flat.paddingHorizontal ?? padding,
|
|
65
|
+
paddingRight: flat.paddingRight ?? flat.paddingHorizontal ?? padding,
|
|
59
66
|
};
|
|
60
67
|
}
|
|
61
68
|
|
|
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
|
-
color
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
blur: () => {
|
|
110
|
-
if (!nativeRef.current) return;
|
|
111
|
-
if (Platform.OS === 'ios' || Platform.OS === 'android') {
|
|
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
|
|
69
|
+
export const ControlledInputView = memo(
|
|
70
|
+
forwardRef<ControlledInputViewRef, ControlledInputViewProps>(
|
|
71
|
+
({ style, onTextChange, ...rest }, ref) => {
|
|
72
|
+
const nativeRef =
|
|
73
|
+
useRef<ElementRef<typeof ControlledInputViewNativeComponent>>(null);
|
|
74
|
+
|
|
75
|
+
const flattenedStyle = (StyleSheet.flatten(style) ?? {}) as TextStyle;
|
|
76
|
+
|
|
77
|
+
let viewStyle: ViewStyle;
|
|
78
|
+
let inputStyle: Record<string, any> | undefined;
|
|
79
|
+
|
|
80
|
+
if (Platform.OS === 'android') {
|
|
81
|
+
viewStyle = Object.fromEntries(
|
|
82
|
+
Object.entries(flattenedStyle).filter(
|
|
83
|
+
([k]) => !androidComposeHandledKeys.includes(k)
|
|
84
|
+
)
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
const hasPadding = Object.entries(flattenedStyle).some(
|
|
88
|
+
([k, v]) => k.includes('padding') && v != null
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
inputStyle = {
|
|
92
|
+
color: flattenedStyle.color,
|
|
93
|
+
fontSize: flattenedStyle.fontSize,
|
|
94
|
+
fontFamily: flattenedStyle.fontFamily,
|
|
95
|
+
...(hasPadding
|
|
96
|
+
? resolveAndroidComposeViewPadding(flattenedStyle)
|
|
97
|
+
: {}),
|
|
98
|
+
borderWidth: flattenedStyle.borderWidth,
|
|
99
|
+
borderRadius: flattenedStyle.borderRadius,
|
|
100
|
+
borderColor: flattenedStyle.borderColor,
|
|
101
|
+
backgroundColor: flattenedStyle.backgroundColor,
|
|
102
|
+
};
|
|
103
|
+
} else {
|
|
104
|
+
const { color, fontSize, fontFamily, ...iosViewStyle } = flattenedStyle;
|
|
105
|
+
viewStyle = iosViewStyle;
|
|
106
|
+
|
|
107
|
+
const hasTextStyle =
|
|
108
|
+
color != null || fontSize != null || fontFamily != null;
|
|
109
|
+
inputStyle = hasTextStyle
|
|
110
|
+
? {
|
|
111
|
+
color: color != null ? processColor(color) : undefined,
|
|
112
|
+
fontSize,
|
|
113
|
+
fontFamily,
|
|
114
|
+
}
|
|
115
|
+
: undefined;
|
|
130
116
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
})
|
|
117
|
+
|
|
118
|
+
const handleTextChange = (e: {
|
|
119
|
+
nativeEvent: Readonly<TextChangeEvent>;
|
|
120
|
+
}) => {
|
|
121
|
+
if (onTextChange) {
|
|
122
|
+
onTextChange(e.nativeEvent.value);
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
useImperativeHandle(ref, () => ({
|
|
127
|
+
blur: () => {
|
|
128
|
+
if (!nativeRef.current) return;
|
|
129
|
+
if (Platform.OS === 'ios' || Platform.OS === 'android') {
|
|
130
|
+
Commands.blur(nativeRef.current);
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
focus: () => {
|
|
134
|
+
if (!nativeRef.current) return;
|
|
135
|
+
if (Platform.OS === 'ios' || Platform.OS === 'android') {
|
|
136
|
+
Commands.focus(nativeRef.current);
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
}));
|
|
140
|
+
|
|
141
|
+
return (
|
|
142
|
+
<ControlledInputViewNativeComponent
|
|
143
|
+
{...rest}
|
|
144
|
+
style={viewStyle}
|
|
145
|
+
inputStyle={inputStyle}
|
|
146
|
+
onTextChange={handleTextChange}
|
|
147
|
+
ref={nativeRef}
|
|
148
|
+
/>
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
)
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
ControlledInputView.displayName = 'ControlledInputView';
|
|
135
155
|
|
|
136
156
|
export * from './ControlledInputViewNativeComponent';
|