11package com.mgcrea.reactnative.jetpackcompose
22
3+ import androidx.compose.foundation.layout.Arrangement
4+ import androidx.compose.foundation.layout.Row
35import androidx.compose.foundation.layout.fillMaxWidth
6+ import androidx.compose.foundation.text.KeyboardActions
7+ import androidx.compose.foundation.text.KeyboardOptions
8+ import androidx.compose.material.icons.Icons
9+ import androidx.compose.material.icons.filled.Check
10+ import androidx.compose.material.icons.filled.Clear
11+ import androidx.compose.material.icons.filled.Close
12+ import androidx.compose.material.icons.filled.Edit
13+ import androidx.compose.material.icons.filled.Email
14+ import androidx.compose.material.icons.filled.Info
15+ import androidx.compose.material.icons.filled.Lock
16+ import androidx.compose.material.icons.filled.Person
17+ import androidx.compose.material.icons.filled.Phone
18+ import androidx.compose.material.icons.filled.Search
19+ import androidx.compose.material.icons.filled.Warning
20+ import androidx.compose.material3.Icon
21+ import androidx.compose.material3.IconButton
422import androidx.compose.material3.OutlinedTextField
523import androidx.compose.material3.Text
624import androidx.compose.runtime.Composable
725import androidx.compose.runtime.mutableStateOf
826import androidx.compose.ui.Modifier
927import androidx.compose.ui.focus.onFocusChanged
28+ import androidx.compose.ui.graphics.vector.ImageVector
29+ import androidx.compose.ui.text.input.ImeAction
30+ import androidx.compose.ui.text.input.KeyboardCapitalization
31+ import androidx.compose.ui.text.input.KeyboardType
1032import androidx.compose.ui.text.input.PasswordVisualTransformation
1133import androidx.compose.ui.text.input.VisualTransformation
1234import com.facebook.react.uimanager.ThemedReactContext
1335import com.mgcrea.reactnative.jetpackcompose.core.InlineComposeView
1436import com.mgcrea.reactnative.jetpackcompose.events.TextFieldChangeEvent
1537import com.mgcrea.reactnative.jetpackcompose.events.TextFieldBlurEvent
1638import com.mgcrea.reactnative.jetpackcompose.events.TextFieldFocusEvent
39+ import com.mgcrea.reactnative.jetpackcompose.events.TextFieldSubmitEvent
40+ import com.mgcrea.reactnative.jetpackcompose.events.TextFieldTrailingIconPressEvent
1741
1842internal class TextFieldView (reactContext : ThemedReactContext ) :
1943 InlineComposeView (reactContext, TAG ) {
@@ -35,6 +59,15 @@ internal class TextFieldView(reactContext: ThemedReactContext) :
3559 private val _helperText = mutableStateOf<String ?>(null )
3660 private val _isFocused = mutableStateOf(false )
3761
62+ // New state for keyboard and icons
63+ private val _keyboardType = mutableStateOf(" default" )
64+ private val _returnKeyType = mutableStateOf(" done" )
65+ private val _autoCapitalize = mutableStateOf(" sentences" )
66+ private val _autoCorrect = mutableStateOf(true )
67+ private val _leadingIcon = mutableStateOf<String ?>(null )
68+ private val _trailingIcon = mutableStateOf<String ?>(null )
69+ private val _showCounter = mutableStateOf(true )
70+
3871 // Property setters called by ViewManager
3972 fun setValue (value : String? ) {
4073 _value .value = value ? : " "
@@ -76,6 +109,73 @@ internal class TextFieldView(reactContext: ThemedReactContext) :
76109 _helperText .value = value
77110 }
78111
112+ fun setKeyboardType (value : String? ) {
113+ _keyboardType .value = value ? : " default"
114+ }
115+
116+ fun setReturnKeyType (value : String? ) {
117+ _returnKeyType .value = value ? : " done"
118+ }
119+
120+ fun setAutoCapitalize (value : String? ) {
121+ _autoCapitalize .value = value ? : " sentences"
122+ }
123+
124+ fun setAutoCorrect (value : Boolean ) {
125+ _autoCorrect .value = value
126+ }
127+
128+ fun setLeadingIcon (value : String? ) {
129+ _leadingIcon .value = value
130+ }
131+
132+ fun setTrailingIcon (value : String? ) {
133+ _trailingIcon .value = value
134+ }
135+
136+ fun setShowCounter (value : Boolean ) {
137+ _showCounter .value = value
138+ }
139+
140+ private fun getKeyboardType (type : String ): KeyboardType = when (type) {
141+ " email" -> KeyboardType .Email
142+ " number" -> KeyboardType .Number
143+ " phone" -> KeyboardType .Phone
144+ " decimal" -> KeyboardType .Decimal
145+ " url" -> KeyboardType .Uri
146+ else -> KeyboardType .Text
147+ }
148+
149+ private fun getImeAction (type : String ): ImeAction = when (type) {
150+ " go" -> ImeAction .Go
151+ " next" -> ImeAction .Next
152+ " search" -> ImeAction .Search
153+ " send" -> ImeAction .Send
154+ else -> ImeAction .Done
155+ }
156+
157+ private fun getCapitalization (type : String ): KeyboardCapitalization = when (type) {
158+ " none" -> KeyboardCapitalization .None
159+ " words" -> KeyboardCapitalization .Words
160+ " characters" -> KeyboardCapitalization .Characters
161+ else -> KeyboardCapitalization .Sentences
162+ }
163+
164+ private fun getIcon (name : String? ): ImageVector ? = when (name?.lowercase()) {
165+ " search" -> Icons .Default .Search
166+ " email" -> Icons .Default .Email
167+ " phone" -> Icons .Default .Phone
168+ " person" -> Icons .Default .Person
169+ " lock" -> Icons .Default .Lock
170+ " clear" -> Icons .Default .Clear
171+ " close" -> Icons .Default .Close
172+ " check" -> Icons .Default .Check
173+ " edit" -> Icons .Default .Edit
174+ " info" -> Icons .Default .Info
175+ " warning" -> Icons .Default .Warning
176+ else -> null
177+ }
178+
79179 @Composable
80180 override fun ComposeContent () {
81181 val value = _value .value
@@ -88,6 +188,17 @@ internal class TextFieldView(reactContext: ThemedReactContext) :
88188 val secureTextEntry = _secureTextEntry .value
89189 val error = _error .value
90190 val helperText = _helperText .value
191+ val keyboardType = _keyboardType .value
192+ val returnKeyType = _returnKeyType .value
193+ val autoCapitalize = _autoCapitalize .value
194+ val autoCorrect = _autoCorrect .value
195+ val leadingIconName = _leadingIcon .value
196+ val trailingIconName = _trailingIcon .value
197+ val showCounter = _showCounter .value
198+
199+ val onSubmit = {
200+ dispatchEvent(TextFieldSubmitEvent (getSurfaceId(), id))
201+ }
91202
92203 OutlinedTextField (
93204 modifier = Modifier
@@ -125,12 +236,53 @@ internal class TextFieldView(reactContext: ThemedReactContext) :
125236 isError = error,
126237 label = label?.let { { Text (it) } },
127238 placeholder = placeholder?.let { { Text (it, maxLines = 1 ) } },
128- supportingText = helperText?.let { { Text (it) } },
239+ supportingText = if (helperText != null || (showCounter && maxLength != null )) {
240+ {
241+ Row (
242+ modifier = Modifier .fillMaxWidth(),
243+ horizontalArrangement = Arrangement .SpaceBetween
244+ ) {
245+ Text (helperText ? : " " )
246+ if (showCounter && maxLength != null ) {
247+ Text (" ${value.length} /$maxLength " )
248+ }
249+ }
250+ }
251+ } else null ,
129252 visualTransformation = if (secureTextEntry) {
130253 PasswordVisualTransformation ()
131254 } else {
132255 VisualTransformation .None
133256 },
257+ keyboardOptions = KeyboardOptions (
258+ keyboardType = getKeyboardType(keyboardType),
259+ imeAction = getImeAction(returnKeyType),
260+ capitalization = getCapitalization(autoCapitalize),
261+ autoCorrectEnabled = autoCorrect
262+ ),
263+ keyboardActions = KeyboardActions (
264+ onDone = { onSubmit() },
265+ onGo = { onSubmit() },
266+ onNext = { onSubmit() },
267+ onSearch = { onSubmit() },
268+ onSend = { onSubmit() }
269+ ),
270+ leadingIcon = leadingIconName?.let { iconName ->
271+ getIcon(iconName)?.let { icon ->
272+ { Icon (icon, contentDescription = iconName) }
273+ }
274+ },
275+ trailingIcon = trailingIconName?.let { iconName ->
276+ getIcon(iconName)?.let { icon ->
277+ {
278+ IconButton (onClick = {
279+ dispatchEvent(TextFieldTrailingIconPressEvent (getSurfaceId(), id))
280+ }) {
281+ Icon (icon, contentDescription = iconName)
282+ }
283+ }
284+ }
285+ },
134286 )
135287 }
136288}
0 commit comments