@onekeyfe/react-native-auto-size-input 1.0.1

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.
Files changed (90) hide show
  1. package/AutoSizeInput.podspec +29 -0
  2. package/android/CMakeLists.txt +24 -0
  3. package/android/build.gradle +128 -0
  4. package/android/src/main/AndroidManifest.xml +2 -0
  5. package/android/src/main/cpp/cpp-adapter.cpp +6 -0
  6. package/android/src/main/java/com/margelo/nitro/autosizeinput/AutoSizeInput.kt +565 -0
  7. package/android/src/main/java/com/margelo/nitro/autosizeinput/AutoSizeInputPackage.kt +29 -0
  8. package/ios/AutoSizeInput.swift +620 -0
  9. package/lib/module/AutoSizeInput.nitro.js +4 -0
  10. package/lib/module/AutoSizeInput.nitro.js.map +1 -0
  11. package/lib/module/index.js +6 -0
  12. package/lib/module/index.js.map +1 -0
  13. package/lib/module/package.json +1 -0
  14. package/lib/nitrogen/generated/android/autosizeinput+autolinking.cmake +83 -0
  15. package/lib/nitrogen/generated/android/autosizeinput+autolinking.gradle +27 -0
  16. package/lib/nitrogen/generated/android/autosizeinputOnLoad.cpp +50 -0
  17. package/lib/nitrogen/generated/android/autosizeinputOnLoad.hpp +25 -0
  18. package/lib/nitrogen/generated/android/c++/JFunc_void.hpp +75 -0
  19. package/lib/nitrogen/generated/android/c++/JFunc_void_std__string.hpp +76 -0
  20. package/lib/nitrogen/generated/android/c++/JHybridAutoSizeInputSpec.cpp +317 -0
  21. package/lib/nitrogen/generated/android/c++/JHybridAutoSizeInputSpec.hpp +117 -0
  22. package/lib/nitrogen/generated/android/c++/views/JHybridAutoSizeInputStateUpdater.cpp +156 -0
  23. package/lib/nitrogen/generated/android/c++/views/JHybridAutoSizeInputStateUpdater.hpp +49 -0
  24. package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/autosizeinput/Func_void.kt +80 -0
  25. package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/autosizeinput/Func_void_std__string.kt +80 -0
  26. package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/autosizeinput/HybridAutoSizeInputSpec.kt +239 -0
  27. package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/autosizeinput/autosizeinputOnLoad.kt +35 -0
  28. package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/autosizeinput/views/HybridAutoSizeInputManager.kt +50 -0
  29. package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/autosizeinput/views/HybridAutoSizeInputStateUpdater.kt +23 -0
  30. package/lib/nitrogen/generated/ios/AutoSizeInput+autolinking.rb +60 -0
  31. package/lib/nitrogen/generated/ios/AutoSizeInput-Swift-Cxx-Bridge.cpp +49 -0
  32. package/lib/nitrogen/generated/ios/AutoSizeInput-Swift-Cxx-Bridge.hpp +173 -0
  33. package/lib/nitrogen/generated/ios/AutoSizeInput-Swift-Cxx-Umbrella.hpp +46 -0
  34. package/lib/nitrogen/generated/ios/AutoSizeInputAutolinking.mm +33 -0
  35. package/lib/nitrogen/generated/ios/AutoSizeInputAutolinking.swift +25 -0
  36. package/lib/nitrogen/generated/ios/c++/HybridAutoSizeInputSpecSwift.cpp +11 -0
  37. package/lib/nitrogen/generated/ios/c++/HybridAutoSizeInputSpecSwift.hpp +263 -0
  38. package/lib/nitrogen/generated/ios/c++/views/HybridAutoSizeInputComponent.mm +221 -0
  39. package/lib/nitrogen/generated/ios/swift/Func_void.swift +47 -0
  40. package/lib/nitrogen/generated/ios/swift/Func_void_std__string.swift +47 -0
  41. package/lib/nitrogen/generated/ios/swift/HybridAutoSizeInputSpec.swift +82 -0
  42. package/lib/nitrogen/generated/ios/swift/HybridAutoSizeInputSpec_cxx.swift +764 -0
  43. package/lib/nitrogen/generated/shared/c++/HybridAutoSizeInputSpec.cpp +74 -0
  44. package/lib/nitrogen/generated/shared/c++/HybridAutoSizeInputSpec.hpp +116 -0
  45. package/lib/nitrogen/generated/shared/c++/views/HybridAutoSizeInputComponent.cpp +387 -0
  46. package/lib/nitrogen/generated/shared/c++/views/HybridAutoSizeInputComponent.hpp +133 -0
  47. package/lib/nitrogen/generated/shared/json/AutoSizeInputConfig.json +35 -0
  48. package/lib/typescript/package.json +1 -0
  49. package/lib/typescript/src/AutoSizeInput.nitro.d.ts +35 -0
  50. package/lib/typescript/src/AutoSizeInput.nitro.d.ts.map +1 -0
  51. package/lib/typescript/src/index.d.ts +4 -0
  52. package/lib/typescript/src/index.d.ts.map +1 -0
  53. package/nitro.json +17 -0
  54. package/nitrogen/generated/android/autosizeinput+autolinking.cmake +83 -0
  55. package/nitrogen/generated/android/autosizeinput+autolinking.gradle +27 -0
  56. package/nitrogen/generated/android/autosizeinputOnLoad.cpp +50 -0
  57. package/nitrogen/generated/android/autosizeinputOnLoad.hpp +25 -0
  58. package/nitrogen/generated/android/c++/JFunc_void.hpp +75 -0
  59. package/nitrogen/generated/android/c++/JFunc_void_std__string.hpp +76 -0
  60. package/nitrogen/generated/android/c++/JHybridAutoSizeInputSpec.cpp +317 -0
  61. package/nitrogen/generated/android/c++/JHybridAutoSizeInputSpec.hpp +117 -0
  62. package/nitrogen/generated/android/c++/views/JHybridAutoSizeInputStateUpdater.cpp +156 -0
  63. package/nitrogen/generated/android/c++/views/JHybridAutoSizeInputStateUpdater.hpp +49 -0
  64. package/nitrogen/generated/android/kotlin/com/margelo/nitro/autosizeinput/Func_void.kt +80 -0
  65. package/nitrogen/generated/android/kotlin/com/margelo/nitro/autosizeinput/Func_void_std__string.kt +80 -0
  66. package/nitrogen/generated/android/kotlin/com/margelo/nitro/autosizeinput/HybridAutoSizeInputSpec.kt +239 -0
  67. package/nitrogen/generated/android/kotlin/com/margelo/nitro/autosizeinput/autosizeinputOnLoad.kt +35 -0
  68. package/nitrogen/generated/android/kotlin/com/margelo/nitro/autosizeinput/views/HybridAutoSizeInputManager.kt +50 -0
  69. package/nitrogen/generated/android/kotlin/com/margelo/nitro/autosizeinput/views/HybridAutoSizeInputStateUpdater.kt +23 -0
  70. package/nitrogen/generated/ios/AutoSizeInput+autolinking.rb +60 -0
  71. package/nitrogen/generated/ios/AutoSizeInput-Swift-Cxx-Bridge.cpp +49 -0
  72. package/nitrogen/generated/ios/AutoSizeInput-Swift-Cxx-Bridge.hpp +173 -0
  73. package/nitrogen/generated/ios/AutoSizeInput-Swift-Cxx-Umbrella.hpp +46 -0
  74. package/nitrogen/generated/ios/AutoSizeInputAutolinking.mm +33 -0
  75. package/nitrogen/generated/ios/AutoSizeInputAutolinking.swift +25 -0
  76. package/nitrogen/generated/ios/c++/HybridAutoSizeInputSpecSwift.cpp +11 -0
  77. package/nitrogen/generated/ios/c++/HybridAutoSizeInputSpecSwift.hpp +263 -0
  78. package/nitrogen/generated/ios/c++/views/HybridAutoSizeInputComponent.mm +221 -0
  79. package/nitrogen/generated/ios/swift/Func_void.swift +47 -0
  80. package/nitrogen/generated/ios/swift/Func_void_std__string.swift +47 -0
  81. package/nitrogen/generated/ios/swift/HybridAutoSizeInputSpec.swift +82 -0
  82. package/nitrogen/generated/ios/swift/HybridAutoSizeInputSpec_cxx.swift +764 -0
  83. package/nitrogen/generated/shared/c++/HybridAutoSizeInputSpec.cpp +74 -0
  84. package/nitrogen/generated/shared/c++/HybridAutoSizeInputSpec.hpp +116 -0
  85. package/nitrogen/generated/shared/c++/views/HybridAutoSizeInputComponent.cpp +387 -0
  86. package/nitrogen/generated/shared/c++/views/HybridAutoSizeInputComponent.hpp +133 -0
  87. package/nitrogen/generated/shared/json/AutoSizeInputConfig.json +35 -0
  88. package/package.json +170 -0
  89. package/src/AutoSizeInput.nitro.ts +56 -0
  90. package/src/index.tsx +13 -0
