react-native-controlled-input 0.21.0 → 0.23.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.
@@ -2,7 +2,6 @@ package com.controlledinput
2
2
 
3
3
  import android.content.Context
4
4
  import android.util.AttributeSet
5
- import android.util.Log
6
5
  import android.util.TypedValue
7
6
  import android.view.View
8
7
  import android.view.inputmethod.InputMethodManager
@@ -32,14 +31,8 @@ import kotlinx.coroutines.flow.MutableStateFlow
32
31
  /**
33
32
  * - [shouldUseAndroidLayout]: requestLayout posts measureAndLayout
34
33
  * - onMeasure skips child [ComposeView] until attached (window + WindowRecomposer)
35
- *
36
- * @see expo.modules.kotlin.views.ExpoComposeView
37
- * @see expo.modules.kotlin.views.ExpoView
38
34
  */
39
35
  class ControlledInputView : LinearLayout, LifecycleOwner {
40
- companion object {
41
- private const val TAG = "ControlledInputView"
42
- }
43
36
  constructor(context: Context) : super(context) {
44
37
  configureComponent(context)
45
38
  }
@@ -67,8 +60,9 @@ class ControlledInputView : LinearLayout, LifecycleOwner {
67
60
  private var windowLifecycleBound = false
68
61
 
69
62
  /**
70
- * Hidden [EditText] used only as KBC's [FocusedInputObserver.lastFocusedInput]: [syncUpLayout]
71
- * reads [EditText]-scoped geometry. It is NOT focused and does not participate in the focus
63
+ * Hidden [EditText] used only as [FocusedInputObserver.lastFocusedInput] from
64
+ * https://github.com/kirillzyusko/react-native-keyboard-controller; [syncUpLayout] reads
65
+ * [EditText]-scoped geometry. It is NOT focused and does not participate in the focus
72
66
  * chain — we push state via reflection + synthetic selection events instead.
73
67
  */
74
68
  private val kbcLayoutHost: EditText by lazy {
@@ -85,8 +79,9 @@ class ControlledInputView : LinearLayout, LifecycleOwner {
85
79
  }
86
80
 
87
81
  /**
88
- * EdgeToEdgeViewRegistry → KeyboardAnimationCallback + FocusedInputObserver.
89
- * Null if react-native-keyboard-controller is missing or not initialized.
82
+ * EdgeToEdgeViewRegistry → KeyboardAnimationCallback + FocusedInputObserver
83
+ * (https://github.com/kirillzyusko/react-native-keyboard-controller).
84
+ * Null if that library is missing or not initialized.
90
85
  */
91
86
  private fun resolveKbcCallbackAndObserver(): Pair<Any, Any>? {
92
87
  try {
@@ -94,49 +89,25 @@ class ControlledInputView : LinearLayout, LifecycleOwner {
94
89
  Class.forName("com.reactnativekeyboardcontroller.views.EdgeToEdgeViewRegistry")
95
90
  val registryInstance = registryClass.getField("INSTANCE").get(null)
96
91
  val edgeToEdgeView =
97
- registryClass.getDeclaredMethod("get").invoke(registryInstance)
98
- ?: run {
99
- Log.w(TAG, "resolveKbcCallbackAndObserver: EdgeToEdgeViewRegistry.get() == null")
100
- return null
101
- }
92
+ registryClass.getDeclaredMethod("get").invoke(registryInstance) ?: return null
102
93
 
103
94
  val callbackField =
104
95
  edgeToEdgeView.javaClass.declaredFields.firstOrNull {
105
96
  it.type.simpleName == "KeyboardAnimationCallback"
106
- }
107
- ?: run {
108
- Log.w(TAG, "resolveKbcCallbackAndObserver: KeyboardAnimationCallback field not found")
109
- return null
110
- }
97
+ } ?: return null
111
98
  callbackField.isAccessible = true
112
- val callback =
113
- callbackField.get(edgeToEdgeView)
114
- ?: run {
115
- Log.w(TAG, "resolveKbcCallbackAndObserver: callback == null")
116
- return null
117
- }
99
+ val callback = callbackField.get(edgeToEdgeView) ?: return null
118
100
 
119
101
  val observerField =
120
102
  callback.javaClass.declaredFields.firstOrNull {
121
103
  it.type.simpleName == "FocusedInputObserver"
122
- }
123
- ?: run {
124
- Log.w(TAG, "resolveKbcCallbackAndObserver: FocusedInputObserver field not found")
125
- return null
126
- }
104
+ } ?: return null
127
105
  observerField.isAccessible = true
128
- val observer =
129
- observerField.get(callback)
130
- ?: run {
131
- Log.w(TAG, "resolveKbcCallbackAndObserver: layoutObserver == null")
132
- return null
133
- }
106
+ val observer = observerField.get(callback) ?: return null
134
107
  return Pair(callback, observer)
135
108
  } catch (_: ClassNotFoundException) {
136
- Log.d(TAG, "resolveKbcCallbackAndObserver: keyboard-controller not on classpath")
137
109
  return null
138
- } catch (e: Exception) {
139
- Log.w(TAG, "resolveKbcCallbackAndObserver: ${e.javaClass.simpleName}: ${e.message}")
110
+ } catch (_: Exception) {
140
111
  return null
141
112
  }
142
113
  }
@@ -146,9 +117,7 @@ class ControlledInputView : LinearLayout, LifecycleOwner {
146
117
  val f = callback.javaClass.getDeclaredField("viewTagFocused")
147
118
  f.isAccessible = true
148
119
  f.setInt(callback, id)
149
- Log.d(TAG, "setKbcViewTagFocused: viewTagFocused=$id")
150
- } catch (e: Exception) {
151
- Log.w(TAG, "setKbcViewTagFocused: ${e.javaClass.simpleName}: ${e.message}")
120
+ } catch (_: Exception) {
152
121
  }
153
122
  }
154
123
 
@@ -160,14 +129,14 @@ class ControlledInputView : LinearLayout, LifecycleOwner {
160
129
  holderClass
161
130
  .getMethod("set", EditText::class.java)
162
131
  .invoke(instance, kbcLayoutHost)
163
- } catch (e: Exception) {
164
- Log.w(TAG, "setKbcFocusedInputHolder: ${e.javaClass.simpleName}: ${e.message}")
132
+ } catch (_: Exception) {
165
133
  }
166
134
  }
