@expo/ui 56.0.16 → 56.0.17
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/CHANGELOG.md +22 -0
- package/android/build.gradle +2 -2
- package/android/src/main/java/expo/modules/ui/ExpoUIModule.kt +40 -6
- package/android/src/main/java/expo/modules/ui/HostView.kt +0 -2
- package/android/src/main/java/expo/modules/ui/ModifierRegistry.kt +20 -0
- package/android/src/main/java/expo/modules/ui/RNHostView.kt +182 -6
- package/android/src/main/java/expo/modules/ui/textfield/BasicTextField.kt +203 -0
- package/android/src/main/java/expo/modules/ui/{TextFieldView.kt → textfield/TextField.kt} +34 -248
- package/android/src/main/java/expo/modules/ui/textfield/TextFieldShared.kt +299 -0
- package/build/State/useNativeState.d.ts +8 -3
- package/build/State/useNativeState.d.ts.map +1 -1
- package/build/community/pager-view/PagerView.android.d.ts.map +1 -1
- package/build/jetpack-compose/TextField/BasicTextField.d.ts +36 -0
- package/build/jetpack-compose/TextField/BasicTextField.d.ts.map +1 -0
- package/build/jetpack-compose/TextField/TextField.d.ts +131 -0
- package/build/jetpack-compose/TextField/TextField.d.ts.map +1 -0
- package/build/jetpack-compose/TextField/index.d.ts +3 -244
- package/build/jetpack-compose/TextField/index.d.ts.map +1 -1
- package/build/jetpack-compose/TextField/shared.d.ts +171 -0
- package/build/jetpack-compose/TextField/shared.d.ts.map +1 -0
- package/build/jetpack-compose/index.d.ts +1 -1
- package/build/jetpack-compose/index.d.ts.map +1 -1
- package/build/jetpack-compose/modifiers/index.d.ts +11 -0
- package/build/jetpack-compose/modifiers/index.d.ts.map +1 -1
- package/build/swift-ui/Image/index.d.ts +3 -1
- package/build/swift-ui/Image/index.d.ts.map +1 -1
- package/build/swift-ui/modifiers/index.d.ts +33 -4
- package/build/swift-ui/modifiers/index.d.ts.map +1 -1
- package/build/universal/TextInput/index.android.d.ts.map +1 -1
- package/build/universal/TextInput/types.d.ts +5 -1
- package/build/universal/TextInput/types.d.ts.map +1 -1
- package/expo-module.config.json +1 -1
- package/ios/ImageView.swift +1 -5
- package/ios/Modifiers/ImageScaleModifier.swift +29 -0
- package/ios/Modifiers/OnGeometryChangeModifier.swift +8 -16
- package/ios/Modifiers/ViewModifierRegistry.swift +32 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/{56.0.16/expo.modules.ui-56.0.16-sources.jar → 56.0.17/expo.modules.ui-56.0.17-sources.jar} +0 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.17/expo.modules.ui-56.0.17-sources.jar.md5 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.17/expo.modules.ui-56.0.17-sources.jar.sha1 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.17/expo.modules.ui-56.0.17-sources.jar.sha256 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.17/expo.modules.ui-56.0.17-sources.jar.sha512 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.17/expo.modules.ui-56.0.17.aar +0 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.17/expo.modules.ui-56.0.17.aar.md5 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.17/expo.modules.ui-56.0.17.aar.sha1 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.17/expo.modules.ui-56.0.17.aar.sha256 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.17/expo.modules.ui-56.0.17.aar.sha512 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/{56.0.16/expo.modules.ui-56.0.16.module → 56.0.17/expo.modules.ui-56.0.17.module} +22 -22
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.17/expo.modules.ui-56.0.17.module.md5 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.17/expo.modules.ui-56.0.17.module.sha1 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.17/expo.modules.ui-56.0.17.module.sha256 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.17/expo.modules.ui-56.0.17.module.sha512 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/{56.0.16/expo.modules.ui-56.0.16.pom → 56.0.17/expo.modules.ui-56.0.17.pom} +1 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.17/expo.modules.ui-56.0.17.pom.md5 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.17/expo.modules.ui-56.0.17.pom.sha1 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.17/expo.modules.ui-56.0.17.pom.sha256 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.17/expo.modules.ui-56.0.17.pom.sha512 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml +4 -4
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.md5 +1 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha1 +1 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha256 +1 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha512 +1 -1
- package/package.json +3 -3
- package/src/State/index.fx.ts +4 -1
- package/src/State/useNativeState.ts +24 -5
- package/src/community/datetime-picker/DateTimePicker.tsx +1 -1
- package/src/community/menu/MenuView.ios.tsx +1 -1
- package/src/community/pager-view/PagerView.android.tsx +16 -2
- package/src/community/pager-view/PagerView.ios.tsx +1 -1
- package/src/community/picker/Picker.ios.tsx +1 -1
- package/src/community/slider/Slider.ios.tsx +1 -1
- package/src/jetpack-compose/TextField/BasicTextField.tsx +118 -0
- package/src/jetpack-compose/TextField/TextField.tsx +198 -0
- package/src/jetpack-compose/TextField/index.ts +19 -0
- package/src/jetpack-compose/TextField/{index.tsx → shared.ts} +71 -203
- package/src/jetpack-compose/index.ts +6 -0
- package/src/jetpack-compose/modifiers/index.ts +13 -0
- package/src/swift-ui/BottomSheet/index.tsx +1 -1
- package/src/swift-ui/Image/index.tsx +12 -3
- package/src/swift-ui/modifiers/index.ts +42 -5
- package/src/universal/TextInput/index.android.tsx +26 -60
- package/src/universal/TextInput/types.ts +5 -1
- package/android/src/main/java/expo/modules/ui/ShadowNodeSyncFlush.kt +0 -28
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.16/expo.modules.ui-56.0.16-sources.jar.md5 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.16/expo.modules.ui-56.0.16-sources.jar.sha1 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.16/expo.modules.ui-56.0.16-sources.jar.sha256 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.16/expo.modules.ui-56.0.16-sources.jar.sha512 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.16/expo.modules.ui-56.0.16.aar +0 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.16/expo.modules.ui-56.0.16.aar.md5 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.16/expo.modules.ui-56.0.16.aar.sha1 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.16/expo.modules.ui-56.0.16.aar.sha256 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.16/expo.modules.ui-56.0.16.aar.sha512 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.16/expo.modules.ui-56.0.16.module.md5 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.16/expo.modules.ui-56.0.16.module.sha1 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.16/expo.modules.ui-56.0.16.module.sha256 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.16/expo.modules.ui-56.0.16.module.sha512 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.16/expo.modules.ui-56.0.16.pom.md5 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.16/expo.modules.ui-56.0.16.pom.sha1 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.16/expo.modules.ui-56.0.16.pom.sha256 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.16/expo.modules.ui-56.0.16.pom.sha512 +0 -1
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
package expo.modules.ui
|
|
1
|
+
package expo.modules.ui.textfield
|
|
2
2
|
|
|
3
3
|
import android.graphics.Color
|
|
4
|
-
import androidx.compose.foundation.text.KeyboardActions
|
|
5
|
-
import androidx.compose.foundation.text.KeyboardOptions
|
|
6
4
|
import androidx.compose.foundation.text.selection.TextSelectionColors
|
|
7
5
|
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
|
8
6
|
import androidx.compose.material3.MaterialExpressiveTheme
|
|
@@ -13,35 +11,24 @@ import androidx.compose.material3.TextField
|
|
|
13
11
|
import androidx.compose.material3.TextFieldColors
|
|
14
12
|
import androidx.compose.material3.TextFieldDefaults
|
|
15
13
|
import androidx.compose.runtime.Composable
|
|
16
|
-
import androidx.compose.runtime.LaunchedEffect
|
|
17
|
-
import androidx.compose.runtime.mutableStateOf
|
|
18
|
-
import androidx.compose.runtime.remember
|
|
19
|
-
import androidx.compose.ui.focus.FocusRequester
|
|
20
|
-
import androidx.compose.ui.focus.focusRequester
|
|
21
|
-
import androidx.compose.ui.focus.onFocusChanged
|
|
22
|
-
import androidx.compose.ui.platform.LocalFocusManager
|
|
23
|
-
import androidx.compose.ui.text.TextRange
|
|
24
|
-
import androidx.compose.ui.text.TextStyle
|
|
25
|
-
import androidx.compose.ui.text.input.ImeAction
|
|
26
|
-
import androidx.compose.ui.text.input.KeyboardCapitalization
|
|
27
|
-
import androidx.compose.ui.text.input.KeyboardType
|
|
28
|
-
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
|
29
|
-
import androidx.compose.ui.text.input.TextFieldValue
|
|
30
|
-
import androidx.compose.ui.text.input.VisualTransformation
|
|
31
|
-
import androidx.compose.ui.text.style.TextAlign
|
|
32
|
-
import androidx.compose.ui.unit.TextUnit
|
|
33
|
-
import androidx.compose.ui.unit.sp
|
|
34
14
|
import expo.modules.kotlin.records.Field
|
|
35
15
|
import expo.modules.kotlin.records.Record
|
|
36
16
|
import expo.modules.kotlin.types.Enumerable
|
|
17
|
+
import expo.modules.kotlin.types.OptimizedRecord
|
|
37
18
|
import expo.modules.kotlin.views.AsyncFunctionHandle
|
|
38
19
|
import expo.modules.kotlin.views.AsyncFunctionHandle2
|
|
39
20
|
import expo.modules.kotlin.views.ComposeProps
|
|
40
21
|
import expo.modules.kotlin.views.FunctionalComposableScope
|
|
41
|
-
import expo.modules.kotlin.
|
|
22
|
+
import expo.modules.kotlin.views.OptimizedComposeProps
|
|
23
|
+
import expo.modules.ui.GenericEventPayload1
|
|
24
|
+
import expo.modules.ui.ModifierList
|
|
25
|
+
import expo.modules.ui.ShapeRecord
|
|
26
|
+
import expo.modules.ui.composeOrNull
|
|
27
|
+
import expo.modules.ui.findChildSlotView
|
|
28
|
+
import expo.modules.ui.renderSlot
|
|
29
|
+
import expo.modules.ui.shapeFromShapeRecord
|
|
42
30
|
import expo.modules.ui.state.ObservableState
|
|
43
31
|
import expo.modules.ui.state.WorkletCallback
|
|
44
|
-
import expo.modules.kotlin.views.OptimizedComposeProps
|
|
45
32
|
|
|
46
33
|
// region Records
|
|
47
34
|
|
|
@@ -50,25 +37,6 @@ enum class TextFieldVariant(val value: String) : Enumerable {
|
|
|
50
37
|
OUTLINED("outlined")
|
|
51
38
|
}
|
|
52
39
|
|
|
53
|
-
@OptimizedRecord
|
|
54
|
-
data class TextFieldKeyboardOptionsRecord(
|
|
55
|
-
@Field val capitalization: String? = null,
|
|
56
|
-
@Field val autoCorrectEnabled: Boolean? = null,
|
|
57
|
-
@Field val keyboardType: String? = null,
|
|
58
|
-
@Field val imeAction: String? = null
|
|
59
|
-
) : Record
|
|
60
|
-
|
|
61
|
-
@OptimizedRecord
|
|
62
|
-
data class TextFieldTextStyleRecord(
|
|
63
|
-
@Field val textAlign: TextAlignType? = null,
|
|
64
|
-
@Field val color: Color? = null,
|
|
65
|
-
@Field val fontSize: Float? = null,
|
|
66
|
-
@Field val fontFamily: String? = null,
|
|
67
|
-
@Field val fontWeight: TextFontWeight? = null,
|
|
68
|
-
@Field val lineHeight: Float? = null,
|
|
69
|
-
@Field val letterSpacing: Float? = null
|
|
70
|
-
) : Record
|
|
71
|
-
|
|
72
40
|
@OptimizedRecord
|
|
73
41
|
data class TextFieldColorsRecord(
|
|
74
42
|
// Text
|
|
@@ -132,21 +100,6 @@ data class TextFieldSelectionColorsRecord(
|
|
|
132
100
|
@Field val backgroundColor: Color? = null
|
|
133
101
|
) : Record
|
|
134
102
|
|
|
135
|
-
data class KeyboardActionEvent(
|
|
136
|
-
@Field val action: String,
|
|
137
|
-
@Field val value: String
|
|
138
|
-
) : Record
|
|
139
|
-
|
|
140
|
-
data class TextFieldSelectionPayload(
|
|
141
|
-
@Field val start: Int,
|
|
142
|
-
@Field val end: Int
|
|
143
|
-
) : Record
|
|
144
|
-
|
|
145
|
-
data class TextFieldValuePayload(
|
|
146
|
-
@Field val text: String,
|
|
147
|
-
@Field val selection: TextFieldSelectionPayload
|
|
148
|
-
) : Record
|
|
149
|
-
|
|
150
103
|
// endregion Records
|
|
151
104
|
|
|
152
105
|
// region Color builder
|
|
@@ -230,54 +183,6 @@ data class TextFieldProps(
|
|
|
230
183
|
|
|
231
184
|
// endregion Props
|
|
232
185
|
|
|
233
|
-
// region Mappers
|
|
234
|
-
|
|
235
|
-
private fun String?.toKeyboardType(): KeyboardType = when (this) {
|
|
236
|
-
"text" -> KeyboardType.Text
|
|
237
|
-
"number" -> KeyboardType.Number
|
|
238
|
-
"email" -> KeyboardType.Email
|
|
239
|
-
"phone" -> KeyboardType.Phone
|
|
240
|
-
"decimal" -> KeyboardType.Decimal
|
|
241
|
-
"password" -> KeyboardType.Password
|
|
242
|
-
"ascii" -> KeyboardType.Ascii
|
|
243
|
-
"uri" -> KeyboardType.Uri
|
|
244
|
-
"numberPassword" -> KeyboardType.NumberPassword
|
|
245
|
-
else -> KeyboardType.Text
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
private fun String?.toCapitalization(): KeyboardCapitalization = when (this) {
|
|
249
|
-
"characters" -> KeyboardCapitalization.Characters
|
|
250
|
-
"none" -> KeyboardCapitalization.None
|
|
251
|
-
"sentences" -> KeyboardCapitalization.Sentences
|
|
252
|
-
"words" -> KeyboardCapitalization.Words
|
|
253
|
-
else -> KeyboardCapitalization.None
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
private fun String?.toImeAction(): ImeAction = when (this) {
|
|
257
|
-
"default" -> ImeAction.Default
|
|
258
|
-
"none" -> ImeAction.None
|
|
259
|
-
"go" -> ImeAction.Go
|
|
260
|
-
"search" -> ImeAction.Search
|
|
261
|
-
"send" -> ImeAction.Send
|
|
262
|
-
"previous" -> ImeAction.Previous
|
|
263
|
-
"next" -> ImeAction.Next
|
|
264
|
-
"done" -> ImeAction.Done
|
|
265
|
-
else -> ImeAction.Default
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// endregion Mappers
|
|
269
|
-
|
|
270
|
-
// region Value helpers
|
|
271
|
-
|
|
272
|
-
private fun ObservableState.extractSelection(textLength: Int): TextRange {
|
|
273
|
-
val selMap = value as? Map<*, *>
|
|
274
|
-
val start = (selMap?.get("start") as? Number)?.toInt()?.coerceIn(0, textLength) ?: 0
|
|
275
|
-
val end = (selMap?.get("end") as? Number)?.toInt()?.coerceIn(0, textLength) ?: 0
|
|
276
|
-
return TextRange(start, end)
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// endregion Value helpers
|
|
280
|
-
|
|
281
186
|
// region View
|
|
282
187
|
|
|
283
188
|
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
|
@@ -294,31 +199,24 @@ fun FunctionalComposableScope.TextFieldContent(
|
|
|
294
199
|
onKeyboardActionTriggered: (KeyboardActionEvent) -> Unit,
|
|
295
200
|
onSelectionChanged: (TextFieldSelectionPayload) -> Unit
|
|
296
201
|
) {
|
|
297
|
-
val
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
val clampedEnd = end.coerceIn(0, text.length)
|
|
316
|
-
props.selection.value = mapOf("start" to clampedStart, "end" to clampedEnd)
|
|
317
|
-
}
|
|
318
|
-
clear.handle {
|
|
319
|
-
state.value = ""
|
|
320
|
-
props.selection.value = mapOf("start" to 0, "end" to 0)
|
|
321
|
-
}
|
|
202
|
+
val core = rememberTextFieldCore(
|
|
203
|
+
value = props.value,
|
|
204
|
+
selection = props.selection,
|
|
205
|
+
maxLength = props.maxLength,
|
|
206
|
+
autoFocus = props.autoFocus,
|
|
207
|
+
keyboardOptionsRecord = props.keyboardOptions,
|
|
208
|
+
modifiers = props.modifiers,
|
|
209
|
+
onValueChangeSync = props.onValueChangeSync,
|
|
210
|
+
setText = setText,
|
|
211
|
+
setSelection = setSelection,
|
|
212
|
+
clear = clear,
|
|
213
|
+
focus = focus,
|
|
214
|
+
blur = blur,
|
|
215
|
+
onValueChanged = onValueChanged,
|
|
216
|
+
onFocusChange = onFocusChange,
|
|
217
|
+
onKeyboardActionTriggered = onKeyboardActionTriggered,
|
|
218
|
+
onSelectionChanged = onSelectionChanged
|
|
219
|
+
)
|
|
322
220
|
|
|
323
221
|
// Slots
|
|
324
222
|
val label: (@Composable () -> Unit)? = findChildSlotView(view, "label")?.let { slot -> { slot.renderSlot() } }
|
|
@@ -329,58 +227,11 @@ fun FunctionalComposableScope.TextFieldContent(
|
|
|
329
227
|
val suffix: (@Composable () -> Unit)? = findChildSlotView(view, "suffix")?.let { slot -> { slot.renderSlot() } }
|
|
330
228
|
val supportingText: (@Composable () -> Unit)? = findChildSlotView(view, "supportingText")?.let { slot -> { slot.renderSlot() } }
|
|
331
229
|
|
|
332
|
-
// Keyboard
|
|
333
|
-
val kbOpts = props.keyboardOptions
|
|
334
|
-
val keyboardOptions = KeyboardOptions.Default.copy(
|
|
335
|
-
keyboardType = kbOpts?.keyboardType.toKeyboardType(),
|
|
336
|
-
autoCorrectEnabled = kbOpts?.autoCorrectEnabled ?: true,
|
|
337
|
-
capitalization = kbOpts?.capitalization.toCapitalization(),
|
|
338
|
-
imeAction = kbOpts?.imeAction.toImeAction()
|
|
339
|
-
)
|
|
340
|
-
val currentText = { state.value as? String ?: "" }
|
|
341
|
-
val keyboardActions = KeyboardActions(
|
|
342
|
-
onDone = {
|
|
343
|
-
defaultKeyboardAction(ImeAction.Done)
|
|
344
|
-
onKeyboardActionTriggered(KeyboardActionEvent("done", currentText()))
|
|
345
|
-
},
|
|
346
|
-
onGo = {
|
|
347
|
-
defaultKeyboardAction(ImeAction.Go)
|
|
348
|
-
onKeyboardActionTriggered(KeyboardActionEvent("go", currentText()))
|
|
349
|
-
},
|
|
350
|
-
onNext = {
|
|
351
|
-
defaultKeyboardAction(ImeAction.Next)
|
|
352
|
-
onKeyboardActionTriggered(KeyboardActionEvent("next", currentText()))
|
|
353
|
-
},
|
|
354
|
-
onPrevious = {
|
|
355
|
-
defaultKeyboardAction(ImeAction.Previous)
|
|
356
|
-
onKeyboardActionTriggered(KeyboardActionEvent("previous", currentText()))
|
|
357
|
-
},
|
|
358
|
-
onSearch = {
|
|
359
|
-
defaultKeyboardAction(ImeAction.Search)
|
|
360
|
-
onKeyboardActionTriggered(KeyboardActionEvent("search", currentText()))
|
|
361
|
-
},
|
|
362
|
-
onSend = {
|
|
363
|
-
defaultKeyboardAction(ImeAction.Send)
|
|
364
|
-
onKeyboardActionTriggered(KeyboardActionEvent("send", currentText()))
|
|
365
|
-
}
|
|
366
|
-
)
|
|
367
|
-
|
|
368
230
|
// Lines
|
|
369
231
|
val singleLine = props.singleLine
|
|
370
232
|
val maxLines = props.maxLines ?: if (singleLine) 1 else Int.MAX_VALUE
|
|
371
233
|
val minLines = props.minLines ?: 1
|
|
372
234
|
|
|
373
|
-
// Modifier
|
|
374
|
-
val modifier = ModifierRegistry.applyModifiers(props.modifiers, appContext, composableScope, globalEventDispatcher)
|
|
375
|
-
.focusRequester(focusRequester)
|
|
376
|
-
.onFocusChanged { focusState ->
|
|
377
|
-
onFocusChange(GenericEventPayload1(focusState.isFocused))
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
if (props.autoFocus) {
|
|
381
|
-
LaunchedEffect(Unit) { focusRequester.requestFocus() }
|
|
382
|
-
}
|
|
383
|
-
|
|
384
235
|
val isOutlined = props.variant == TextFieldVariant.OUTLINED
|
|
385
236
|
val shape = shapeFromShapeRecord(props.shape)
|
|
386
237
|
?: if (isOutlined) OutlinedTextFieldDefaults.shape else TextFieldDefaults.shape
|
|
@@ -402,73 +253,8 @@ fun FunctionalComposableScope.TextFieldContent(
|
|
|
402
253
|
}
|
|
403
254
|
} ?: baseColors
|
|
404
255
|
|
|
405
|
-
val
|
|
406
|
-
val
|
|
407
|
-
|
|
408
|
-
val localValue = remember { mutableStateOf(TextFieldValue(text, selection)) }
|
|
409
|
-
if (localValue.value.text != text || localValue.value.selection != selection) {
|
|
410
|
-
localValue.value = TextFieldValue(text, selection)
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
val value = localValue.value
|
|
414
|
-
|
|
415
|
-
val onValueChange: (TextFieldValue) -> Unit = { incoming ->
|
|
416
|
-
val new = props.maxLength?.let { max ->
|
|
417
|
-
if (incoming.text.length > max) {
|
|
418
|
-
val truncated = incoming.text.substring(0, max)
|
|
419
|
-
incoming.copy(
|
|
420
|
-
text = truncated,
|
|
421
|
-
selection = TextRange(
|
|
422
|
-
incoming.selection.start.coerceAtMost(max),
|
|
423
|
-
incoming.selection.end.coerceAtMost(max)
|
|
424
|
-
)
|
|
425
|
-
)
|
|
426
|
-
} else {
|
|
427
|
-
null
|
|
428
|
-
}
|
|
429
|
-
} ?: incoming
|
|
430
|
-
val prev = localValue.value
|
|
431
|
-
localValue.value = new
|
|
432
|
-
if (new.selection != prev.selection) {
|
|
433
|
-
val cur = props.selection.value as? Map<*, *>
|
|
434
|
-
val curStart = (cur?.get("start") as? Number)?.toInt()
|
|
435
|
-
val curEnd = (cur?.get("end") as? Number)?.toInt()
|
|
436
|
-
if (curStart != new.selection.start || curEnd != new.selection.end) {
|
|
437
|
-
props.selection.value = mapOf(
|
|
438
|
-
"start" to new.selection.start,
|
|
439
|
-
"end" to new.selection.end
|
|
440
|
-
)
|
|
441
|
-
}
|
|
442
|
-
onSelectionChanged(TextFieldSelectionPayload(new.selection.start, new.selection.end))
|
|
443
|
-
}
|
|
444
|
-
if (new.text != prev.text) {
|
|
445
|
-
state.value = new.text
|
|
446
|
-
val payload = TextFieldValuePayload(
|
|
447
|
-
text = new.text,
|
|
448
|
-
selection = TextFieldSelectionPayload(new.selection.start, new.selection.end)
|
|
449
|
-
)
|
|
450
|
-
onValueChanged(payload)
|
|
451
|
-
props.onValueChangeSync?.invoke(new.text)
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
val context = appContext.reactContext
|
|
456
|
-
val textStyle = props.textStyle?.let { textStyleProps ->
|
|
457
|
-
TextStyle(
|
|
458
|
-
color = colorToComposeColorOrNull(textStyleProps.color) ?: androidx.compose.ui.graphics.Color.Unspecified,
|
|
459
|
-
fontSize = textStyleProps.fontSize?.sp ?: TextUnit.Unspecified,
|
|
460
|
-
fontWeight = textStyleProps.fontWeight?.toComposeFontWeight(),
|
|
461
|
-
fontFamily = context?.let { resolveFontFamily(textStyleProps.fontFamily, it) },
|
|
462
|
-
letterSpacing = textStyleProps.letterSpacing?.sp ?: TextUnit.Unspecified,
|
|
463
|
-
lineHeight = textStyleProps.lineHeight?.sp ?: TextUnit.Unspecified,
|
|
464
|
-
textAlign = textStyleProps.textAlign?.toComposeTextAlign() ?: TextAlign.Unspecified
|
|
465
|
-
)
|
|
466
|
-
} ?: TextStyle.Default
|
|
467
|
-
|
|
468
|
-
val visualTransformation = when (props.visualTransformation) {
|
|
469
|
-
"password" -> PasswordVisualTransformation()
|
|
470
|
-
else -> VisualTransformation.None
|
|
471
|
-
}
|
|
256
|
+
val textStyle = props.textStyle.toTextStyle(appContext.reactContext)
|
|
257
|
+
val visualTransformation = props.visualTransformation.toVisualTransformation()
|
|
472
258
|
|
|
473
259
|
// Workaround (pending upstream fix, https://issuetracker.google.com/issues/519816993)
|
|
474
260
|
// the expressive motion scheme's spring overshoots >1f, and TextField's calculateHeight
|
|
@@ -477,25 +263,25 @@ fun FunctionalComposableScope.TextFieldContent(
|
|
|
477
263
|
MaterialExpressiveTheme(motionScheme = MotionScheme.standard()) {
|
|
478
264
|
if (isOutlined) {
|
|
479
265
|
OutlinedTextField(
|
|
480
|
-
value = value, onValueChange = onValueChange, modifier = modifier,
|
|
266
|
+
value = core.value, onValueChange = core.onValueChange, modifier = core.modifier,
|
|
481
267
|
enabled = props.enabled, readOnly = props.readOnly, textStyle = textStyle,
|
|
482
268
|
label = label, placeholder = placeholder,
|
|
483
269
|
leadingIcon = leadingIcon, trailingIcon = trailingIcon,
|
|
484
270
|
prefix = prefix, suffix = suffix, supportingText = supportingText,
|
|
485
271
|
isError = props.isError, visualTransformation = visualTransformation,
|
|
486
|
-
keyboardOptions = keyboardOptions, keyboardActions = keyboardActions,
|
|
272
|
+
keyboardOptions = core.keyboardOptions, keyboardActions = core.keyboardActions,
|
|
487
273
|
singleLine = singleLine, maxLines = maxLines, minLines = minLines,
|
|
488
274
|
shape = shape, colors = colors
|
|
489
275
|
)
|
|
490
276
|
} else {
|
|
491
277
|
TextField(
|
|
492
|
-
value = value, onValueChange = onValueChange, modifier = modifier,
|
|
278
|
+
value = core.value, onValueChange = core.onValueChange, modifier = core.modifier,
|
|
493
279
|
enabled = props.enabled, readOnly = props.readOnly, textStyle = textStyle,
|
|
494
280
|
label = label, placeholder = placeholder,
|
|
495
281
|
leadingIcon = leadingIcon, trailingIcon = trailingIcon,
|
|
496
282
|
prefix = prefix, suffix = suffix, supportingText = supportingText,
|
|
497
283
|
isError = props.isError, visualTransformation = visualTransformation,
|
|
498
|
-
keyboardOptions = keyboardOptions, keyboardActions = keyboardActions,
|
|
284
|
+
keyboardOptions = core.keyboardOptions, keyboardActions = core.keyboardActions,
|
|
499
285
|
singleLine = singleLine, maxLines = maxLines, minLines = minLines,
|
|
500
286
|
shape = shape, colors = colors
|
|
501
287
|
)
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
package expo.modules.ui.textfield
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.graphics.Color
|
|
5
|
+
import androidx.compose.foundation.text.KeyboardActions
|
|
6
|
+
import androidx.compose.foundation.text.KeyboardOptions
|
|
7
|
+
import androidx.compose.runtime.Composable
|
|
8
|
+
import androidx.compose.runtime.LaunchedEffect
|
|
9
|
+
import androidx.compose.runtime.mutableStateOf
|
|
10
|
+
import androidx.compose.runtime.remember
|
|
11
|
+
import androidx.compose.ui.Modifier
|
|
12
|
+
import androidx.compose.ui.focus.FocusRequester
|
|
13
|
+
import androidx.compose.ui.focus.focusRequester
|
|
14
|
+
import androidx.compose.ui.focus.onFocusChanged
|
|
15
|
+
import androidx.compose.ui.platform.LocalFocusManager
|
|
16
|
+
import androidx.compose.ui.text.TextRange
|
|
17
|
+
import androidx.compose.ui.text.TextStyle
|
|
18
|
+
import androidx.compose.ui.text.input.ImeAction
|
|
19
|
+
import androidx.compose.ui.text.input.KeyboardCapitalization
|
|
20
|
+
import androidx.compose.ui.text.input.KeyboardType
|
|
21
|
+
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
|
22
|
+
import androidx.compose.ui.text.input.TextFieldValue
|
|
23
|
+
import androidx.compose.ui.text.input.VisualTransformation
|
|
24
|
+
import androidx.compose.ui.text.style.TextAlign
|
|
25
|
+
import androidx.compose.ui.unit.TextUnit
|
|
26
|
+
import androidx.compose.ui.unit.sp
|
|
27
|
+
import expo.modules.kotlin.records.Field
|
|
28
|
+
import expo.modules.kotlin.records.Record
|
|
29
|
+
import expo.modules.kotlin.types.OptimizedRecord
|
|
30
|
+
import expo.modules.kotlin.views.AsyncFunctionHandle
|
|
31
|
+
import expo.modules.kotlin.views.AsyncFunctionHandle2
|
|
32
|
+
import expo.modules.kotlin.views.FunctionalComposableScope
|
|
33
|
+
import expo.modules.ui.GenericEventPayload1
|
|
34
|
+
import expo.modules.ui.ModifierList
|
|
35
|
+
import expo.modules.ui.ModifierRegistry
|
|
36
|
+
import expo.modules.ui.TextAlignType
|
|
37
|
+
import expo.modules.ui.TextFontWeight
|
|
38
|
+
import expo.modules.ui.colorToComposeColorOrNull
|
|
39
|
+
import expo.modules.ui.resolveFontFamily
|
|
40
|
+
import expo.modules.ui.state.ObservableState
|
|
41
|
+
import expo.modules.ui.state.WorkletCallback
|
|
42
|
+
|
|
43
|
+
// region Records
|
|
44
|
+
|
|
45
|
+
@OptimizedRecord
|
|
46
|
+
data class TextFieldKeyboardOptionsRecord(
|
|
47
|
+
@Field val capitalization: String? = null,
|
|
48
|
+
@Field val autoCorrectEnabled: Boolean? = null,
|
|
49
|
+
@Field val keyboardType: String? = null,
|
|
50
|
+
@Field val imeAction: String? = null
|
|
51
|
+
) : Record
|
|
52
|
+
|
|
53
|
+
@OptimizedRecord
|
|
54
|
+
data class TextFieldTextStyleRecord(
|
|
55
|
+
@Field val textAlign: TextAlignType? = null,
|
|
56
|
+
@Field val color: Color? = null,
|
|
57
|
+
@Field val fontSize: Float? = null,
|
|
58
|
+
@Field val fontFamily: String? = null,
|
|
59
|
+
@Field val fontWeight: TextFontWeight? = null,
|
|
60
|
+
@Field val lineHeight: Float? = null,
|
|
61
|
+
@Field val letterSpacing: Float? = null
|
|
62
|
+
) : Record
|
|
63
|
+
|
|
64
|
+
data class KeyboardActionEvent(
|
|
65
|
+
@Field val action: String,
|
|
66
|
+
@Field val value: String
|
|
67
|
+
) : Record
|
|
68
|
+
|
|
69
|
+
data class TextFieldSelectionPayload(
|
|
70
|
+
@Field val start: Int,
|
|
71
|
+
@Field val end: Int
|
|
72
|
+
) : Record
|
|
73
|
+
|
|
74
|
+
data class TextFieldValuePayload(
|
|
75
|
+
@Field val text: String,
|
|
76
|
+
@Field val selection: TextFieldSelectionPayload
|
|
77
|
+
) : Record
|
|
78
|
+
|
|
79
|
+
// endregion Records
|
|
80
|
+
|
|
81
|
+
// region Mappers
|
|
82
|
+
|
|
83
|
+
private fun String?.toKeyboardType(): KeyboardType = when (this) {
|
|
84
|
+
"text" -> KeyboardType.Text
|
|
85
|
+
"number" -> KeyboardType.Number
|
|
86
|
+
"email" -> KeyboardType.Email
|
|
87
|
+
"phone" -> KeyboardType.Phone
|
|
88
|
+
"decimal" -> KeyboardType.Decimal
|
|
89
|
+
"password" -> KeyboardType.Password
|
|
90
|
+
"ascii" -> KeyboardType.Ascii
|
|
91
|
+
"uri" -> KeyboardType.Uri
|
|
92
|
+
"numberPassword" -> KeyboardType.NumberPassword
|
|
93
|
+
else -> KeyboardType.Text
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
private fun String?.toCapitalization(): KeyboardCapitalization = when (this) {
|
|
97
|
+
"characters" -> KeyboardCapitalization.Characters
|
|
98
|
+
"none" -> KeyboardCapitalization.None
|
|
99
|
+
"sentences" -> KeyboardCapitalization.Sentences
|
|
100
|
+
"words" -> KeyboardCapitalization.Words
|
|
101
|
+
else -> KeyboardCapitalization.None
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private fun String?.toImeAction(): ImeAction = when (this) {
|
|
105
|
+
"default" -> ImeAction.Default
|
|
106
|
+
"none" -> ImeAction.None
|
|
107
|
+
"go" -> ImeAction.Go
|
|
108
|
+
"search" -> ImeAction.Search
|
|
109
|
+
"send" -> ImeAction.Send
|
|
110
|
+
"previous" -> ImeAction.Previous
|
|
111
|
+
"next" -> ImeAction.Next
|
|
112
|
+
"done" -> ImeAction.Done
|
|
113
|
+
else -> ImeAction.Default
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
internal fun String?.toVisualTransformation(): VisualTransformation = when (this) {
|
|
117
|
+
"password" -> PasswordVisualTransformation()
|
|
118
|
+
else -> VisualTransformation.None
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
internal fun TextFieldTextStyleRecord?.toTextStyle(context: Context?): TextStyle {
|
|
122
|
+
if (this == null) return TextStyle.Default
|
|
123
|
+
return TextStyle(
|
|
124
|
+
color = colorToComposeColorOrNull(color) ?: androidx.compose.ui.graphics.Color.Unspecified,
|
|
125
|
+
fontSize = fontSize?.sp ?: TextUnit.Unspecified,
|
|
126
|
+
fontWeight = fontWeight?.toComposeFontWeight(),
|
|
127
|
+
fontFamily = context?.let { resolveFontFamily(fontFamily, it) },
|
|
128
|
+
letterSpacing = letterSpacing?.sp ?: TextUnit.Unspecified,
|
|
129
|
+
lineHeight = lineHeight?.sp ?: TextUnit.Unspecified,
|
|
130
|
+
textAlign = textAlign?.toComposeTextAlign() ?: TextAlign.Unspecified
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// endregion Mappers
|
|
135
|
+
|
|
136
|
+
// region Value helpers
|
|
137
|
+
|
|
138
|
+
private fun ObservableState.extractSelection(textLength: Int): TextRange {
|
|
139
|
+
val selMap = value as? Map<*, *>
|
|
140
|
+
val start = (selMap?.get("start") as? Number)?.toInt()?.coerceIn(0, textLength) ?: 0
|
|
141
|
+
val end = (selMap?.get("end") as? Number)?.toInt()?.coerceIn(0, textLength) ?: 0
|
|
142
|
+
return TextRange(start, end)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// endregion Value helpers
|
|
146
|
+
|
|
147
|
+
// region Shared core
|
|
148
|
+
|
|
149
|
+
class TextFieldCore(
|
|
150
|
+
val value: TextFieldValue,
|
|
151
|
+
val onValueChange: (TextFieldValue) -> Unit,
|
|
152
|
+
val keyboardOptions: KeyboardOptions,
|
|
153
|
+
val keyboardActions: KeyboardActions,
|
|
154
|
+
val modifier: Modifier
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
@Composable
|
|
158
|
+
fun FunctionalComposableScope.rememberTextFieldCore(
|
|
159
|
+
value: ObservableState,
|
|
160
|
+
selection: ObservableState,
|
|
161
|
+
maxLength: Int?,
|
|
162
|
+
autoFocus: Boolean,
|
|
163
|
+
keyboardOptionsRecord: TextFieldKeyboardOptionsRecord?,
|
|
164
|
+
modifiers: ModifierList,
|
|
165
|
+
onValueChangeSync: WorkletCallback?,
|
|
166
|
+
setText: AsyncFunctionHandle<String>,
|
|
167
|
+
setSelection: AsyncFunctionHandle2<Int, Int>,
|
|
168
|
+
clear: AsyncFunctionHandle<Unit>,
|
|
169
|
+
focus: AsyncFunctionHandle<Unit>,
|
|
170
|
+
blur: AsyncFunctionHandle<Unit>,
|
|
171
|
+
onValueChanged: (TextFieldValuePayload) -> Unit,
|
|
172
|
+
onFocusChange: (GenericEventPayload1<Boolean>) -> Unit,
|
|
173
|
+
onKeyboardActionTriggered: (KeyboardActionEvent) -> Unit,
|
|
174
|
+
onSelectionChanged: (TextFieldSelectionPayload) -> Unit
|
|
175
|
+
): TextFieldCore {
|
|
176
|
+
val focusManager = LocalFocusManager.current
|
|
177
|
+
val focusRequester = remember { FocusRequester() }
|
|
178
|
+
val state = value
|
|
179
|
+
|
|
180
|
+
setText.handle { text ->
|
|
181
|
+
state.value = text
|
|
182
|
+
// setText moves the cursor to the end; use setSelection afterwards to override.
|
|
183
|
+
selection.value = mapOf("start" to text.length, "end" to text.length)
|
|
184
|
+
}
|
|
185
|
+
focus.handle {
|
|
186
|
+
focusRequester.requestFocus()
|
|
187
|
+
}
|
|
188
|
+
blur.handle {
|
|
189
|
+
focusManager.clearFocus()
|
|
190
|
+
}
|
|
191
|
+
setSelection.handle { start, end ->
|
|
192
|
+
val text = state.value as? String ?: ""
|
|
193
|
+
val clampedStart = start.coerceIn(0, text.length)
|
|
194
|
+
val clampedEnd = end.coerceIn(0, text.length)
|
|
195
|
+
selection.value = mapOf("start" to clampedStart, "end" to clampedEnd)
|
|
196
|
+
}
|
|
197
|
+
clear.handle {
|
|
198
|
+
state.value = ""
|
|
199
|
+
selection.value = mapOf("start" to 0, "end" to 0)
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Keyboard
|
|
203
|
+
val keyboardOptions = KeyboardOptions.Default.copy(
|
|
204
|
+
keyboardType = keyboardOptionsRecord?.keyboardType.toKeyboardType(),
|
|
205
|
+
autoCorrectEnabled = keyboardOptionsRecord?.autoCorrectEnabled ?: true,
|
|
206
|
+
capitalization = keyboardOptionsRecord?.capitalization.toCapitalization(),
|
|
207
|
+
imeAction = keyboardOptionsRecord?.imeAction.toImeAction()
|
|
208
|
+
)
|
|
209
|
+
val currentText = { state.value as? String ?: "" }
|
|
210
|
+
val keyboardActions = KeyboardActions(
|
|
211
|
+
onDone = {
|
|
212
|
+
defaultKeyboardAction(ImeAction.Done)
|
|
213
|
+
onKeyboardActionTriggered(KeyboardActionEvent("done", currentText()))
|
|
214
|
+
},
|
|
215
|
+
onGo = {
|
|
216
|
+
defaultKeyboardAction(ImeAction.Go)
|
|
217
|
+
onKeyboardActionTriggered(KeyboardActionEvent("go", currentText()))
|
|
218
|
+
},
|
|
219
|
+
onNext = {
|
|
220
|
+
defaultKeyboardAction(ImeAction.Next)
|
|
221
|
+
onKeyboardActionTriggered(KeyboardActionEvent("next", currentText()))
|
|
222
|
+
},
|
|
223
|
+
onPrevious = {
|
|
224
|
+
defaultKeyboardAction(ImeAction.Previous)
|
|
225
|
+
onKeyboardActionTriggered(KeyboardActionEvent("previous", currentText()))
|
|
226
|
+
},
|
|
227
|
+
onSearch = {
|
|
228
|
+
defaultKeyboardAction(ImeAction.Search)
|
|
229
|
+
onKeyboardActionTriggered(KeyboardActionEvent("search", currentText()))
|
|
230
|
+
},
|
|
231
|
+
onSend = {
|
|
232
|
+
defaultKeyboardAction(ImeAction.Send)
|
|
233
|
+
onKeyboardActionTriggered(KeyboardActionEvent("send", currentText()))
|
|
234
|
+
}
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
// Modifier
|
|
238
|
+
val modifier = ModifierRegistry.applyModifiers(modifiers, appContext, composableScope, globalEventDispatcher)
|
|
239
|
+
.focusRequester(focusRequester)
|
|
240
|
+
.onFocusChanged { focusState ->
|
|
241
|
+
onFocusChange(GenericEventPayload1(focusState.isFocused))
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (autoFocus) {
|
|
245
|
+
LaunchedEffect(Unit) { focusRequester.requestFocus() }
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
val text = state.value as? String ?: ""
|
|
249
|
+
val sel = selection.extractSelection(text.length)
|
|
250
|
+
|
|
251
|
+
val localValue = remember { mutableStateOf(TextFieldValue(text, sel)) }
|
|
252
|
+
if (localValue.value.text != text || localValue.value.selection != sel) {
|
|
253
|
+
localValue.value = TextFieldValue(text, sel)
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
val onValueChange: (TextFieldValue) -> Unit = { incoming ->
|
|
257
|
+
val new = maxLength?.let { max ->
|
|
258
|
+
if (incoming.text.length > max) {
|
|
259
|
+
val truncated = incoming.text.substring(0, max)
|
|
260
|
+
incoming.copy(
|
|
261
|
+
text = truncated,
|
|
262
|
+
selection = TextRange(
|
|
263
|
+
incoming.selection.start.coerceAtMost(max),
|
|
264
|
+
incoming.selection.end.coerceAtMost(max)
|
|
265
|
+
)
|
|
266
|
+
)
|
|
267
|
+
} else {
|
|
268
|
+
null
|
|
269
|
+
}
|
|
270
|
+
} ?: incoming
|
|
271
|
+
val prev = localValue.value
|
|
272
|
+
localValue.value = new
|
|
273
|
+
if (new.selection != prev.selection) {
|
|
274
|
+
val cur = selection.value as? Map<*, *>
|
|
275
|
+
val curStart = (cur?.get("start") as? Number)?.toInt()
|
|
276
|
+
val curEnd = (cur?.get("end") as? Number)?.toInt()
|
|
277
|
+
if (curStart != new.selection.start || curEnd != new.selection.end) {
|
|
278
|
+
selection.value = mapOf(
|
|
279
|
+
"start" to new.selection.start,
|
|
280
|
+
"end" to new.selection.end
|
|
281
|
+
)
|
|
282
|
+
}
|
|
283
|
+
onSelectionChanged(TextFieldSelectionPayload(new.selection.start, new.selection.end))
|
|
284
|
+
}
|
|
285
|
+
if (new.text != prev.text) {
|
|
286
|
+
state.value = new.text
|
|
287
|
+
val payload = TextFieldValuePayload(
|
|
288
|
+
text = new.text,
|
|
289
|
+
selection = TextFieldSelectionPayload(new.selection.start, new.selection.end)
|
|
290
|
+
)
|
|
291
|
+
onValueChanged(payload)
|
|
292
|
+
onValueChangeSync?.invoke(new.text)
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
return TextFieldCore(localValue.value, onValueChange, keyboardOptions, keyboardActions, modifier)
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// endregion Shared core
|
|
@@ -12,6 +12,14 @@ export type ObservableState<T> = SharedObject & {
|
|
|
12
12
|
* applied. Prefer writing from a worklet when you need synchronous updates
|
|
13
13
|
*/
|
|
14
14
|
value: T;
|
|
15
|
+
/**
|
|
16
|
+
* Reads the current value. A React Compiler compliant alternative to reading `.value`
|
|
17
|
+
*/
|
|
18
|
+
get(): T;
|
|
19
|
+
/**
|
|
20
|
+
* Writes a new value. A React Compiler-compliant alternative to assigning `.value`
|
|
21
|
+
*/
|
|
22
|
+
set(value: T): void;
|
|
15
23
|
/**
|
|
16
24
|
* A single listener invoked on the native UI runtime whenever the value changes
|
|
17
25
|
* (after iOS `didSet` and Android's setter). Assigning replaces the previous
|
|
@@ -30,9 +38,6 @@ export type ObservableState<T> = SharedObject & {
|
|
|
30
38
|
* 'worklet';
|
|
31
39
|
* console.log('changed to', value);
|
|
32
40
|
* };
|
|
33
|
-
* return () => {
|
|
34
|
-
* state.onChange = null;
|
|
35
|
-
* };
|
|
36
41
|
* }, []);
|
|
37
42
|
* ```
|
|
38
43
|
*/
|