@@ -0,0 +1,565 @@
1
+ package com.margelo.nitro.autosizeinput
2
+
3
+ import android.graphics.Color
4
+ import android.graphics.Paint
5
+ import android.graphics.Typeface
6
+ import android.text.Editable
7
+ import android.text.InputType
8
+ import android.view.inputmethod.EditorInfo
9
+ import android.text.StaticLayout
10
+ import android.text.TextPaint
11
+ import android.text.TextWatcher
12
+ import android.util.TypedValue
13
+ import android.view.Gravity
14
+ import android.view.View
15
+ import android.view.ViewGroup
16
+ import android.widget.EditText
17
+ import android.widget.TextView
18
+ import com.facebook.proguard.annotations.DoNotStrip
19
+ import com.facebook.react.uimanager.ThemedReactContext
20
+
21
+ @DoNotStrip
22
+ class HybridAutoSizeInput(val context: ThemedReactContext) : HybridAutoSizeInputSpec() {
23
+
24
+ // Subviews
25
+ private val prefixView = TextView(context)
26
+ private val inputView = EditText(context)
27
+ private val suffixView = TextView(context)
28
+
29
+ // State
30
+ private var isUpdatingFromJS = false
31
+ private var isRecalculating = false
32
+ private var currentFontSize = 48f
33
+ private var isDisposed = false
34
+ private var maxFontSizeProp: Double? = null
35
+ private var minFontSizeProp: Double? = null
36
+
37
+ // Container view
38
+ override val view: View = object : ViewGroup(context) {
39
+ override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
40
+ performLayout(r - l, b - t)
41
+ }
42
+
43
+ override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
44
+ super.onSizeChanged(w, h, oldw, oldh)
45
+ if (w > 0 && h > 0) {
46
+ post { recalculateFontSize() }
47
+ }
48
+ }
49
+ }
50
+
51
+ // TextWatcher
52
+ private val textWatcher = object : TextWatcher {
53
+ override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
54
+ override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
55
+ override fun afterTextChanged(s: Editable?) {
56
+ if (isUpdatingFromJS || isDisposed) return
57
+ recalculateFontSize()
58
+ onChangeText?.invoke(s?.toString() ?: "")
59
+ }
60
+ }
61
+
62
+ // Props
63
+ override var text: String?
64
+ get() = inputView.text?.toString()
65
+ set(value) {
66
+ if (isDisposed) return
67
+ isUpdatingFromJS = true
68
+ inputView.removeTextChangedListener(textWatcher)
69
+ inputView.setText(value ?: "")
70
+ inputView.setSelection(inputView.text?.length ?: 0)
71
+ inputView.addTextChangedListener(textWatcher)
72
+ isUpdatingFromJS = false
73
+ recalculateFontSize()
74
+ }
75
+
76
+ override var prefix: String?
77
+ get() = prefixView.text?.toString()
78
+ set(value) {
79
+ if (isDisposed) return
80
+ prefixView.text = value ?: ""
81
+ prefixView.visibility = if ((value ?: "").isEmpty()) View.GONE else View.VISIBLE
82
+ view.requestLayout()
83
+ recalculateFontSize()
84
+ }
85
+
86
+ override var suffix: String?
87
+ get() = suffixView.text?.toString()
88
+ set(value) {
89
+ if (isDisposed) return
90
+ suffixView.text = value ?: ""
91
+ suffixView.visibility = if ((value ?: "").isEmpty()) View.GONE else View.VISIBLE
92
+ view.requestLayout()
93
+ recalculateFontSize()
94
+ }
95
+
96
+ override var placeholder: String?
97
+ get() = inputView.hint?.toString()
98
+ set(value) {
99
+ if (isDisposed) return
100
+ inputView.hint = value ?: ""
101
+ }
102
+
103
+ override var fontSize: Double?
104
+ get() = maxFontSizeProp
105
+ set(value) {
106
+ if (isDisposed) return
107
+ maxFontSizeProp = value
108
+ recalculateFontSize()
109
+ }
110
+
111
+ override var minFontSize: Double?
112
+ get() = minFontSizeProp
113
+ set(value) {
114
+ if (isDisposed) return
115
+ minFontSizeProp = value
116
+ recalculateFontSize()
117
+ }
118
+
119
+ override var multiline: Boolean?
120
+ get() = field
121
+ set(value) {
122
+ if (isDisposed) return
123
+ field = value
124
+ updateInputMode()
125
+ }
126
+
127
+ override var maxNumberOfLines: Double?
128
+ get() = field
129
+ set(value) {
130
+ if (isDisposed) return
131
+ field = value
132
+ recalculateFontSize()
133
+ }
134
+
135
+ override var textColor: String?
136
+ get() = field
137
+ set(value) {
138
+ if (isDisposed) return
139
+ field = value
140
+ val color = parseColor(value) ?: Color.BLACK
141
+ inputView.setTextColor(color)
142
+ if (prefixColor == null) prefixView.setTextColor(color)
143
+ if (suffixColor == null) suffixView.setTextColor(color)
144
+ }
145
+
146
+ override var prefixColor: String?
147
+ get() = field
148
+ set(value) {
149
+ if (isDisposed) return
150
+ field = value
151
+ prefixView.setTextColor(parseColor(value) ?: parseColor(textColor) ?: Color.BLACK)
152
+ }
153
+
154
+ override var suffixColor: String?
155
+ get() = field
156
+ set(value) {
157
+ if (isDisposed) return
158
+ field = value
159
+ suffixView.setTextColor(parseColor(value) ?: parseColor(textColor) ?: Color.BLACK)
160
+ }
161
+
162
+ override var placeholderColor: String?
163
+ get() = field
164
+ set(value) {
165
+ if (isDisposed) return
166
+ field = value
167
+ inputView.setHintTextColor(parseColor(value) ?: Color.GRAY)
168
+ }
169
+
170
+ override var textAlign: String?
171
+ get() = field
172
+ set(value) {
173
+ if (isDisposed) return
174
+ field = value
175
+ val gravity = when (value) {
176
+ "center" -> Gravity.CENTER
177
+ "right" -> Gravity.CENTER_VERTICAL or Gravity.END
178
+ else -> Gravity.CENTER_VERTICAL or Gravity.START
179
+ }
180
+ inputView.gravity = gravity
181
+ }
182
+
183
+ override var fontFamily: String?
184
+ get() = field
185
+ set(value) {
186
+ if (isDisposed) return
187
+ field = value
188
+ recalculateFontSize()
189
+ }
190
+
191
+ override var fontWeight: String?
192
+ get() = field
193
+ set(value) {
194
+ if (isDisposed) return
195
+ field = value
196
+ recalculateFontSize()
197
+ }
198
+
199
+ override var editable: Boolean?
200
+ get() = field
201
+ set(value) {
202
+ if (isDisposed) return
203
+ field = value
204
+ val isEditable = value ?: true
205
+ inputView.isFocusable = isEditable
206
+ inputView.isFocusableInTouchMode = isEditable
207
+ inputView.isCursorVisible = isEditable
208
+ }
209
+
210
+ override var keyboardType: String?
211
+ get() = field
212
+ set(value) {
213
+ if (isDisposed) return
214
+ field = value
215
+ inputView.inputType = when (value) {
216
+ "numberPad" -> InputType.TYPE_CLASS_NUMBER
217
+ "decimalPad" -> InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL
218
+ "emailAddress" -> InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
219
+ "phonePad" -> InputType.TYPE_CLASS_PHONE
220
+ else -> InputType.TYPE_CLASS_TEXT
221
+ }
222
+ }
223
+
224
+ override var returnKeyType: String?
225
+ get() = field
226
+ set(value) {
227
+ if (isDisposed) return
228
+ field = value
229
+ inputView.imeOptions = when (value) {
230
+ "done" -> EditorInfo.IME_ACTION_DONE
231
+ "go" -> EditorInfo.IME_ACTION_GO
232
+ "next" -> EditorInfo.IME_ACTION_NEXT
233
+ "search" -> EditorInfo.IME_ACTION_SEARCH
234
+ "send" -> EditorInfo.IME_ACTION_SEND
235
+ else -> EditorInfo.IME_ACTION_UNSPECIFIED
236
+ }
237
+ }
238
+
239
+ override var autoCorrect: Boolean?
240
+ get() = field
241
+ set(value) {
242
+ if (isDisposed) return
243
+ field = value
244
+ val currentType = inputView.inputType
245
+ inputView.inputType = if (value == true) {
246
+ currentType or InputType.TYPE_TEXT_FLAG_AUTO_CORRECT
247
+ } else {
248
+ currentType and InputType.TYPE_TEXT_FLAG_AUTO_CORRECT.inv()
249
+ }
250
+ }
251
+
252
+ override var autoCapitalize: String?
253
+ get() = field
254
+ set(value) {
255
+ if (isDisposed) return
256
+ field = value
257
+ val baseType = inputView.inputType and InputType.TYPE_MASK_CLASS
258
+ val capFlag = when (value) {
259
+ "characters" -> InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS
260
+ "words" -> InputType.TYPE_TEXT_FLAG_CAP_WORDS
261
+ "sentences" -> InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
262
+ else -> 0
263
+ }
264
+ // Clear existing cap flags, then apply new one
265
+ val cleared = inputView.inputType and
266
+ (InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS or
267
+ InputType.TYPE_TEXT_FLAG_CAP_WORDS or
268
+ InputType.TYPE_TEXT_FLAG_CAP_SENTENCES).inv()
269
+ inputView.inputType = cleared or capFlag
270
+ }
271
+
272
+ override var selectionColor: String?
273
+ get() = field
274
+ set(value) {
275
+ if (isDisposed) return
276
+ field = value
277
+ val color = parseColor(value)
278
+ if (color != null) {
279
+ inputView.highlightColor = color
280
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
281
+ inputView.textCursorDrawable?.setTint(color)
282
+ inputView.textSelectHandle?.setTint(color)
283
+ inputView.textSelectHandleLeft?.setTint(color)
284
+ inputView.textSelectHandleRight?.setTint(color)
285
+ }
286
+ }
287
+ }
288
+
289
+ override var prefixMarginRight: Double?
290
+ get() = field
291
+ set(value) {
292
+ if (isDisposed) return
293
+ field = value
294
+ view.requestLayout()
295
+ }
296
+
297
+ override var suffixMarginLeft: Double?
298
+ get() = field
299
+ set(value) {
300
+ if (isDisposed) return
301
+ field = value
302
+ view.requestLayout()
303
+ }
304
+
305
+ override var onChangeText: ((String) -> Unit)? = null
306
+ override var onFocus: (() -> Unit)? = null
307
+ override var onBlur: (() -> Unit)? = null
308
+
309
+ init {
310
+ setupViews()
311
+ }
312
+
313
+ private fun setupViews() {
314
+ // Configure prefix
315
+ prefixView.visibility = View.GONE
316
+ prefixView.includeFontPadding = false
317
+
318
+ // Configure suffix
319
+ suffixView.visibility = View.GONE
320
+ suffixView.includeFontPadding = false
321
+
322
+ // Configure input
323
+ inputView.background = null
324
+ inputView.setPadding(0, 0, 0, 0)
325
+ inputView.includeFontPadding = false
326
+ inputView.isSingleLine = true
327
+ inputView.maxLines = 1
328
+ inputView.addTextChangedListener(textWatcher)
329
+
330
+ inputView.setOnFocusChangeListener { _, hasFocus ->
331
+ if (isDisposed) return@setOnFocusChangeListener
332
+ if (hasFocus) onFocus?.invoke() else onBlur?.invoke()
333
+ }
334
+
335
+ // Add to container
336
+ (view as ViewGroup).addView(prefixView)
337
+ (view as ViewGroup).addView(inputView)
338
+ (view as ViewGroup).addView(suffixView)
339
+ }
340
+
341
+ private fun updateInputMode() {
342
+ val isMulti = multiline == true
343
+ if (isMulti) {
344
+ inputView.isSingleLine = false
345
+ inputView.maxLines = (maxNumberOfLines ?: 1.0).toInt()
346
+ inputView.inputType = inputView.inputType or InputType.TYPE_TEXT_FLAG_MULTI_LINE
347
+ } else {
348
+ inputView.isSingleLine = true
349
+ inputView.maxLines = 1
350
+ }
351
+ view.requestLayout()
352
+ recalculateFontSize()
353
+ }
354
+
355
+ private fun performLayout(width: Int, height: Int) {
356
+ if (width <= 0 || height <= 0) return
357
+
358
+ val density = context.resources.displayMetrics.density
359
+
360
+ // Measure prefix
361
+ val prefixW = if (prefixView.visibility == View.VISIBLE) {
362
+ prefixView.measure(
363
+ View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.AT_MOST),
364
+ View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY)
365
+ )
366
+ prefixView.measuredWidth
367
+ } else 0
368
+
369
+ // Measure suffix
370
+ val suffixW = if (suffixView.visibility == View.VISIBLE) {
371
+ suffixView.measure(
372
+ View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.AT_MOST),
373
+ View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY)
374
+ )
375
+ suffixView.measuredWidth
376
+ } else 0
377
+
378
+ val prefixGap = if (prefixView.visibility == View.VISIBLE) ((prefixMarginRight ?: 0.0) * density).toInt() else 0
379
+ val suffixGap = if (suffixView.visibility == View.VISIBLE) ((suffixMarginLeft ?: 0.0) * density).toInt() else 0
380
+
381
+ val inputX = prefixW + prefixGap
382
+ val inputW = maxOf(width - inputX - suffixW - suffixGap, 0)
383
+
384
+ // Layout prefix
385
+ prefixView.layout(0, 0, prefixW, height)
386
+
387
+ // Layout input
388
+ inputView.measure(
389
+ View.MeasureSpec.makeMeasureSpec(inputW, View.MeasureSpec.EXACTLY),
390
+ View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY)
391
+ )
392
+ inputView.layout(inputX, 0, inputX + inputW, height)
393
+
394
+ // Layout suffix
395
+ suffixView.layout(width - suffixW, 0, width, height)
396
+ }
397
+
398
+ // MARK: - Font Size Calculation
399
+
400
+ private fun recalculateFontSize() {
401
+ if (isDisposed || isRecalculating) return
402
+ isRecalculating = true
403
+
404
+ val width = view.width
405
+ val height = view.height
406
+ if (width <= 0 || height <= 0) {
407
+ isRecalculating = false
408
+ return
409
+ }
410
+
411
+ val density = context.resources.displayMetrics.density
412
+ val maxSize = (maxFontSizeProp ?: 48.0).toFloat()
413
+ val minSize = (minFontSizeProp ?: 16.0).toFloat()
414
+
415
+ val prefixText = prefix ?: ""
416
+ val suffixText = suffix ?: ""
417
+ val inputText = inputView.text?.toString() ?: ""
418
+ val displayText = if (inputText.isEmpty()) (placeholder ?: "") else inputText
419
+ val fullText = prefixText + displayText + suffixText
420
+
421
+ if (fullText.isEmpty()) {
422
+ applyFontSize(maxSize)
423
+ return
424
+ }
425
+
426
+ val prefixGap = if (prefixView.visibility == View.VISIBLE) ((prefixMarginRight ?: 0.0) * density) else 0.0
427
+ val suffixGap = if (suffixView.visibility == View.VISIBLE) ((suffixMarginLeft ?: 0.0) * density) else 0.0
428
+ val availableWidth = width - prefixGap.toFloat() - suffixGap.toFloat()
429
+
430
+ val optimalSize = if (multiline == true) {
431
+ val maxLines = (maxNumberOfLines ?: 1.0).toInt()
432
+ findOptimalFontSizeMultiline(fullText, availableWidth, height.toFloat(), maxLines, minSize, maxSize)
433
+ } else {
434
+ findOptimalFontSizeSingleLine(fullText, availableWidth, minSize, maxSize)
435
+ }
436
+
437
+ applyFontSize(optimalSize)
438
+ isRecalculating = false
439
+ }
440
+
441
+ private fun findOptimalFontSizeSingleLine(
442
+ fullText: String,
443
+ availableWidth: Float,
444
+ minSize: Float,
445
+ maxSize: Float
446
+ ): Float {
447
+ var low = minSize
448
+ var high = maxSize
449
+ val paint = TextPaint(Paint.ANTI_ALIAS_FLAG)
450
+
451
+ while (high - low > 0.5f) {
452
+ val mid = (low + high) / 2f
453
+ paint.textSize = mid * context.resources.displayMetrics.scaledDensity
454
+ paint.typeface = makeTypeface()
455
+ val textWidth = paint.measureText(fullText)
456
+ if (textWidth <= availableWidth) {
457
+ low = mid
458
+ } else {
459
+ high = mid
460
+ }
461
+ }
462
+
463
+ return low
464
+ }
465
+
466
+ private fun findOptimalFontSizeMultiline(
467
+ fullText: String,
468
+ availableWidth: Float,
469
+ availableHeight: Float,
470
+ maxLines: Int,
471
+ minSize: Float,
472
+ maxSize: Float
473
+ ): Float {
474
+ var low = minSize
475
+ var high = maxSize
476
+ val paint = TextPaint(Paint.ANTI_ALIAS_FLAG)
477
+
478
+ while (high - low > 0.5f) {
479
+ val mid = (low + high) / 2f
480
+ paint.textSize = mid * context.resources.displayMetrics.scaledDensity
481
+ paint.typeface = makeTypeface()
482
+
483
+ val layout = StaticLayout.Builder
484
+ .obtain(fullText, 0, fullText.length, paint, availableWidth.toInt())
485
+ .build()
486
+
487
+ if (layout.lineCount <= maxLines && layout.height <= availableHeight) {
488
+ low = mid
489
+ } else {
490
+ high = mid
491
+ }
492
+ }
493
+
494
+ return low
495
+ }
496
+
497
+ private fun applyFontSize(size: Float) {
498
+ currentFontSize = size
499
+ val typeface = makeTypeface()
500
+ inputView.setTextSize(TypedValue.COMPLEX_UNIT_SP, size)
501
+ inputView.typeface = typeface
502
+ prefixView.setTextSize(TypedValue.COMPLEX_UNIT_SP, size)
503
+ prefixView.typeface = typeface
504
+ suffixView.setTextSize(TypedValue.COMPLEX_UNIT_SP, size)
505
+ suffixView.typeface = typeface
506
+ view.requestLayout()
507
+ }
508
+
509
+ private fun makeTypeface(): Typeface {
510
+ val style = when (fontWeight) {
511
+ "bold" -> Typeface.BOLD
512
+ else -> Typeface.NORMAL
513
+ }
514
+
515
+ return if (fontFamily != null && fontFamily!!.isNotEmpty()) {
516
+ try {
517
+ Typeface.create(fontFamily, style)
518
+ } catch (e: Exception) {
519
+ Typeface.defaultFromStyle(style)
520
+ }
521
+ } else {
522
+ Typeface.defaultFromStyle(style)
523
+ }
524
+ }
525
+
526
+ // MARK: - Methods
527
+
528
+ override fun focus() {
529
+ if (isDisposed) return
530
+ inputView.requestFocus()
531
+ val imm = context.getSystemService(android.content.Context.INPUT_METHOD_SERVICE) as? android.view.inputmethod.InputMethodManager
532
+ imm?.showSoftInput(inputView, android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT)
533
+ }
534
+
535
+ override fun blur() {
536
+ if (isDisposed) return
537
+ inputView.clearFocus()
538
+ val imm = context.getSystemService(android.content.Context.INPUT_METHOD_SERVICE) as? android.view.inputmethod.InputMethodManager
539
+ imm?.hideSoftInputFromWindow(inputView.windowToken, 0)
540
+ }
541
+
542
+ // MARK: - Helpers
543
+
544
+ private fun parseColor(hex: String?): Int? {
545
+ if (hex.isNullOrEmpty()) return null
546
+ return try {
547
+ Color.parseColor(if (hex.startsWith("#")) hex else "#$hex")
548
+ } catch (e: Exception) {
549
+ null
550
+ }
551
+ }
552
+
553
+ override fun afterUpdate() {
554
+ super.afterUpdate()
555
+ if (!isDisposed) {
556
+ view.requestLayout()
557
+ }
558
+ }
559
+
560
+ override fun dispose() {
561
+ isDisposed = true
562
+ inputView.removeTextChangedListener(textWatcher)
563
+ inputView.onFocusChangeListener = null
564
+ }
565
+ }
@@ -0,0 +1,29 @@
1
+ package com.margelo.nitro.autosizeinput
2
+
3
+ import com.facebook.react.BaseReactPackage
4
+ import com.facebook.react.bridge.NativeModule
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.module.model.ReactModuleInfoProvider
7
+ import com.facebook.react.uimanager.ViewManager
8
+
9
+ import com.margelo.nitro.autosizeinput.views.HybridAutoSizeInputManager
10
+
11
+ class AutoSizeInputPackage : BaseReactPackage() {
12
+ override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
13
+ return null
14
+ }
15
+
16
+ override fun getReactModuleInfoProvider(): ReactModuleInfoProvider {
17
+ return ReactModuleInfoProvider { HashMap() }
18
+ }
19
+
20
+ override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
21
+ return listOf(HybridAutoSizeInputManager())
22
+ }
23
+
24
+ companion object {
25
+ init {
26
+ System.loadLibrary("autosizeinput")
27
+ }
28
+ }
29
+ }