167
135
 
168
136
  /**
169
- * `selection.end.y` for KBC / JS customHeight: prefer explicit style height (dp, same as padding
170
- * in [InputStyle]), else measured view height in dp.
137
+ * `selection.end.y` for https://github.com/kirillzyusko/react-native-keyboard-controller / JS
138
+ * customHeight: prefer explicit style height (dp, same as padding in [InputStyle]), else measured
139
+ * view height in dp.
171
140
  */
172
141
  private fun approximateSelectionEndYDp(): Double {
173
142
  viewModel.inputStyle.value?.height?.takeIf { it > 0 }?.let { return it }
@@ -192,11 +161,7 @@ class ControlledInputView : LinearLayout, LifecycleOwner {
192
161
  val dataClz =
193
162
  Class.forName("com.reactnativekeyboardcontroller.events.FocusedInputSelectionChangedEventData")
194
163
  val dataCtor =
195
- dataClz.declaredConstructors.singleOrNull { it.parameterTypes.size == 7 }
196
- ?: run {
197
- Log.w(TAG, "dispatchSyntheticKbcSelectionEvent: no 7-arg data ctor")
198
- return
199
- }
164
+ dataClz.declaredConstructors.singleOrNull { it.parameterTypes.size == 7 } ?: return
200
165
  dataCtor.isAccessible = true
201
166
  val data =
202
167
  dataCtor.newInstance(targetId, 0.0, 0.0, 0.0, endY, 0, 0)
@@ -213,21 +178,16 @@ class ControlledInputView : LinearLayout, LifecycleOwner {
213
178
 
214
179
  UIManagerHelper.getEventDispatcherForReactTag(reactContext, propagationId)
215
180
  ?.dispatchEvent(event)
216
- Log.d(
217
- TAG,
218
- "dispatchSyntheticKbcSelectionEvent: propagationId=$propagationId target=$targetId endY(dp)=$endY",
219
- )
220
- } catch (e: Exception) {
221
- Log.w(TAG, "dispatchSyntheticKbcSelectionEvent: ${e.javaClass.simpleName}: ${e.message}")
181
+ } catch (_: Exception) {
222
182
  }
223
183
  }
224
184
 
225
185
  /**
226
- * Pushes ControlledInput state into KBC without stealing Compose focus:
227
- * viewTagFocused, lastFocusedInput, FocusedInputHolder, syncUpLayout, synthetic selection.
186
+ * Pushes ControlledInput state into https://github.com/kirillzyusko/react-native-keyboard-controller
187
+ * without stealing Compose focus: viewTagFocused, lastFocusedInput, FocusedInputHolder,
188
+ * syncUpLayout, synthetic selection.
228
189
  */
229
190
  private fun syncKeyboardControllerFocusedInput() {
230
- Log.d(TAG, "syncKeyboardControllerFocusedInput: id=$id")
231
191
  kbcLayoutHost.id = id
232
192
  viewModel.inputStyle.value?.fontSize?.toFloat()?.let {
233
193
  kbcLayoutHost.setTextSize(TypedValue.COMPLEX_UNIT_SP, it)
@@ -246,11 +206,9 @@ class ControlledInputView : LinearLayout, LifecycleOwner {
246
206
  val syncMethod = observer.javaClass.getDeclaredMethod("syncUpLayout")
247
207
  syncMethod.isAccessible = true
248
208
  syncMethod.invoke(observer)
249
- Log.d(TAG, "syncKeyboardControllerFocusedInput: syncUpLayout() ok")
250
209
 
251
210
  dispatchSyntheticKbcSelectionEvent(observer)
252
- } catch (e: Exception) {
253
- Log.w(TAG, "syncKeyboardControllerFocusedInput: ${e.javaClass.simpleName}: ${e.message}")
211
+ } catch (_: Exception) {
254
212
  }
255
213
  }
256
214
 
@@ -259,11 +217,6 @@ class ControlledInputView : LinearLayout, LifecycleOwner {
259
217
  override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
260
218
  super.onLayout(changed, l, t, r, b)
261
219
  kbcLayoutHost.layout(0, 0, width, height)
262
- if (changed) {
263
- val loc = IntArray(2)
264
- getLocationOnScreen(loc)
265
- Log.d(TAG, "onLayout: view=${width}x${height} screenX=${loc[0]} screenY=${loc[1]}")
266
- }
267
220
  }
268
221
 
269
222
  override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
@@ -295,12 +248,10 @@ class ControlledInputView : LinearLayout, LifecycleOwner {
295
248
 
296
249
  override fun onAttachedToWindow() {
297
250
  super.onAttachedToWindow()
298
- Log.d(TAG, "onAttachedToWindow: id=$id")
299
251
  bindComposeToWindowLifecycle()
300
252
  }
301
253
 
302
254
  override fun onDetachedFromWindow() {
303
- Log.d(TAG, "onDetachedFromWindow: id=$id")
304
255
  if (usesLocalFallbackLifecycle) {
305
256
  lifecycleRegistry.currentState = Lifecycle.State.CREATED
306
257
  }
@@ -345,7 +296,6 @@ class ControlledInputView : LinearLayout, LifecycleOwner {
345
296
  }
346
297
 
347
298
  fun blur() {
348
- Log.d(TAG, "blur() called from JS ref")
349
299
  blurSignal.value = blurSignal.value + 1
350
300
  val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
351
301
  imm.hideSoftInputFromWindow(windowToken, 0)
@@ -353,7 +303,6 @@ class ControlledInputView : LinearLayout, LifecycleOwner {
353
303
  }
354
304
 
355
305
  fun focus() {
356
- Log.d(TAG, "focus() called from JS ref")
357
306
  focusSignal.value = focusSignal.value + 1
358
307
  }
359
308
 
@@ -421,7 +370,6 @@ class ControlledInputView : LinearLayout, LifecycleOwner {
421
370
  )
422
371
  },
423
372
  onFocus = {
424
- Log.d(TAG, "Compose onFocus id=$id")
425
373
  val surfaceId = UIManagerHelper.getSurfaceId(context)
426
374
  val viewId = this@ControlledInputView.id
427
375
  UIManagerHelper
@@ -430,7 +378,6 @@ class ControlledInputView : LinearLayout, LifecycleOwner {
430
378
  post { syncKeyboardControllerFocusedInput() }
431
379
  },
432
380
  onBlur = {
433
- Log.d(TAG, "Compose onBlur id=$id")
434
381
  val surfaceId = UIManagerHelper.getSurfaceId(context)
435
382
  val viewId = this@ControlledInputView.id
436
383
  UIManagerHelper
@@ -43,6 +43,12 @@ using namespace facebook::react;
43
43
  return self;
44
44
  }
45
45
 
46
+ - (void)setTag:(NSInteger)tag
47
+ {
48
+ [super setTag:tag];
49
+ _inputView.tag = tag;
50
+ }
51
+
46
52
  - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
47
53
  {
48
54
  const auto &oldViewProps = *std::static_pointer_cast<ControlledInputViewProps const>(_props);
@@ -25,7 +25,6 @@ export interface BlurEvent {
25
25
  export interface InputStyle {
26
26
  color?: ColorValue;
27
27
  fontSize?: Double;
28
- /** Matches style.height (dp); used for keyboard-controller customHeight on Android. */
29
28
  height?: Double;
30
29
  fontFamily?: string;
31
30
  paddingTop?: Double;
@@ -10,7 +10,6 @@ export interface BlurEvent {
10
10
  export interface InputStyle {
11
11
  color?: ColorValue;
12
12
  fontSize?: Double;
13
- /** Matches style.height (dp); used for keyboard-controller customHeight on Android. */
14
13
  height?: Double;
15
14
  fontFamily?: string;
16
15
  paddingTop?: Double;
@@ -1 +1 @@
1
- {"version":3,"file":"ControlledInputViewNativeComponent.d.ts","sourceRoot":"","sources":["../../../src/ControlledInputViewNativeComponent.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAEtB,OAAO,KAAK,EACV,oBAAoB,EACpB,MAAM,EACP,MAAM,2CAA2C,CAAC;AAEnD,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,UAAU;CAE1B;AAED,MAAM,WAAW,SAAS;CAEzB;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uFAAuF;IACvF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,WAAY,SAAQ,SAAS;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oBAAoB,CAAC,EAAE,UAAU,CAAC;IAClC,cAAc,CAAC,EAAE,UAAU,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,YAAY,CAAC,EAAE,oBAAoB,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC,CAAC;IAC/D,OAAO,CAAC,EAAE,oBAAoB,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC;IACrD,MAAM,CAAC,EAAE,oBAAoB,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;CACpD;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,UAAU,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,KAAK,IAAI,CAAC;IACvE,IAAI,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,UAAU,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,KAAK,IAAI,CAAC;CACvE;AAED,eAAO,MAAM,QAAQ,EAAE,cAErB,CAAC;;AAEH,wBAA0E"}
1
+ {"version":3,"file":"ControlledInputViewNativeComponent.d.ts","sourceRoot":"","sources":["../../../src/ControlledInputViewNativeComponent.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAEtB,OAAO,KAAK,EACV,oBAAoB,EACpB,MAAM,EACP,MAAM,2CAA2C,CAAC;AAEnD,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,UAAU;CAE1B;AAED,MAAM,WAAW,SAAS;CAEzB;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,WAAY,SAAQ,SAAS;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oBAAoB,CAAC,EAAE,UAAU,CAAC;IAClC,cAAc,CAAC,EAAE,UAAU,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,YAAY,CAAC,EAAE,oBAAoB,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC,CAAC;IAC/D,OAAO,CAAC,EAAE,oBAAoB,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC;IACrD,MAAM,CAAC,EAAE,oBAAoB,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;CACpD;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,UAAU,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,KAAK,IAAI,CAAC;IACvE,IAAI,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,UAAU,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,KAAK,IAAI,CAAC;CACvE;AAED,eAAO,MAAM,QAAQ,EAAE,cAErB,CAAC;;AAEH,wBAA0E"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-controlled-input",
3
- "version": "0.21.0",
3
+ "version": "0.23.0",
4
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",
@@ -25,7 +25,6 @@ export interface BlurEvent {
25
25
  export interface InputStyle {
26
26
  color?: ColorValue;
27
27
  fontSize?: Double;
28
- /** Matches style.height (dp); used for keyboard-controller customHeight on Android. */
29
28
  height?: Double;
30
29
  fontFamily?: string;
31
30
  paddingTop?: Double;