@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.
- package/AutoSizeInput.podspec +29 -0
- package/android/CMakeLists.txt +24 -0
- package/android/build.gradle +128 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/cpp/cpp-adapter.cpp +6 -0
- package/android/src/main/java/com/margelo/nitro/autosizeinput/AutoSizeInput.kt +565 -0
- package/android/src/main/java/com/margelo/nitro/autosizeinput/AutoSizeInputPackage.kt +29 -0
- package/ios/AutoSizeInput.swift +620 -0
- package/lib/module/AutoSizeInput.nitro.js +4 -0
- package/lib/module/AutoSizeInput.nitro.js.map +1 -0
- package/lib/module/index.js +6 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/nitrogen/generated/android/autosizeinput+autolinking.cmake +83 -0
- package/lib/nitrogen/generated/android/autosizeinput+autolinking.gradle +27 -0
- package/lib/nitrogen/generated/android/autosizeinputOnLoad.cpp +50 -0
- package/lib/nitrogen/generated/android/autosizeinputOnLoad.hpp +25 -0
- package/lib/nitrogen/generated/android/c++/JFunc_void.hpp +75 -0
- package/lib/nitrogen/generated/android/c++/JFunc_void_std__string.hpp +76 -0
- package/lib/nitrogen/generated/android/c++/JHybridAutoSizeInputSpec.cpp +317 -0
- package/lib/nitrogen/generated/android/c++/JHybridAutoSizeInputSpec.hpp +117 -0
- package/lib/nitrogen/generated/android/c++/views/JHybridAutoSizeInputStateUpdater.cpp +156 -0
- package/lib/nitrogen/generated/android/c++/views/JHybridAutoSizeInputStateUpdater.hpp +49 -0
- package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/autosizeinput/Func_void.kt +80 -0
- package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/autosizeinput/Func_void_std__string.kt +80 -0
- package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/autosizeinput/HybridAutoSizeInputSpec.kt +239 -0
- package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/autosizeinput/autosizeinputOnLoad.kt +35 -0
- package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/autosizeinput/views/HybridAutoSizeInputManager.kt +50 -0
- package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/autosizeinput/views/HybridAutoSizeInputStateUpdater.kt +23 -0
- package/lib/nitrogen/generated/ios/AutoSizeInput+autolinking.rb +60 -0
- package/lib/nitrogen/generated/ios/AutoSizeInput-Swift-Cxx-Bridge.cpp +49 -0
- package/lib/nitrogen/generated/ios/AutoSizeInput-Swift-Cxx-Bridge.hpp +173 -0
- package/lib/nitrogen/generated/ios/AutoSizeInput-Swift-Cxx-Umbrella.hpp +46 -0
- package/lib/nitrogen/generated/ios/AutoSizeInputAutolinking.mm +33 -0
- package/lib/nitrogen/generated/ios/AutoSizeInputAutolinking.swift +25 -0
- package/lib/nitrogen/generated/ios/c++/HybridAutoSizeInputSpecSwift.cpp +11 -0
- package/lib/nitrogen/generated/ios/c++/HybridAutoSizeInputSpecSwift.hpp +263 -0
- package/lib/nitrogen/generated/ios/c++/views/HybridAutoSizeInputComponent.mm +221 -0
- package/lib/nitrogen/generated/ios/swift/Func_void.swift +47 -0
- package/lib/nitrogen/generated/ios/swift/Func_void_std__string.swift +47 -0
- package/lib/nitrogen/generated/ios/swift/HybridAutoSizeInputSpec.swift +82 -0
- package/lib/nitrogen/generated/ios/swift/HybridAutoSizeInputSpec_cxx.swift +764 -0
- package/lib/nitrogen/generated/shared/c++/HybridAutoSizeInputSpec.cpp +74 -0
- package/lib/nitrogen/generated/shared/c++/HybridAutoSizeInputSpec.hpp +116 -0
- package/lib/nitrogen/generated/shared/c++/views/HybridAutoSizeInputComponent.cpp +387 -0
- package/lib/nitrogen/generated/shared/c++/views/HybridAutoSizeInputComponent.hpp +133 -0
- package/lib/nitrogen/generated/shared/json/AutoSizeInputConfig.json +35 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/AutoSizeInput.nitro.d.ts +35 -0
- package/lib/typescript/src/AutoSizeInput.nitro.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +4 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/nitro.json +17 -0
- package/nitrogen/generated/android/autosizeinput+autolinking.cmake +83 -0
- package/nitrogen/generated/android/autosizeinput+autolinking.gradle +27 -0
- package/nitrogen/generated/android/autosizeinputOnLoad.cpp +50 -0
- package/nitrogen/generated/android/autosizeinputOnLoad.hpp +25 -0
- package/nitrogen/generated/android/c++/JFunc_void.hpp +75 -0
- package/nitrogen/generated/android/c++/JFunc_void_std__string.hpp +76 -0
- package/nitrogen/generated/android/c++/JHybridAutoSizeInputSpec.cpp +317 -0
- package/nitrogen/generated/android/c++/JHybridAutoSizeInputSpec.hpp +117 -0
- package/nitrogen/generated/android/c++/views/JHybridAutoSizeInputStateUpdater.cpp +156 -0
- package/nitrogen/generated/android/c++/views/JHybridAutoSizeInputStateUpdater.hpp +49 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/autosizeinput/Func_void.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/autosizeinput/Func_void_std__string.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/autosizeinput/HybridAutoSizeInputSpec.kt +239 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/autosizeinput/autosizeinputOnLoad.kt +35 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/autosizeinput/views/HybridAutoSizeInputManager.kt +50 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/autosizeinput/views/HybridAutoSizeInputStateUpdater.kt +23 -0
- package/nitrogen/generated/ios/AutoSizeInput+autolinking.rb +60 -0
- package/nitrogen/generated/ios/AutoSizeInput-Swift-Cxx-Bridge.cpp +49 -0
- package/nitrogen/generated/ios/AutoSizeInput-Swift-Cxx-Bridge.hpp +173 -0
- package/nitrogen/generated/ios/AutoSizeInput-Swift-Cxx-Umbrella.hpp +46 -0
- package/nitrogen/generated/ios/AutoSizeInputAutolinking.mm +33 -0
- package/nitrogen/generated/ios/AutoSizeInputAutolinking.swift +25 -0
- package/nitrogen/generated/ios/c++/HybridAutoSizeInputSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridAutoSizeInputSpecSwift.hpp +263 -0
- package/nitrogen/generated/ios/c++/views/HybridAutoSizeInputComponent.mm +221 -0
- package/nitrogen/generated/ios/swift/Func_void.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_std__string.swift +47 -0
- package/nitrogen/generated/ios/swift/HybridAutoSizeInputSpec.swift +82 -0
- package/nitrogen/generated/ios/swift/HybridAutoSizeInputSpec_cxx.swift +764 -0
- package/nitrogen/generated/shared/c++/HybridAutoSizeInputSpec.cpp +74 -0
- package/nitrogen/generated/shared/c++/HybridAutoSizeInputSpec.hpp +116 -0
- package/nitrogen/generated/shared/c++/views/HybridAutoSizeInputComponent.cpp +387 -0
- package/nitrogen/generated/shared/c++/views/HybridAutoSizeInputComponent.hpp +133 -0
- package/nitrogen/generated/shared/json/AutoSizeInputConfig.json +35 -0
- package/package.json +170 -0
- package/src/AutoSizeInput.nitro.ts +56 -0
- 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
|
+
}
|