react-native-controlled-input 0.14.0 → 0.16.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/ControlledInput.podspec +1 -1
- package/LICENSE +2 -1
- package/README.md +575 -18
- package/android/src/main/java/com/controlledinput/ControlledInputView.kt +47 -8
- package/ios/ControlledInputView.mm +0 -2
- package/ios/RNControlledInput.swift +14 -3
- package/lib/module/ControlledInputViewNativeComponent.ts +1 -1
- package/lib/module/index.js +0 -2
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +24 -25
- package/src/ControlledInputViewNativeComponent.ts +1 -1
- package/src/index.tsx +11 -6
- package/src/react-native-codegen.d.ts +2 -1
package/ControlledInput.podspec
CHANGED
|
@@ -11,7 +11,7 @@ Pod::Spec.new do |s|
|
|
|
11
11
|
s.authors = package["author"]
|
|
12
12
|
|
|
13
13
|
s.platforms = { :ios => min_ios_version_supported }
|
|
14
|
-
s.source = { :git => "https://github.com/
|
|
14
|
+
s.source = { :git => "https://github.com/RonasIT/react-native-controlled-input.git", :tag => "#{s.version}" }
|
|
15
15
|
|
|
16
16
|
s.source_files = "ios/**/*.{h,m,mm,swift,cpp}"
|
|
17
17
|
s.private_header_files = "ios/**/*.h"
|
package/LICENSE
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2026
|
|
3
|
+
Copyright (c) 2026 Ronas IT
|
|
4
|
+
|
|
4
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
6
|
of this software and associated documentation files (the "Software"), to deal
|
|
6
7
|
in the Software without restriction, including without limitation the rights
|
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# react-native-controlled-input
|
|
1
|
+
# @ronas-it/react-native-controlled-input
|
|
2
2
|
|
|
3
3
|
A controlled React Native input that lets you format and constrain the value exactly how you want in JS, while keeping the displayed text in sync without invalid characters flashing in the field.
|
|
4
4
|
|
|
@@ -8,12 +8,12 @@ With a regular controlled `TextInput`, native input is applied first, then JS re
|
|
|
8
8
|
|
|
9
9
|
That means invalid characters can still flash in the field for a moment.
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
`@ronas-it/react-native-controlled-input` is built for this exact case: you decide what text is valid, and the displayed value stays driven by `value`.
|
|
12
12
|
|
|
13
13
|
## Install
|
|
14
14
|
|
|
15
15
|
```sh
|
|
16
|
-
npm install react-native-controlled-input
|
|
16
|
+
npm install @ronas-it/react-native-controlled-input
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
Requires React Native New Architecture / Fabric.
|
|
@@ -26,7 +26,7 @@ import { StyleSheet } from 'react-native';
|
|
|
26
26
|
import {
|
|
27
27
|
ControlledInputView,
|
|
28
28
|
type ControlledInputViewRef,
|
|
29
|
-
} from 'react-native-controlled-input';
|
|
29
|
+
} from '@ronas-it/react-native-controlled-input';
|
|
30
30
|
|
|
31
31
|
export function Example() {
|
|
32
32
|
const [value, setValue] = useState('');
|
|
@@ -64,19 +64,21 @@ inputRef.current?.blur();
|
|
|
64
64
|
|
|
65
65
|
## Props
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
|
70
|
-
| `
|
|
71
|
-
| `
|
|
72
|
-
| `
|
|
73
|
-
| `
|
|
74
|
-
| `
|
|
75
|
-
| `
|
|
76
|
-
| `
|
|
77
|
-
| `
|
|
78
|
-
| `
|
|
79
|
-
| `
|
|
67
|
+
|
|
68
|
+
| Prop | Type | Description |
|
|
69
|
+
| ---------------------- | ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
70
|
+
| `value` | `string` | Current input value. |
|
|
71
|
+
| `onTextChange` | `(value: string) => void` | Called with the next text value. Filter it and update `value`. |
|
|
72
|
+
| `onFocus` | `() => void` | Called when the text input is focused. |
|
|
73
|
+
| `onBlur` | `() => void` | Called when the text input is blurred. |
|
|
74
|
+
| `autoComplete` | `string` | Specifies autocomplete hints for the system. Same as React Native `[TextInput](https://reactnative.dev/docs/textinput#autocomplete)`. |
|
|
75
|
+
| `autoCapitalize` | `string` | Can be `none`, `sentences`, `words`, `characters`. Same as React Native `[TextInput](https://reactnative.dev/docs/textinput#autocapitalize)`. |
|
|
76
|
+
| `keyboardType` | `string` | Determines which keyboard to open, e.g. `numeric`. Same as React Native `[TextInput](https://reactnative.dev/docs/textinput#keyboardtype)`. |
|
|
77
|
+
| `returnKeyType` | `string` | Determines how the return key should look. Same as React Native `[TextInput](https://reactnative.dev/docs/textinput#returnkeytype)`. |
|
|
78
|
+
| `placeholder` | `string` | The string that will be rendered before text input has been entered. |
|
|
79
|
+
| `placeholderTextColor` | `ColorValue` | The text color of the placeholder string. |
|
|
80
|
+
| `selectionColor` | `ColorValue` | The highlight and cursor color of the text input. |
|
|
81
|
+
|
|
80
82
|
|
|
81
83
|
## Style support
|
|
82
84
|
|
|
@@ -92,15 +94,570 @@ Commonly used supported styles:
|
|
|
92
94
|
|
|
93
95
|
Implementation differs internally between platforms, but usage is the same for library consumers.
|
|
94
96
|
|
|
97
|
+
## Fonts
|
|
98
|
+
|
|
99
|
+
In Expo projects, `**fontFamily` on this input only applies when the font is linked for native use**. Relying on runtime loading alone (`useFonts` / `loadAsync`) is often not enough here; use the **expo-font config plugin** so fonts are embedded at build time. See [Expo Font — Configuration in app config](https://docs.expo.dev/versions/latest/sdk/font/#configuration-in-app-config).
|
|
100
|
+
|
|
95
101
|
## Ref
|
|
96
102
|
|
|
97
103
|
- `focus()`
|
|
98
104
|
- `blur()`
|
|
99
105
|
|
|
106
|
+
## Troubleshooting
|
|
107
|
+
|
|
108
|
+
### `react-native-keyboard-controller`
|
|
109
|
+
|
|
110
|
+
If you use [react-native-keyboard-controller](https://github.com/kirillzyusko/react-native-keyboard-controller) with this package, apply the patch below that matches **your** installed library version so keyboard-aware scrolling and focused-input layout stay correct (especially on Android).
|
|
111
|
+
|
|
112
|
+
`1.20.7` (`react-native-keyboard-controller+1.20.7.patch`)
|
|
113
|
+
|
|
114
|
+
```diff
|
|
115
|
+
diff --git a/node_modules/react-native-keyboard-controller/android/src/main/java/com/reactnativekeyboardcontroller/extensions/EditText.kt b/node_modules/react-native-keyboard-controller/android/src/main/java/com/reactnativekeyboardcontroller/extensions/EditText.kt
|
|
116
|
+
index ddd9b88..b8a851b 100644
|
|
117
|
+
--- a/node_modules/react-native-keyboard-controller/android/src/main/java/com/reactnativekeyboardcontroller/extensions/EditText.kt
|
|
118
|
+
+++ b/node_modules/react-native-keyboard-controller/android/src/main/java/com/reactnativekeyboardcontroller/extensions/EditText.kt
|
|
119
|
+
@@ -8,7 +8,6 @@ import android.view.Gravity
|
|
120
|
+
import android.view.View
|
|
121
|
+
import android.view.ViewTreeObserver.OnPreDrawListener
|
|
122
|
+
import android.widget.EditText
|
|
123
|
+
-import com.facebook.react.views.scroll.ReactScrollView
|
|
124
|
+
import com.facebook.react.views.textinput.ReactEditText
|
|
125
|
+
import com.reactnativekeyboardcontroller.log.Logger
|
|
126
|
+
import java.lang.reflect.Field
|
|
127
|
+
@@ -99,24 +98,7 @@ fun EditText.addOnTextChangedListener(action: (String) -> Unit): TextWatcher {
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
val EditText.parentScrollViewTarget: Int
|
|
131
|
+
- get() {
|
|
132
|
+
- var currentView: View? = this
|
|
133
|
+
-
|
|
134
|
+
- while (currentView != null) {
|
|
135
|
+
- val parentView = currentView.parent as? View
|
|
136
|
+
-
|
|
137
|
+
- if (parentView is ReactScrollView && parentView.scrollEnabled) {
|
|
138
|
+
- // If the parent is a vertical, scrollable ScrollView - return its id
|
|
139
|
+
- return parentView.id
|
|
140
|
+
- }
|
|
141
|
+
-
|
|
142
|
+
- // Move to the next parent view
|
|
143
|
+
- currentView = parentView
|
|
144
|
+
- }
|
|
145
|
+
-
|
|
146
|
+
- // ScrollView was not found
|
|
147
|
+
- return -1
|
|
148
|
+
- }
|
|
149
|
+
+ get() = keyboardParentScrollViewTarget()
|
|
150
|
+
|
|
151
|
+
fun EditText?.focus() {
|
|
152
|
+
if (this is ReactEditText) {
|
|
153
|
+
diff --git a/node_modules/react-native-keyboard-controller/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ViewKeyboardScrollHost.kt b/node_modules/react-native-keyboard-controller/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ViewKeyboardScrollHost.kt
|
|
154
|
+
new file mode 100644
|
|
155
|
+
index 0000000..75c1ba5
|
|
156
|
+
--- /dev/null
|
|
157
|
+
+++ b/node_modules/react-native-keyboard-controller/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ViewKeyboardScrollHost.kt
|
|
158
|
+
@@ -0,0 +1,35 @@
|
|
159
|
+
+package com.reactnativekeyboardcontroller.extensions
|
|
160
|
+
+
|
|
161
|
+
+import android.view.View
|
|
162
|
+
+import com.facebook.react.views.scroll.ReactScrollView
|
|
163
|
+
+
|
|
164
|
+
+/**
|
|
165
|
+
+ * Nearest vertical [ReactScrollView] above this view (same strategy as [EditText.parentScrollViewTarget]).
|
|
166
|
+
+ */
|
|
167
|
+
+fun View.keyboardParentScrollViewTarget(): Int {
|
|
168
|
+
+ var current: View? = this
|
|
169
|
+
+ while (current != null) {
|
|
170
|
+
+ val parent = current.parent as? View ?: break
|
|
171
|
+
+ if (parent is ReactScrollView && parent.scrollEnabled) {
|
|
172
|
+
+ return parent.id
|
|
173
|
+
+ }
|
|
174
|
+
+ current = parent
|
|
175
|
+
+ }
|
|
176
|
+
+ return -1
|
|
177
|
+
+}
|
|
178
|
+
+
|
|
179
|
+
+/**
|
|
180
|
+
+ * react-native-controlled-input uses Compose [androidx.compose.foundation.text.BasicTextField] without a
|
|
181
|
+
+ * platform [android.widget.EditText], so [FocusedInputObserver] never sees `newFocus is EditText`.
|
|
182
|
+
+ * Resolve the RN view manager root to measure bounds and [keyboardParentScrollViewTarget].
|
|
183
|
+
+ */
|
|
184
|
+
+fun View?.findReactControlledInputHostOrNull(): View? {
|
|
185
|
+
+ var v: View? = this
|
|
186
|
+
+ while (v != null) {
|
|
187
|
+
+ if (v.javaClass.name == "com.controlledinput.ControlledInputView") {
|
|
188
|
+
+ return v
|
|
189
|
+
+ }
|
|
190
|
+
+ v = v.parent as? View
|
|
191
|
+
+ }
|
|
192
|
+
+ return null
|
|
193
|
+
+}
|
|
194
|
+
diff --git a/node_modules/react-native-keyboard-controller/android/src/main/java/com/reactnativekeyboardcontroller/listeners/FocusedInputObserver.kt b/node_modules/react-native-keyboard-controller/android/src/main/java/com/reactnativekeyboardcontroller/listeners/FocusedInputObserver.kt
|
|
195
|
+
index 1e7be51..373444b 100644
|
|
196
|
+
--- a/node_modules/react-native-keyboard-controller/android/src/main/java/com/reactnativekeyboardcontroller/listeners/FocusedInputObserver.kt
|
|
197
|
+
+++ b/node_modules/react-native-keyboard-controller/android/src/main/java/com/reactnativekeyboardcontroller/listeners/FocusedInputObserver.kt
|
|
198
|
+
@@ -19,6 +19,8 @@ import com.reactnativekeyboardcontroller.extensions.addOnTextChangedListener
|
|
199
|
+
import com.reactnativekeyboardcontroller.extensions.dispatchEvent
|
|
200
|
+
import com.reactnativekeyboardcontroller.extensions.dp
|
|
201
|
+
import com.reactnativekeyboardcontroller.extensions.emitEvent
|
|
202
|
+
+import com.reactnativekeyboardcontroller.extensions.findReactControlledInputHostOrNull
|
|
203
|
+
+import com.reactnativekeyboardcontroller.exteыnsions.keyboardParentScrollViewTarget
|
|
204
|
+
import com.reactnativekeyboardcontroller.extensions.parentScrollViewTarget
|
|
205
|
+
import com.reactnativekeyboardcontroller.extensions.rootView
|
|
206
|
+
import com.reactnativekeyboardcontroller.extensions.screenLocation
|
|
207
|
+
@@ -47,6 +49,7 @@ class FocusedInputObserver(
|
|
208
|
+
|
|
209
|
+
// state variables
|
|
210
|
+
private var lastFocusedInput: EditText? = null
|
|
211
|
+
+ private var lastComposeHost: View? = null
|
|
212
|
+
private var lastEventDispatched: FocusedInputLayoutChangedEventData = noFocusedInputEvent
|
|
213
|
+
private var textWatcher: TextWatcher? = null
|
|
214
|
+
private var selectionSubscription: (() -> Unit)? = null
|
|
215
|
+
@@ -101,6 +104,7 @@ class FocusedInputObserver(
|
|
216
|
+
// unfocused or focus was changed
|
|
217
|
+
if (newFocus == null || oldFocus != null) {
|
|
218
|
+
lastFocusedInput?.removeOnLayoutChangeListener(layoutListener)
|
|
219
|
+
+ lastComposeHost?.removeOnLayoutChangeListener(layoutListener)
|
|
220
|
+
lastFocusedInput?.let { input ->
|
|
221
|
+
val watcher = textWatcher
|
|
222
|
+
// remove it asynchronously to avoid crash in stripe input
|
|
223
|
+
@@ -111,6 +115,7 @@ class FocusedInputObserver(
|
|
224
|
+
}
|
|
225
|
+
selectionSubscription?.invoke()
|
|
226
|
+
lastFocusedInput = null
|
|
227
|
+
+ lastComposeHost = null
|
|
228
|
+
}
|
|
229
|
+
if (newFocus is EditText) {
|
|
230
|
+
lastFocusedInput = newFocus
|
|
231
|
+
@@ -130,6 +135,22 @@ class FocusedInputObserver(
|
|
232
|
+
putInt("count", allInputFields.size)
|
|
233
|
+
},
|
|
234
|
+
)
|
|
235
|
+
+ } else {
|
|
236
|
+
+ val host = newFocus.findReactControlledInputHostOrNull()
|
|
237
|
+
+ if (host != null) {
|
|
238
|
+
+ lastComposeHost = host
|
|
239
|
+
+ host.addOnLayoutChangeListener(layoutListener)
|
|
240
|
+
+ this.syncUpLayout()
|
|
241
|
+
+
|
|
242
|
+
+ val allInputFields = ViewHierarchyNavigator.getAllInputFields(context?.rootView)
|
|
243
|
+
+ context.emitEvent(
|
|
244
|
+
+ "KeyboardController::focusDidSet",
|
|
245
|
+
+ Arguments.createMap().apply {
|
|
246
|
+
+ putInt("current", -1)
|
|
247
|
+
+ putInt("count", allInputFields.size)
|
|
248
|
+
+ },
|
|
249
|
+
+ )
|
|
250
|
+
+ }
|
|
251
|
+
}
|
|
252
|
+
// unfocused
|
|
253
|
+
if (newFocus == null) {
|
|
254
|
+
@@ -142,19 +163,37 @@ class FocusedInputObserver(
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
fun syncUpLayout() {
|
|
258
|
+
- val input = lastFocusedInput ?: return
|
|
259
|
+
+ val input = lastFocusedInput
|
|
260
|
+
+ if (input != null) {
|
|
261
|
+
+ val (x, y) = input.screenLocation
|
|
262
|
+
+ val event =
|
|
263
|
+
+ FocusedInputLayoutChangedEventData(
|
|
264
|
+
+ x = input.x.dp,
|
|
265
|
+
+ y = input.y.dp,
|
|
266
|
+
+ width = input.width.toFloat().dp,
|
|
267
|
+
+ height = input.height.toFloat().dp,
|
|
268
|
+
+ absoluteX = x.toFloat().dp,
|
|
269
|
+
+ absoluteY = y.toFloat().dp,
|
|
270
|
+
+ target = input.id,
|
|
271
|
+
+ parentScrollViewTarget = input.parentScrollViewTarget,
|
|
272
|
+
+ )
|
|
273
|
+
+
|
|
274
|
+
+ dispatchEventToJS(event)
|
|
275
|
+
+ return
|
|
276
|
+
+ }
|
|
277
|
+
|
|
278
|
+
- val (x, y) = input.screenLocation
|
|
279
|
+
+ val host = lastComposeHost ?: return
|
|
280
|
+
+ val (x, y) = host.screenLocation
|
|
281
|
+
val event =
|
|
282
|
+
FocusedInputLayoutChangedEventData(
|
|
283
|
+
- x = input.x.dp,
|
|
284
|
+
- y = input.y.dp,
|
|
285
|
+
- width = input.width.toFloat().dp,
|
|
286
|
+
- height = input.height.toFloat().dp,
|
|
287
|
+
+ x = host.x.dp,
|
|
288
|
+
+ y = host.y.dp,
|
|
289
|
+
+ width = host.width.toFloat().dp,
|
|
290
|
+
+ height = host.height.toFloat().dp,
|
|
291
|
+
absoluteX = x.toFloat().dp,
|
|
292
|
+
absoluteY = y.toFloat().dp,
|
|
293
|
+
- target = input.id,
|
|
294
|
+
- parentScrollViewTarget = input.parentScrollViewTarget,
|
|
295
|
+
+ target = host.id,
|
|
296
|
+
+ parentScrollViewTarget = host.keyboardParentScrollViewTarget(),
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
dispatchEventToJS(event)
|
|
300
|
+
diff --git a/node_modules/react-native-keyboard-controller/src/components/KeyboardAwareScrollView/index.tsx b/node_modules/react-native-keyboard-controller/src/components/KeyboardAwareScrollView/index.tsx
|
|
301
|
+
index 4b21666..e35f03c 100644
|
|
302
|
+
--- a/node_modules/react-native-keyboard-controller/src/components/KeyboardAwareScrollView/index.tsx
|
|
303
|
+
+++ b/node_modules/react-native-keyboard-controller/src/components/KeyboardAwareScrollView/index.tsx
|
|
304
|
+
@@ -246,7 +246,15 @@ const KeyboardAwareScrollView = forwardRef<
|
|
305
|
+
|
|
306
|
+
const customHeight = lastSelection.value?.selection.end.y;
|
|
307
|
+
|
|
308
|
+
- if (!input.value?.layout || !customHeight) {
|
|
309
|
+
+ // Without selection geometry (e.g. Compose BasicTextField / controlled-input on Android)
|
|
310
|
+
+ // we still must mirror `input` into local `layout` so `maybeScroll` sees parentScrollViewTarget.
|
|
311
|
+
+ if (!input.value?.layout) {
|
|
312
|
+
+ layout.value = input.value;
|
|
313
|
+
+ return false;
|
|
314
|
+
+ }
|
|
315
|
+
+
|
|
316
|
+
+ if (!customHeight) {
|
|
317
|
+
+ layout.value = input.value;
|
|
318
|
+
return false;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
@@ -351,25 +359,25 @@ const KeyboardAwareScrollView = forwardRef<
|
|
322
|
+
keyboardWillChangeSize ||
|
|
323
|
+
focusWasChanged
|
|
324
|
+
) {
|
|
325
|
+
- // persist scroll value
|
|
326
|
+
- scrollPosition.value = position.value;
|
|
327
|
+
- // just persist height - later will be used in interpolation
|
|
328
|
+
- keyboardHeight.value = e.height;
|
|
329
|
+
+ // Do not clobber scroll / keyboard height while hiding — `keyboardWillHide`
|
|
330
|
+
+ // already anchored `scrollPosition` to `scrollBeforeKeyboardMovement`. A bogus
|
|
331
|
+
+ // `focusWasChanged` would otherwise zero `keyboardHeight` / wrong scroll anchor,
|
|
332
|
+
+ // or run `maybeScroll(0)` and produce an up-then-down jump.
|
|
333
|
+
+ if (!keyboardWillHide) {
|
|
334
|
+
+ scrollPosition.value = position.value;
|
|
335
|
+
+ keyboardHeight.value = e.height;
|
|
336
|
+
+ }
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
- // focus was changed
|
|
340
|
+
if (focusWasChanged) {
|
|
341
|
+
tag.value = e.target;
|
|
342
|
+
- // save position of focused text input when keyboard starts to move
|
|
343
|
+
- updateLayoutFromSelection();
|
|
344
|
+
- // save current scroll position - when keyboard will hide we'll reuse
|
|
345
|
+
- // this value to achieve smooth hide effect
|
|
346
|
+
- scrollBeforeKeyboardMovement.value = position.value;
|
|
347
|
+
+ if (!keyboardWillHide) {
|
|
348
|
+
+ updateLayoutFromSelection();
|
|
349
|
+
+ scrollBeforeKeyboardMovement.value = position.value;
|
|
350
|
+
+ }
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
- if (focusWasChanged && !keyboardWillAppear.value) {
|
|
354
|
+
- // update position on scroll value, so `onEnd` handler
|
|
355
|
+
- // will pick up correct values
|
|
356
|
+
+ if (focusWasChanged && !keyboardWillAppear.value && !keyboardWillHide) {
|
|
357
|
+
position.value += maybeScroll(e.height, true);
|
|
358
|
+
}
|
|
359
|
+
},
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
`1.21.4` (`react-native-keyboard-controller+1.21.4.patch`)
|
|
365
|
+
|
|
366
|
+
```diff
|
|
367
|
+
diff --git a/node_modules/react-native-keyboard-controller/android/src/main/java/com/reactnativekeyboardcontroller/extensions/EditText.kt b/node_modules/react-native-keyboard-controller/android/src/main/java/com/reactnativekeyboardcontroller/extensions/EditText.kt
|
|
368
|
+
index ddd9b880..b8a851b5 100644
|
|
369
|
+
--- a/node_modules/react-native-keyboard-controller/android/src/main/java/com/reactnativekeyboardcontroller/extensions/EditText.kt
|
|
370
|
+
+++ b/node_modules/react-native-keyboard-controller/android/src/main/java/com/reactnativekeyboardcontroller/extensions/EditText.kt
|
|
371
|
+
@@ -8,7 +8,6 @@ import android.view.Gravity
|
|
372
|
+
import android.view.View
|
|
373
|
+
import android.view.ViewTreeObserver.OnPreDrawListener
|
|
374
|
+
import android.widget.EditText
|
|
375
|
+
-import com.facebook.react.views.scroll.ReactScrollView
|
|
376
|
+
import com.facebook.react.views.textinput.ReactEditText
|
|
377
|
+
import com.reactnativekeyboardcontroller.log.Logger
|
|
378
|
+
import java.lang.reflect.Field
|
|
379
|
+
@@ -99,24 +98,7 @@ fun EditText.addOnTextChangedListener(action: (String) -> Unit): TextWatcher {
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
val EditText.parentScrollViewTarget: Int
|
|
383
|
+
- get() {
|
|
384
|
+
- var currentView: View? = this
|
|
385
|
+
-
|
|
386
|
+
- while (currentView != null) {
|
|
387
|
+
- val parentView = currentView.parent as? View
|
|
388
|
+
-
|
|
389
|
+
- if (parentView is ReactScrollView && parentView.scrollEnabled) {
|
|
390
|
+
- // If the parent is a vertical, scrollable ScrollView - return its id
|
|
391
|
+
- return parentView.id
|
|
392
|
+
- }
|
|
393
|
+
-
|
|
394
|
+
- // Move to the next parent view
|
|
395
|
+
- currentView = parentView
|
|
396
|
+
- }
|
|
397
|
+
-
|
|
398
|
+
- // ScrollView was not found
|
|
399
|
+
- return -1
|
|
400
|
+
- }
|
|
401
|
+
+ get() = keyboardParentScrollViewTarget()
|
|
402
|
+
|
|
403
|
+
fun EditText?.focus() {
|
|
404
|
+
if (this is ReactEditText) {
|
|
405
|
+
diff --git a/node_modules/react-native-keyboard-controller/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ViewKeyboardScrollHost.kt b/node_modules/react-native-keyboard-controller/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ViewKeyboardScrollHost.kt
|
|
406
|
+
new file mode 100644
|
|
407
|
+
index 00000000..75c1ba52
|
|
408
|
+
--- /dev/null
|
|
409
|
+
+++ b/node_modules/react-native-keyboard-controller/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ViewKeyboardScrollHost.kt
|
|
410
|
+
@@ -0,0 +1,35 @@
|
|
411
|
+
+package com.reactnativekeyboardcontroller.extensions
|
|
412
|
+
+
|
|
413
|
+
+import android.view.View
|
|
414
|
+
+import com.facebook.react.views.scroll.ReactScrollView
|
|
415
|
+
+
|
|
416
|
+
+/**
|
|
417
|
+
+ * Nearest vertical [ReactScrollView] above this view (same strategy as [EditText.parentScrollViewTarget]).
|
|
418
|
+
+ */
|
|
419
|
+
+fun View.keyboardParentScrollViewTarget(): Int {
|
|
420
|
+
+ var current: View? = this
|
|
421
|
+
+ while (current != null) {
|
|
422
|
+
+ val parent = current.parent as? View ?: break
|
|
423
|
+
+ if (parent is ReactScrollView && parent.scrollEnabled) {
|
|
424
|
+
+ return parent.id
|
|
425
|
+
+ }
|
|
426
|
+
+ current = parent
|
|
427
|
+
+ }
|
|
428
|
+
+ return -1
|
|
429
|
+
+}
|
|
430
|
+
+
|
|
431
|
+
+/**
|
|
432
|
+
+ * react-native-controlled-input uses Compose [androidx.compose.foundation.text.BasicTextField] without a
|
|
433
|
+
+ * platform [android.widget.EditText], so [FocusedInputObserver] never sees `newFocus is EditText`.
|
|
434
|
+
+ * Resolve the RN view manager root to measure bounds and [keyboardParentScrollViewTarget].
|
|
435
|
+
+ */
|
|
436
|
+
+fun View?.findReactControlledInputHostOrNull(): View? {
|
|
437
|
+
+ var v: View? = this
|
|
438
|
+
+ while (v != null) {
|
|
439
|
+
+ if (v.javaClass.name == "com.controlledinput.ControlledInputView") {
|
|
440
|
+
+ return v
|
|
441
|
+
+ }
|
|
442
|
+
+ v = v.parent as? View
|
|
443
|
+
+ }
|
|
444
|
+
+ return null
|
|
445
|
+
+}
|
|
446
|
+
diff --git a/node_modules/react-native-keyboard-controller/android/src/main/java/com/reactnativekeyboardcontroller/listeners/FocusedInputObserver.kt b/node_modules/react-native-keyboard-controller/android/src/main/java/com/reactnativekeyboardcontroller/listeners/FocusedInputObserver.kt
|
|
447
|
+
index 4de762da..dfb90a87 100644
|
|
448
|
+
--- a/node_modules/react-native-keyboard-controller/android/src/main/java/com/reactnativekeyboardcontroller/listeners/FocusedInputObserver.kt
|
|
449
|
+
+++ b/node_modules/react-native-keyboard-controller/android/src/main/java/com/reactnativekeyboardcontroller/listeners/FocusedInputObserver.kt
|
|
450
|
+
@@ -19,6 +19,8 @@ import com.reactnativekeyboardcontroller.extensions.addOnTextChangedListener
|
|
451
|
+
import com.reactnativekeyboardcontroller.extensions.dispatchEvent
|
|
452
|
+
import com.reactnativekeyboardcontroller.extensions.dp
|
|
453
|
+
import com.reactnativekeyboardcontroller.extensions.emitEvent
|
|
454
|
+
+import com.reactnativekeyboardcontroller.extensions.findReactControlledInputHostOrNull
|
|
455
|
+
+import com.reactnativekeyboardcontroller.extensions.keyboardParentScrollViewTarget
|
|
456
|
+
import com.reactnativekeyboardcontroller.extensions.parentScrollViewTarget
|
|
457
|
+
import com.reactnativekeyboardcontroller.extensions.rootView
|
|
458
|
+
import com.reactnativekeyboardcontroller.extensions.screenLocation
|
|
459
|
+
@@ -47,6 +49,7 @@ class FocusedInputObserver(
|
|
460
|
+
|
|
461
|
+
// state variables
|
|
462
|
+
private var lastFocusedInput: EditText? = null
|
|
463
|
+
+ private var lastComposeHost: View? = null
|
|
464
|
+
private var lastEventDispatched: FocusedInputLayoutChangedEventData = noFocusedInputEvent
|
|
465
|
+
private var textWatcher: TextWatcher? = null
|
|
466
|
+
private var selectionSubscription: (() -> Unit)? = null
|
|
467
|
+
@@ -101,6 +104,7 @@ class FocusedInputObserver(
|
|
468
|
+
// unfocused or focus was changed
|
|
469
|
+
if (newFocus == null || oldFocus != null) {
|
|
470
|
+
lastFocusedInput?.removeOnLayoutChangeListener(layoutListener)
|
|
471
|
+
+ lastComposeHost?.removeOnLayoutChangeListener(layoutListener)
|
|
472
|
+
lastFocusedInput?.let { input ->
|
|
473
|
+
val watcher = textWatcher
|
|
474
|
+
// remove it asynchronously to avoid crash in stripe input
|
|
475
|
+
@@ -111,6 +115,7 @@ class FocusedInputObserver(
|
|
476
|
+
}
|
|
477
|
+
selectionSubscription?.invoke()
|
|
478
|
+
lastFocusedInput = null
|
|
479
|
+
+ lastComposeHost = null
|
|
480
|
+
}
|
|
481
|
+
if (newFocus is EditText) {
|
|
482
|
+
lastFocusedInput = newFocus
|
|
483
|
+
@@ -131,6 +136,22 @@ class FocusedInputObserver(
|
|
484
|
+
putInt("count", allInputFields.size)
|
|
485
|
+
},
|
|
486
|
+
)
|
|
487
|
+
+ } else {
|
|
488
|
+
+ val host = newFocus.findReactControlledInputHostOrNull()
|
|
489
|
+
+ if (host != null) {
|
|
490
|
+
+ lastComposeHost = host
|
|
491
|
+
+ host.addOnLayoutChangeListener(layoutListener)
|
|
492
|
+
+ this.syncUpLayout()
|
|
493
|
+
+
|
|
494
|
+
+ val allInputFields = ViewHierarchyNavigator.getAllInputFields(context?.rootView)
|
|
495
|
+
+ context.emitEvent(
|
|
496
|
+
+ "KeyboardController::focusDidSet",
|
|
497
|
+
+ Arguments.createMap().apply {
|
|
498
|
+
+ putInt("current", -1)
|
|
499
|
+
+ putInt("count", allInputFields.size)
|
|
500
|
+
+ },
|
|
501
|
+
+ )
|
|
502
|
+
+ }
|
|
503
|
+
}
|
|
504
|
+
// unfocused
|
|
505
|
+
if (newFocus == null) {
|
|
506
|
+
@@ -143,19 +164,37 @@ class FocusedInputObserver(
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
fun syncUpLayout() {
|
|
510
|
+
- val input = lastFocusedInput ?: return
|
|
511
|
+
+ val input = lastFocusedInput
|
|
512
|
+
+ if (input != null) {
|
|
513
|
+
+ val (x, y) = input.screenLocation
|
|
514
|
+
+ val event =
|
|
515
|
+
+ FocusedInputLayoutChangedEventData(
|
|
516
|
+
+ x = input.x.dp,
|
|
517
|
+
+ y = input.y.dp,
|
|
518
|
+
+ width = input.width.toFloat().dp,
|
|
519
|
+
+ height = input.height.toFloat().dp,
|
|
520
|
+
+ absoluteX = x.toFloat().dp,
|
|
521
|
+
+ absoluteY = y.toFloat().dp,
|
|
522
|
+
+ target = input.id,
|
|
523
|
+
+ parentScrollViewTarget = input.parentScrollViewTarget,
|
|
524
|
+
+ )
|
|
525
|
+
+
|
|
526
|
+
+ dispatchEventToJS(event)
|
|
527
|
+
+ return
|
|
528
|
+
+ }
|
|
529
|
+
|
|
530
|
+
- val (x, y) = input.screenLocation
|
|
531
|
+
+ val host = lastComposeHost ?: return
|
|
532
|
+
+ val (x, y) = host.screenLocation
|
|
533
|
+
val event =
|
|
534
|
+
FocusedInputLayoutChangedEventData(
|
|
535
|
+
- x = input.x.dp,
|
|
536
|
+
- y = input.y.dp,
|
|
537
|
+
- width = input.width.toFloat().dp,
|
|
538
|
+
- height = input.height.toFloat().dp,
|
|
539
|
+
+ x = host.x.dp,
|
|
540
|
+
+ y = host.y.dp,
|
|
541
|
+
+ width = host.width.toFloat().dp,
|
|
542
|
+
+ height = host.height.toFloat().dp,
|
|
543
|
+
absoluteX = x.toFloat().dp,
|
|
544
|
+
absoluteY = y.toFloat().dp,
|
|
545
|
+
- target = input.id,
|
|
546
|
+
- parentScrollViewTarget = input.parentScrollViewTarget,
|
|
547
|
+
+ target = host.id,
|
|
548
|
+
+ parentScrollViewTarget = host.keyboardParentScrollViewTarget(),
|
|
549
|
+
)
|
|
550
|
+
|
|
551
|
+
dispatchEventToJS(event)
|
|
552
|
+
diff --git a/node_modules/react-native-keyboard-controller/src/components/KeyboardAwareScrollView/index.tsx b/node_modules/react-native-keyboard-controller/src/components/KeyboardAwareScrollView/index.tsx
|
|
553
|
+
index 90586e84..e506d284 100644
|
|
554
|
+
--- a/node_modules/react-native-keyboard-controller/src/components/KeyboardAwareScrollView/index.tsx
|
|
555
|
+
+++ b/node_modules/react-native-keyboard-controller/src/components/KeyboardAwareScrollView/index.tsx
|
|
556
|
+
@@ -287,7 +287,15 @@ const KeyboardAwareScrollView = forwardRef<
|
|
557
|
+
|
|
558
|
+
const customHeight = lastSelection.value?.selection.end.y;
|
|
559
|
+
|
|
560
|
+
- if (!input.value?.layout || !customHeight) {
|
|
561
|
+
+ // Without selection geometry (e.g. Compose BasicTextField / controlled-input on Android)
|
|
562
|
+
+ // we still must mirror `input` into local `layout` so `maybeScroll` sees parentScrollViewTarget.
|
|
563
|
+
+ if (!input.value?.layout) {
|
|
564
|
+
+ layout.value = input.value;
|
|
565
|
+
+ return false;
|
|
566
|
+
+ }
|
|
567
|
+
+
|
|
568
|
+
+ if (!customHeight) {
|
|
569
|
+
+ layout.value = input.value;
|
|
570
|
+
return false;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
@@ -410,10 +418,14 @@ const KeyboardAwareScrollView = forwardRef<
|
|
574
|
+
keyboardWillChangeSize ||
|
|
575
|
+
focusWasChanged
|
|
576
|
+
) {
|
|
577
|
+
- // persist scroll value
|
|
578
|
+
- scrollPosition.value = position.value;
|
|
579
|
+
- // just persist height - later will be used in interpolation
|
|
580
|
+
- keyboardHeight.value = e.height;
|
|
581
|
+
+ // Do not clobber scroll / keyboard height while hiding — `keyboardWillHide`
|
|
582
|
+
+ // already anchored `scrollPosition` to `scrollBeforeKeyboardMovement`. A bogus
|
|
583
|
+
+ // `focusWasChanged` would otherwise zero `keyboardHeight` / wrong scroll anchor,
|
|
584
|
+
+ // or run `maybeScroll(0)` and produce an up-then-down jump.
|
|
585
|
+
+ if (!keyboardWillHide) {
|
|
586
|
+
+ scrollPosition.value = position.value;
|
|
587
|
+
+ keyboardHeight.value = e.height;
|
|
588
|
+
+ }
|
|
589
|
+
|
|
590
|
+
// insets mode: set the full contentInset upfront so that maybeScroll
|
|
591
|
+
// calculations are correct from the very first onMove frame.
|
|
592
|
+
@@ -428,33 +440,35 @@ const KeyboardAwareScrollView = forwardRef<
|
|
593
|
+
if (focusWasChanged) {
|
|
594
|
+
tag.value = e.target;
|
|
595
|
+
|
|
596
|
+
- if (
|
|
597
|
+
- lastSelection.value?.target === e.target &&
|
|
598
|
+
- selectionUpdatedSinceHide.value
|
|
599
|
+
- ) {
|
|
600
|
+
- // fresh selection arrived before onStart - use it to update layout
|
|
601
|
+
- updateLayoutFromSelection();
|
|
602
|
+
- pendingSelectionForFocus.value = false;
|
|
603
|
+
- } else {
|
|
604
|
+
- // selection hasn't arrived yet for the new target (iOS 15),
|
|
605
|
+
- // or it's stale from previous session (Android refocus same input).
|
|
606
|
+
- // Use stale selection as best-effort fallback if available for same target,
|
|
607
|
+
- // otherwise fall back to full input layout.
|
|
608
|
+
- // Will be corrected if a fresh onSelectionChange arrives.
|
|
609
|
+
- if (lastSelection.value?.target === e.target) {
|
|
610
|
+
+ if (!keyboardWillHide) {
|
|
611
|
+
+ if (
|
|
612
|
+
+ lastSelection.value?.target === e.target &&
|
|
613
|
+
+ selectionUpdatedSinceHide.value
|
|
614
|
+
+ ) {
|
|
615
|
+
+ // fresh selection arrived before onStart - use it to update layout
|
|
616
|
+
updateLayoutFromSelection();
|
|
617
|
+
- } else if (input.value) {
|
|
618
|
+
- layout.value = input.value;
|
|
619
|
+
+ pendingSelectionForFocus.value = false;
|
|
620
|
+
+ } else {
|
|
621
|
+
+ // selection hasn't arrived yet for the new target (iOS 15),
|
|
622
|
+
+ // or it's stale from previous session (Android refocus same input).
|
|
623
|
+
+ // Use stale selection as best-effort fallback if available for same target,
|
|
624
|
+
+ // otherwise fall back to full input layout.
|
|
625
|
+
+ // Will be corrected if a fresh onSelectionChange arrives.
|
|
626
|
+
+ if (lastSelection.value?.target === e.target) {
|
|
627
|
+
+ updateLayoutFromSelection();
|
|
628
|
+
+ } else if (input.value) {
|
|
629
|
+
+ layout.value = input.value;
|
|
630
|
+
+ }
|
|
631
|
+
+ pendingSelectionForFocus.value = true;
|
|
632
|
+
}
|
|
633
|
+
- pendingSelectionForFocus.value = true;
|
|
634
|
+
- }
|
|
635
|
+
|
|
636
|
+
- // save current scroll position - when keyboard will hide we'll reuse
|
|
637
|
+
- // this value to achieve smooth hide effect
|
|
638
|
+
- scrollBeforeKeyboardMovement.value = position.value;
|
|
639
|
+
+ // save current scroll position - when keyboard will hide we'll reuse
|
|
640
|
+
+ // this value to achieve smooth hide effect
|
|
641
|
+
+ scrollBeforeKeyboardMovement.value = position.value;
|
|
642
|
+
+ }
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
- if (focusWasChanged && !keyboardWillAppear.value) {
|
|
646
|
+
+ if (focusWasChanged && !keyboardWillAppear.value && !keyboardWillHide) {
|
|
647
|
+
if (!pendingSelectionForFocus.value) {
|
|
648
|
+
// update position on scroll value, so `onEnd` handler
|
|
649
|
+
// will pick up correct values
|
|
650
|
+
position.value += maybeScroll(e.height, true);
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
```
|
|
654
|
+
|
|
655
|
+
|
|
656
|
+
|
|
100
657
|
## License
|
|
101
658
|
|
|
102
659
|
MIT
|
|
103
660
|
|
|
104
661
|
---
|
|
105
662
|
|
|
106
|
-
Made with [create-react-native-library](https://github.com/callstack/react-native-builder-bob)
|
|
663
|
+
Made with [create-react-native-library](https://github.com/callstack/react-native-builder-bob)
|
|
@@ -4,6 +4,7 @@ import android.content.Context
|
|
|
4
4
|
import android.util.AttributeSet
|
|
5
5
|
import android.view.View
|
|
6
6
|
import android.view.inputmethod.InputMethodManager
|
|
7
|
+
import android.widget.EditText
|
|
7
8
|
import android.widget.LinearLayout
|
|
8
9
|
import androidx.annotation.UiThread
|
|
9
10
|
import androidx.compose.runtime.LaunchedEffect
|
|
@@ -26,8 +27,7 @@ import com.facebook.react.uimanager.UIManagerHelper
|
|
|
26
27
|
import kotlinx.coroutines.flow.MutableStateFlow
|
|
27
28
|
|
|
28
29
|
/**
|
|
29
|
-
*
|
|
30
|
-
* - [shouldUseAndroidLayout]: requestLayout posts measureAndLayout (RN #17968)
|
|
30
|
+
* - [shouldUseAndroidLayout]: requestLayout posts measureAndLayout
|
|
31
31
|
* - onMeasure skips child [ComposeView] until attached (window + WindowRecomposer)
|
|
32
32
|
*
|
|
33
33
|
* @see expo.modules.kotlin.views.ExpoComposeView
|
|
@@ -60,11 +60,48 @@ class ControlledInputView : LinearLayout, LifecycleOwner {
|
|
|
60
60
|
private var usesLocalFallbackLifecycle = false
|
|
61
61
|
private var windowLifecycleBound = false
|
|
62
62
|
|
|
63
|
-
/**
|
|
63
|
+
/**
|
|
64
|
+
* Invisible EditText that acts as a focus proxy for react-native-keyboard-controller.
|
|
65
|
+
*
|
|
66
|
+
* keyboard-controller's FocusedInputObserver only tracks views that are `EditText` instances.
|
|
67
|
+
* Since ControlledInputView uses Compose BasicTextField, it is invisible to that observer.
|
|
68
|
+
* Focusing this proxy when Compose gains focus makes keyboard-controller aware of the input
|
|
69
|
+
* and allows KeyboardAwareScrollView to scroll correctly.
|
|
70
|
+
*
|
|
71
|
+
* Layout height is 0 so LinearLayout ignores it visually. onLayout() forces its bounds to
|
|
72
|
+
* match ControlledInputView so keyboard-controller reads the correct width/height/position.
|
|
73
|
+
*/
|
|
74
|
+
private val focusProxy: EditText by lazy {
|
|
75
|
+
EditText(context).also { proxy ->
|
|
76
|
+
proxy.layoutParams = LayoutParams(0, 0)
|
|
77
|
+
proxy.visibility = View.INVISIBLE
|
|
78
|
+
proxy.isFocusableInTouchMode = true
|
|
79
|
+
proxy.showSoftInputOnFocus = false
|
|
80
|
+
proxy.isClickable = false
|
|
81
|
+
proxy.isCursorVisible = false
|
|
82
|
+
proxy.isLongClickable = false
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
private fun requestFocusProxy() {
|
|
87
|
+
focusProxy.requestFocus()
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
private fun clearFocusProxy() {
|
|
91
|
+
focusProxy.clearFocus()
|
|
92
|
+
}
|
|
93
|
+
|
|
64
94
|
private val shouldUseAndroidLayout = true
|
|
65
95
|
|
|
96
|
+
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
|
|
97
|
+
super.onLayout(changed, l, t, r, b)
|
|
98
|
+
// Force proxy bounds to match ControlledInputView so keyboard-controller reads
|
|
99
|
+
// the correct width/height/absolutePosition when the proxy is focused.
|
|
100
|
+
focusProxy.layout(0, 0, width, height)
|
|
101
|
+
}
|
|
102
|
+
|
|
66
103
|
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
|
67
|
-
//
|
|
104
|
+
// Do not measure ComposeView until attached to a window.
|
|
68
105
|
if (shouldUseAndroidLayout && !isAttachedToWindow) {
|
|
69
106
|
setMeasuredDimension(
|
|
70
107
|
MeasureSpec.getSize(widthMeasureSpec).coerceAtLeast(0),
|
|
@@ -75,10 +112,7 @@ class ControlledInputView : LinearLayout, LifecycleOwner {
|
|
|
75
112
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
|
76
113
|
}
|
|
77
114
|
|
|
78
|
-
|
|
79
|
-
* ExpoView.requestLayout / measureAndLayout — Fabric/Yoga often won't drive Android layout
|
|
80
|
-
* for native children; this mirrors expo-modules-core behavior.
|
|
81
|
-
*/
|
|
115
|
+
// Fabric/Yoga often won't drive Android layout for native children.
|
|
82
116
|
override fun requestLayout() {
|
|
83
117
|
super.requestLayout()
|
|
84
118
|
if (shouldUseAndroidLayout) {
|
|
@@ -149,6 +183,7 @@ class ControlledInputView : LinearLayout, LifecycleOwner {
|
|
|
149
183
|
val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
|
150
184
|
imm.hideSoftInputFromWindow(windowToken, 0)
|
|
151
185
|
clearFocus()
|
|
186
|
+
clearFocusProxy()
|
|
152
187
|
}
|
|
153
188
|
|
|
154
189
|
fun focus() {
|
|
@@ -167,6 +202,8 @@ class ControlledInputView : LinearLayout, LifecycleOwner {
|
|
|
167
202
|
|
|
168
203
|
viewModel = JetpackComposeViewModel()
|
|
169
204
|
|
|
205
|
+
addView(focusProxy)
|
|
206
|
+
|
|
170
207
|
composeView = ComposeView(context).also { cv ->
|
|
171
208
|
cv.layoutParams = LayoutParams(
|
|
172
209
|
LayoutParams.MATCH_PARENT,
|
|
@@ -227,6 +264,7 @@ class ControlledInputView : LinearLayout, LifecycleOwner {
|
|
|
227
264
|
viewId
|
|
228
265
|
)
|
|
229
266
|
)
|
|
267
|
+
requestFocusProxy()
|
|
230
268
|
},
|
|
231
269
|
onBlur = {
|
|
232
270
|
val surfaceId = UIManagerHelper.getSurfaceId(context)
|
|
@@ -239,6 +277,7 @@ class ControlledInputView : LinearLayout, LifecycleOwner {
|
|
|
239
277
|
viewId
|
|
240
278
|
)
|
|
241
279
|
)
|
|
280
|
+
clearFocusProxy()
|
|
242
281
|
},
|
|
243
282
|
focusRequester = focusRequester
|
|
244
283
|
)
|
|
@@ -101,13 +101,11 @@ using namespace facebook::react;
|
|
|
101
101
|
- (void)handleCommand:(const NSString *)commandName args:(const NSArray *)args
|
|
102
102
|
{
|
|
103
103
|
if ([commandName isEqualToString:@"focus"]) {
|
|
104
|
-
NSLog(@"[ControlledInputView] handleCommand focus");
|
|
105
104
|
[_inputView focus];
|
|
106
105
|
return;
|
|
107
106
|
}
|
|
108
107
|
|
|
109
108
|
if ([commandName isEqualToString:@"blur"]) {
|
|
110
|
-
NSLog(@"[ControlledInputView] handleCommand blur");
|
|
111
109
|
[_inputView blur];
|
|
112
110
|
return;
|
|
113
111
|
}
|
|
@@ -144,11 +144,22 @@ public class RNControlledInput: UIView, UITextFieldDelegate {
|
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
private func applyFont() {
|
|
147
|
-
|
|
148
|
-
textField.font = font
|
|
149
|
-
} else {
|
|
147
|
+
guard let family = fontFamily else {
|
|
150
148
|
textField.font = UIFont.systemFont(ofSize: fontSize)
|
|
149
|
+
return
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if let font = UIFont(name: family, size: fontSize) {
|
|
153
|
+
textField.font = font
|
|
154
|
+
return
|
|
155
|
+
}
|
|
156
|
+
for face in UIFont.fontNames(forFamilyName: family) {
|
|
157
|
+
if let font = UIFont(name: face, size: fontSize) {
|
|
158
|
+
textField.font = font
|
|
159
|
+
return
|
|
160
|
+
}
|
|
151
161
|
}
|
|
162
|
+
textField.font = UIFont.systemFont(ofSize: fontSize)
|
|
152
163
|
}
|
|
153
164
|
|
|
154
165
|
private func applyAutoComplete() {
|
|
@@ -60,4 +60,4 @@ export const Commands: NativeCommands = codegenNativeCommands<NativeCommands>({
|
|
|
60
60
|
supportedCommands: ['focus', 'blur'],
|
|
61
61
|
});
|
|
62
62
|
|
|
63
|
-
export default codegenNativeComponent<NativeProps>('ControlledInputView');
|
|
63
|
+
export default codegenNativeComponent<NativeProps>('ControlledInputView');
|
package/lib/module/index.js
CHANGED
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
import { forwardRef, memo, useImperativeHandle, useLayoutEffect, useRef } from 'react';
|
|
4
4
|
import { Platform, processColor, StyleSheet } from 'react-native';
|
|
5
|
-
// TextInputState is not exported from the public react-native package.
|
|
6
|
-
// eslint-disable-next-line @react-native/no-deep-imports -- integrate with ScrollView keyboard dismissal
|
|
7
5
|
import TextInputState from 'react-native/Libraries/Components/TextInput/TextInputState';
|
|
8
6
|
import ControlledInputViewNativeComponent from './ControlledInputViewNativeComponent';
|
|
9
7
|
import { jsx as _jsx } from "react/jsx-runtime";
|
package/lib/module/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["forwardRef","memo","useImperativeHandle","useLayoutEffect","useRef","Platform","processColor","StyleSheet","TextInputState","ControlledInputViewNativeComponent","jsx","_jsx","androidComposeHandledKeys","resolveAndroidComposeViewPadding","flat","padding","paddingTop","paddingVertical","paddingBottom","paddingLeft","paddingHorizontal","paddingRight","ControlledInputView","style","onTextChange","onFocus","onBlur","selectionColor","placeholderTextColor","rest","ref","nativeRef","isNativePlatform","OS","node","current","registerInput","unregisterInput","currentlyFocusedInput","blurTextInput","flattenedStyle","flatten","viewStyle","inputStyle","Object","fromEntries","entries","filter","k","includes","hasPadding","some","v","color","fontSize","fontFamily","borderWidth","borderRadius","borderColor","backgroundColor","iosViewStyle","hasTextStyle","undefined","handleTextChange","e","nativeEvent","value","handleFocus","focusInput","handleBlur","blurInput","blur","focus","focusTextInput","displayName"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,SACEA,UAAU,EACVC,IAAI,EACJC,mBAAmB,EACnBC,eAAe,EACfC,MAAM,QAED,OAAO;AACd,SACEC,QAAQ,EACRC,YAAY,EACZC,UAAU,QAIL,cAAc;AACrB
|
|
1
|
+
{"version":3,"names":["forwardRef","memo","useImperativeHandle","useLayoutEffect","useRef","Platform","processColor","StyleSheet","TextInputState","ControlledInputViewNativeComponent","jsx","_jsx","androidComposeHandledKeys","resolveAndroidComposeViewPadding","flat","padding","paddingTop","paddingVertical","paddingBottom","paddingLeft","paddingHorizontal","paddingRight","ControlledInputView","style","onTextChange","onFocus","onBlur","selectionColor","placeholderTextColor","rest","ref","nativeRef","isNativePlatform","OS","node","current","registerInput","unregisterInput","currentlyFocusedInput","blurTextInput","flattenedStyle","flatten","viewStyle","inputStyle","Object","fromEntries","entries","filter","k","includes","hasPadding","some","v","color","fontSize","fontFamily","borderWidth","borderRadius","borderColor","backgroundColor","iosViewStyle","hasTextStyle","undefined","handleTextChange","e","nativeEvent","value","handleFocus","focusInput","handleBlur","blurInput","blur","focus","focusTextInput","displayName"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAQA,OAAO,EAIL,KAAK,cAAc,EAGpB,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAQA,OAAO,EAIL,KAAK,cAAc,EAGpB,MAAM,cAAc,CAAC;AAEtB,OAA2C,EACzC,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;AA2CJ,eAAO,MAAM,mBAAmB;mBA5Cb,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI;0DAiLzC,CAAC;AAIF,cAAc,sCAAsC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-controlled-input",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "React Native
|
|
3
|
+
"version": "0.16.0",
|
|
4
|
+
"description": "React Native controlled TextInput with strict value sync (Fabric)",
|
|
5
5
|
"main": "./lib/module/index.js",
|
|
6
6
|
"types": "./lib/typescript/src/index.d.ts",
|
|
7
7
|
"exports": {
|
|
@@ -32,13 +32,15 @@
|
|
|
32
32
|
"!**/.*"
|
|
33
33
|
],
|
|
34
34
|
"scripts": {
|
|
35
|
-
"example": "
|
|
35
|
+
"example": "npm run start -w react-native-controlled-input-example",
|
|
36
36
|
"clean": "del-cli android/build example/android/build example/android/app/build example/ios/build lib",
|
|
37
37
|
"prepare": "bob build",
|
|
38
|
+
"build": "bob build",
|
|
38
39
|
"typecheck": "tsc",
|
|
39
|
-
"lint": "eslint
|
|
40
|
+
"lint": "npx tsc && npx eslint ./",
|
|
41
|
+
"format": "npx prettier --write . && npm run lint -- --fix",
|
|
40
42
|
"test": "jest",
|
|
41
|
-
"release": "release-it
|
|
43
|
+
"release": "release-it"
|
|
42
44
|
},
|
|
43
45
|
"keywords": [
|
|
44
46
|
"react-native",
|
|
@@ -49,7 +51,7 @@
|
|
|
49
51
|
"type": "git",
|
|
50
52
|
"url": "git+https://github.com/veliseev93/react-native-controlled-input.git"
|
|
51
53
|
},
|
|
52
|
-
"author": "
|
|
54
|
+
"author": "veliseev93 (https://github.com/veliseev93)",
|
|
53
55
|
"license": "MIT",
|
|
54
56
|
"bugs": {
|
|
55
57
|
"url": "https://github.com/veliseev93/react-native-controlled-input/issues"
|
|
@@ -60,28 +62,30 @@
|
|
|
60
62
|
},
|
|
61
63
|
"devDependencies": {
|
|
62
64
|
"@commitlint/config-conventional": "^19.8.1",
|
|
63
|
-
"@eslint/compat": "^1.
|
|
64
|
-
"@eslint/eslintrc": "^3.3.1",
|
|
65
|
-
"@eslint/js": "^9.35.0",
|
|
65
|
+
"@eslint/compat": "^1.2.5",
|
|
66
66
|
"@react-native/babel-preset": "0.83.0",
|
|
67
|
-
"@react-native/eslint-config": "0.83.0",
|
|
68
67
|
"@release-it/conventional-changelog": "^10.0.1",
|
|
68
|
+
"@stylistic/eslint-plugin": "^2.13.0",
|
|
69
69
|
"@types/jest": "^29.5.14",
|
|
70
70
|
"@types/react": "^19.2.0",
|
|
71
71
|
"commitlint": "^19.8.1",
|
|
72
72
|
"del-cli": "^6.0.0",
|
|
73
|
-
"eslint": "^9.
|
|
74
|
-
"eslint-config-prettier": "^
|
|
75
|
-
"eslint-
|
|
73
|
+
"eslint": "^9.18.0",
|
|
74
|
+
"eslint-config-prettier": "^9.1.0",
|
|
75
|
+
"eslint-import-resolver-typescript": "^3.7.0",
|
|
76
|
+
"eslint-plugin-import": "^2.31.0",
|
|
77
|
+
"eslint-plugin-unused-imports": "^4.1.4",
|
|
78
|
+
"globals": "^15.14.0",
|
|
76
79
|
"jest": "^29.7.0",
|
|
77
80
|
"lefthook": "^2.0.3",
|
|
78
|
-
"prettier": "^
|
|
81
|
+
"prettier": "^3.4.2",
|
|
79
82
|
"react": "19.2.0",
|
|
80
83
|
"react-native": "0.83.0",
|
|
81
84
|
"react-native-builder-bob": "^0.40.17",
|
|
82
85
|
"release-it": "^19.0.4",
|
|
83
86
|
"turbo": "^2.5.6",
|
|
84
|
-
"typescript": "^5.
|
|
87
|
+
"typescript": "^5.7.3",
|
|
88
|
+
"typescript-eslint": "^8.20.0"
|
|
85
89
|
},
|
|
86
90
|
"peerDependencies": {
|
|
87
91
|
"react": "*",
|
|
@@ -90,7 +94,6 @@
|
|
|
90
94
|
"workspaces": [
|
|
91
95
|
"example"
|
|
92
96
|
],
|
|
93
|
-
"packageManager": "yarn@4.11.0",
|
|
94
97
|
"react-native-builder-bob": {
|
|
95
98
|
"source": "src",
|
|
96
99
|
"output": "lib",
|
|
@@ -122,13 +125,6 @@
|
|
|
122
125
|
}
|
|
123
126
|
}
|
|
124
127
|
},
|
|
125
|
-
"prettier": {
|
|
126
|
-
"quoteProps": "consistent",
|
|
127
|
-
"singleQuote": true,
|
|
128
|
-
"tabWidth": 2,
|
|
129
|
-
"trailingComma": "es5",
|
|
130
|
-
"useTabs": false
|
|
131
|
-
},
|
|
132
128
|
"jest": {
|
|
133
129
|
"preset": "react-native",
|
|
134
130
|
"modulePathIgnorePatterns": [
|
|
@@ -142,12 +138,15 @@
|
|
|
142
138
|
]
|
|
143
139
|
},
|
|
144
140
|
"release-it": {
|
|
141
|
+
"increment": false,
|
|
145
142
|
"git": {
|
|
146
143
|
"commitMessage": "chore: release ${version}",
|
|
147
|
-
"tagName": "v${version}"
|
|
144
|
+
"tagName": "v${version}",
|
|
145
|
+
"requireBranch": ["main", "test-keboard--controller"]
|
|
148
146
|
},
|
|
149
147
|
"npm": {
|
|
150
|
-
"publish": true
|
|
148
|
+
"publish": true,
|
|
149
|
+
"tag": "alpha"
|
|
151
150
|
},
|
|
152
151
|
"github": {
|
|
153
152
|
"release": true
|
|
@@ -60,4 +60,4 @@ export const Commands: NativeCommands = codegenNativeCommands<NativeCommands>({
|
|
|
60
60
|
supportedCommands: ['focus', 'blur'],
|
|
61
61
|
});
|
|
62
62
|
|
|
63
|
-
export default codegenNativeComponent<NativeProps>('ControlledInputView');
|
|
63
|
+
export default codegenNativeComponent<NativeProps>('ControlledInputView');
|
package/src/index.tsx
CHANGED
|
@@ -14,8 +14,6 @@ import {
|
|
|
14
14
|
type ViewStyle,
|
|
15
15
|
type TextStyle,
|
|
16
16
|
} from 'react-native';
|
|
17
|
-
// TextInputState is not exported from the public react-native package.
|
|
18
|
-
// eslint-disable-next-line @react-native/no-deep-imports -- integrate with ScrollView keyboard dismissal
|
|
19
17
|
import TextInputState from 'react-native/Libraries/Components/TextInput/TextInputState';
|
|
20
18
|
import ControlledInputViewNativeComponent, {
|
|
21
19
|
type NativeProps,
|
|
@@ -71,7 +69,12 @@ const androidComposeHandledKeys = [
|
|
|
71
69
|
'backgroundColor',
|
|
72
70
|
];
|
|
73
71
|
|
|
74
|
-
function resolveAndroidComposeViewPadding(flat: Record<string, any>) {
|
|
72
|
+
function resolveAndroidComposeViewPadding(flat: Record<string, any>): {
|
|
73
|
+
paddingTop: unknown;
|
|
74
|
+
paddingBottom: unknown;
|
|
75
|
+
paddingLeft: unknown;
|
|
76
|
+
paddingRight: unknown;
|
|
77
|
+
} {
|
|
75
78
|
const padding = flat.padding ?? 0;
|
|
76
79
|
|
|
77
80
|
return {
|
|
@@ -108,6 +111,7 @@ export const ControlledInputView = memo(
|
|
|
108
111
|
}
|
|
109
112
|
|
|
110
113
|
const node = nativeRef.current;
|
|
114
|
+
|
|
111
115
|
if (node == null) {
|
|
112
116
|
return;
|
|
113
117
|
}
|
|
@@ -116,6 +120,7 @@ export const ControlledInputView = memo(
|
|
|
116
120
|
|
|
117
121
|
return () => {
|
|
118
122
|
TextInputState.unregisterInput(node);
|
|
123
|
+
|
|
119
124
|
if (TextInputState.currentlyFocusedInput() === node) {
|
|
120
125
|
TextInputState.blurTextInput(node);
|
|
121
126
|
}
|
|
@@ -167,20 +172,20 @@ export const ControlledInputView = memo(
|
|
|
167
172
|
|
|
168
173
|
const handleTextChange = (e: {
|
|
169
174
|
nativeEvent: Readonly<TextChangeEvent>;
|
|
170
|
-
}) => {
|
|
175
|
+
}): void => {
|
|
171
176
|
if (onTextChange) {
|
|
172
177
|
onTextChange(e.nativeEvent.value);
|
|
173
178
|
}
|
|
174
179
|
};
|
|
175
180
|
|
|
176
|
-
const handleFocus = (e: ControlledInputFocusEvent) => {
|
|
181
|
+
const handleFocus = (e: ControlledInputFocusEvent): void => {
|
|
177
182
|
if (isNativePlatform) {
|
|
178
183
|
TextInputState.focusInput(nativeRef.current);
|
|
179
184
|
}
|
|
180
185
|
onFocus?.(e);
|
|
181
186
|
};
|
|
182
187
|
|
|
183
|
-
const handleBlur = (e: ControlledInputBlurEvent) => {
|
|
188
|
+
const handleBlur = (e: ControlledInputBlurEvent): void => {
|
|
184
189
|
if (isNativePlatform) {
|
|
185
190
|
TextInputState.blurInput(nativeRef.current);
|
|
186
191
|
}
|
|
@@ -15,6 +15,7 @@ declare module 'react-native/Libraries/Types/CodegenTypes' {
|
|
|
15
15
|
|
|
16
16
|
declare module 'react-native/Libraries/Utilities/codegenNativeComponent' {
|
|
17
17
|
import type { HostComponent } from 'react-native';
|
|
18
|
+
|
|
18
19
|
export default function codegenNativeComponent<T>(
|
|
19
20
|
componentName: string
|
|
20
21
|
): HostComponent<T>;
|
|
@@ -34,4 +35,4 @@ declare module 'react-native/Libraries/Components/TextInput/TextInputState' {
|
|
|
34
35
|
};
|
|
35
36
|
|
|
36
37
|
export default TextInputState;
|
|
37
|
-
}
|
|
38
|
+
}
|