@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,620 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import UIKit
|
|
3
|
+
|
|
4
|
+
class HybridAutoSizeInput: HybridAutoSizeInputSpec {
|
|
5
|
+
|
|
6
|
+
// MARK: - Subviews
|
|
7
|
+
private let prefixLabel = UILabel()
|
|
8
|
+
private let suffixLabel = UILabel()
|
|
9
|
+
private let singleLineInput = UITextField()
|
|
10
|
+
private let multiLineInput = UITextView()
|
|
11
|
+
|
|
12
|
+
// MARK: - State
|
|
13
|
+
private var isUpdatingFromJS = false
|
|
14
|
+
private var isRecalculating = false
|
|
15
|
+
private var currentFontSize: CGFloat = 48
|
|
16
|
+
private var inputDelegate: InputDelegate?
|
|
17
|
+
|
|
18
|
+
// MARK: - HybridView
|
|
19
|
+
var view: UIView = UIView()
|
|
20
|
+
|
|
21
|
+
// MARK: - Props
|
|
22
|
+
var text: String? {
|
|
23
|
+
didSet {
|
|
24
|
+
guard !isUpdatingFromJS else { return }
|
|
25
|
+
isUpdatingFromJS = true
|
|
26
|
+
if multiline == true {
|
|
27
|
+
multiLineInput.text = text ?? ""
|
|
28
|
+
} else {
|
|
29
|
+
singleLineInput.text = text ?? ""
|
|
30
|
+
}
|
|
31
|
+
isUpdatingFromJS = false
|
|
32
|
+
recalculateFontSize()
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
var prefix: String? {
|
|
37
|
+
didSet {
|
|
38
|
+
prefixLabel.text = prefix
|
|
39
|
+
prefixLabel.isHidden = (prefix ?? "").isEmpty
|
|
40
|
+
recalculateFontSize()
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
var suffix: String? {
|
|
45
|
+
didSet {
|
|
46
|
+
suffixLabel.text = suffix
|
|
47
|
+
suffixLabel.isHidden = (suffix ?? "").isEmpty
|
|
48
|
+
recalculateFontSize()
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
var placeholder: String? {
|
|
53
|
+
didSet {
|
|
54
|
+
updatePlaceholder()
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
var fontSize: Double? {
|
|
59
|
+
didSet {
|
|
60
|
+
recalculateFontSize()
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
var minFontSize: Double? {
|
|
65
|
+
didSet {
|
|
66
|
+
recalculateFontSize()
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
var multiline: Bool? {
|
|
71
|
+
didSet {
|
|
72
|
+
updateInputMode()
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
var maxNumberOfLines: Double? {
|
|
77
|
+
didSet {
|
|
78
|
+
recalculateFontSize()
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
var textColor: String? {
|
|
83
|
+
didSet {
|
|
84
|
+
let color = colorFromHex(textColor) ?? .label
|
|
85
|
+
singleLineInput.textColor = color
|
|
86
|
+
multiLineInput.textColor = color
|
|
87
|
+
// Update prefix/suffix if they don't have custom colors
|
|
88
|
+
if prefixColor == nil { prefixLabel.textColor = color }
|
|
89
|
+
if suffixColor == nil { suffixLabel.textColor = color }
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
var prefixColor: String? {
|
|
94
|
+
didSet {
|
|
95
|
+
prefixLabel.textColor = colorFromHex(prefixColor) ?? colorFromHex(textColor) ?? .label
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
var suffixColor: String? {
|
|
100
|
+
didSet {
|
|
101
|
+
suffixLabel.textColor = colorFromHex(suffixColor) ?? colorFromHex(textColor) ?? .label
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
var placeholderColor: String? {
|
|
106
|
+
didSet {
|
|
107
|
+
updatePlaceholder()
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
var textAlign: String? {
|
|
112
|
+
didSet {
|
|
113
|
+
let alignment = textAlignmentFrom(textAlign)
|
|
114
|
+
singleLineInput.textAlignment = alignment
|
|
115
|
+
multiLineInput.textAlignment = alignment
|
|
116
|
+
prefixLabel.textAlignment = .left
|
|
117
|
+
suffixLabel.textAlignment = .right
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
var fontFamily: String? {
|
|
122
|
+
didSet {
|
|
123
|
+
recalculateFontSize()
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
var fontWeight: String? {
|
|
128
|
+
didSet {
|
|
129
|
+
recalculateFontSize()
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
var editable: Bool? {
|
|
134
|
+
didSet {
|
|
135
|
+
let isEditable = editable ?? true
|
|
136
|
+
singleLineInput.isEnabled = isEditable
|
|
137
|
+
multiLineInput.isEditable = isEditable
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
var keyboardType: String? {
|
|
142
|
+
didSet {
|
|
143
|
+
let kt = keyboardTypeFrom(keyboardType)
|
|
144
|
+
singleLineInput.keyboardType = kt
|
|
145
|
+
multiLineInput.keyboardType = kt
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
var returnKeyType: String? {
|
|
150
|
+
didSet {
|
|
151
|
+
let rkt = returnKeyTypeFrom(returnKeyType)
|
|
152
|
+
singleLineInput.returnKeyType = rkt
|
|
153
|
+
multiLineInput.returnKeyType = rkt
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
var autoCorrect: Bool? {
|
|
158
|
+
didSet {
|
|
159
|
+
let type: UITextAutocorrectionType = (autoCorrect == true) ? .yes : .no
|
|
160
|
+
singleLineInput.autocorrectionType = type
|
|
161
|
+
multiLineInput.autocorrectionType = type
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
var autoCapitalize: String? {
|
|
166
|
+
didSet {
|
|
167
|
+
let type = autoCapitalizeTypeFrom(autoCapitalize)
|
|
168
|
+
singleLineInput.autocapitalizationType = type
|
|
169
|
+
multiLineInput.autocapitalizationType = type
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
var selectionColor: String? {
|
|
174
|
+
didSet {
|
|
175
|
+
let color = colorFromHex(selectionColor) ?? .tintColor
|
|
176
|
+
singleLineInput.tintColor = color
|
|
177
|
+
multiLineInput.tintColor = color
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
var prefixMarginRight: Double? {
|
|
182
|
+
didSet {
|
|
183
|
+
view.setNeedsLayout()
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
var suffixMarginLeft: Double? {
|
|
188
|
+
didSet {
|
|
189
|
+
view.setNeedsLayout()
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
var onChangeText: ((String) -> Void)?
|
|
194
|
+
var onFocus: (() -> Void)?
|
|
195
|
+
var onBlur: (() -> Void)?
|
|
196
|
+
|
|
197
|
+
// MARK: - Init
|
|
198
|
+
|
|
199
|
+
override init() {
|
|
200
|
+
super.init()
|
|
201
|
+
setupView()
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
private func setupView() {
|
|
205
|
+
view.clipsToBounds = true
|
|
206
|
+
|
|
207
|
+
// Configure prefix label
|
|
208
|
+
prefixLabel.isHidden = true
|
|
209
|
+
prefixLabel.setContentHuggingPriority(.required, for: .horizontal)
|
|
210
|
+
prefixLabel.setContentCompressionResistancePriority(.required, for: .horizontal)
|
|
211
|
+
|
|
212
|
+
// Configure suffix label
|
|
213
|
+
suffixLabel.isHidden = true
|
|
214
|
+
suffixLabel.setContentHuggingPriority(.required, for: .horizontal)
|
|
215
|
+
suffixLabel.setContentCompressionResistancePriority(.required, for: .horizontal)
|
|
216
|
+
|
|
217
|
+
// Configure delegate helper
|
|
218
|
+
let delegate = InputDelegate(owner: self)
|
|
219
|
+
self.inputDelegate = delegate
|
|
220
|
+
|
|
221
|
+
// Configure single-line input
|
|
222
|
+
singleLineInput.borderStyle = .none
|
|
223
|
+
singleLineInput.delegate = delegate
|
|
224
|
+
singleLineInput.addTarget(delegate, action: #selector(InputDelegate.textFieldDidChange(_:)), for: .editingChanged)
|
|
225
|
+
|
|
226
|
+
// Configure multi-line input
|
|
227
|
+
multiLineInput.delegate = delegate
|
|
228
|
+
multiLineInput.textContainerInset = .zero
|
|
229
|
+
multiLineInput.textContainer.lineFragmentPadding = 0
|
|
230
|
+
multiLineInput.backgroundColor = .clear
|
|
231
|
+
multiLineInput.isScrollEnabled = false
|
|
232
|
+
multiLineInput.isHidden = true
|
|
233
|
+
|
|
234
|
+
view.addSubview(prefixLabel)
|
|
235
|
+
view.addSubview(singleLineInput)
|
|
236
|
+
view.addSubview(multiLineInput)
|
|
237
|
+
view.addSubview(suffixLabel)
|
|
238
|
+
|
|
239
|
+
// Override layoutSubviews
|
|
240
|
+
let layoutView = LayoutView()
|
|
241
|
+
layoutView.layoutCallback = { [weak self] in
|
|
242
|
+
self?.performLayout()
|
|
243
|
+
}
|
|
244
|
+
// Replace view with our layout-aware view
|
|
245
|
+
let oldView = view
|
|
246
|
+
view = layoutView
|
|
247
|
+
view.clipsToBounds = true
|
|
248
|
+
for subview in oldView.subviews {
|
|
249
|
+
subview.removeFromSuperview()
|
|
250
|
+
view.addSubview(subview)
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
private func performLayout() {
|
|
255
|
+
let bounds = view.bounds
|
|
256
|
+
guard bounds.width > 0 && bounds.height > 0 else { return }
|
|
257
|
+
|
|
258
|
+
// First, calculate font size (this updates prefix/suffix fonts too)
|
|
259
|
+
recalculateFontSize()
|
|
260
|
+
|
|
261
|
+
// Now measure prefix/suffix with the correct font applied
|
|
262
|
+
let prefixW: CGFloat
|
|
263
|
+
if prefixLabel.isHidden {
|
|
264
|
+
prefixW = 0
|
|
265
|
+
} else {
|
|
266
|
+
prefixW = prefixLabel.sizeThatFits(bounds.size).width
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
let suffixW: CGFloat
|
|
270
|
+
if suffixLabel.isHidden {
|
|
271
|
+
suffixW = 0
|
|
272
|
+
} else {
|
|
273
|
+
suffixW = suffixLabel.sizeThatFits(bounds.size).width
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
let prefixGap = prefixLabel.isHidden ? 0 : CGFloat(prefixMarginRight ?? 0)
|
|
277
|
+
let suffixGap = suffixLabel.isHidden ? 0 : CGFloat(suffixMarginLeft ?? 0)
|
|
278
|
+
|
|
279
|
+
let inputX = prefixW + prefixGap
|
|
280
|
+
let inputW = bounds.width - inputX - suffixW - suffixGap
|
|
281
|
+
|
|
282
|
+
prefixLabel.frame = CGRect(x: 0, y: 0, width: prefixW, height: bounds.height)
|
|
283
|
+
suffixLabel.frame = CGRect(x: bounds.width - suffixW, y: 0, width: suffixW, height: bounds.height)
|
|
284
|
+
|
|
285
|
+
let activeInput: UIView = (multiline == true) ? multiLineInput : singleLineInput
|
|
286
|
+
activeInput.frame = CGRect(x: inputX, y: 0, width: max(inputW, 0), height: bounds.height)
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
private func updateInputMode() {
|
|
290
|
+
let isMulti = multiline == true
|
|
291
|
+
singleLineInput.isHidden = isMulti
|
|
292
|
+
multiLineInput.isHidden = !isMulti
|
|
293
|
+
|
|
294
|
+
// Transfer text between inputs
|
|
295
|
+
if isMulti {
|
|
296
|
+
multiLineInput.text = singleLineInput.text
|
|
297
|
+
} else {
|
|
298
|
+
singleLineInput.text = multiLineInput.text
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
view.setNeedsLayout()
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// MARK: - Font Size Calculation
|
|
305
|
+
|
|
306
|
+
private func recalculateFontSize() {
|
|
307
|
+
guard !isRecalculating else { return }
|
|
308
|
+
let bounds = view.bounds
|
|
309
|
+
guard bounds.width > 0 && bounds.height > 0 else { return }
|
|
310
|
+
isRecalculating = true
|
|
311
|
+
defer { isRecalculating = false }
|
|
312
|
+
|
|
313
|
+
let maxSize = CGFloat(fontSize ?? 48)
|
|
314
|
+
let minSize = CGFloat(minFontSize ?? 16)
|
|
315
|
+
|
|
316
|
+
let prefixText = prefix ?? ""
|
|
317
|
+
let suffixText = suffix ?? ""
|
|
318
|
+
let inputText: String
|
|
319
|
+
if multiline == true {
|
|
320
|
+
inputText = multiLineInput.text ?? ""
|
|
321
|
+
} else {
|
|
322
|
+
inputText = singleLineInput.text ?? ""
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// If no text, use max font size
|
|
326
|
+
let displayText = inputText.isEmpty ? (placeholder ?? "") : inputText
|
|
327
|
+
let fullText = prefixText + displayText + suffixText
|
|
328
|
+
|
|
329
|
+
guard !fullText.isEmpty else {
|
|
330
|
+
applyFontSize(maxSize)
|
|
331
|
+
return
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
let prefixGap = prefixLabel.isHidden ? 0 : CGFloat(prefixMarginRight ?? 0)
|
|
335
|
+
let suffixGap = suffixLabel.isHidden ? 0 : CGFloat(suffixMarginLeft ?? 0)
|
|
336
|
+
let totalGap = prefixGap + suffixGap
|
|
337
|
+
let availableWidth = bounds.width - totalGap
|
|
338
|
+
|
|
339
|
+
let optimalSize: CGFloat
|
|
340
|
+
if multiline == true {
|
|
341
|
+
let maxLines = Int(maxNumberOfLines ?? 1)
|
|
342
|
+
optimalSize = findOptimalFontSizeMultiline(
|
|
343
|
+
fullText: fullText,
|
|
344
|
+
availableWidth: availableWidth,
|
|
345
|
+
availableHeight: bounds.height,
|
|
346
|
+
maxLines: maxLines,
|
|
347
|
+
minSize: minSize,
|
|
348
|
+
maxSize: maxSize
|
|
349
|
+
)
|
|
350
|
+
} else {
|
|
351
|
+
optimalSize = findOptimalFontSizeSingleLine(
|
|
352
|
+
fullText: fullText,
|
|
353
|
+
availableWidth: availableWidth,
|
|
354
|
+
minSize: minSize,
|
|
355
|
+
maxSize: maxSize
|
|
356
|
+
)
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
applyFontSize(optimalSize)
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
private func findOptimalFontSizeSingleLine(
|
|
363
|
+
fullText: String,
|
|
364
|
+
availableWidth: CGFloat,
|
|
365
|
+
minSize: CGFloat,
|
|
366
|
+
maxSize: CGFloat
|
|
367
|
+
) -> CGFloat {
|
|
368
|
+
var low = minSize
|
|
369
|
+
var high = maxSize
|
|
370
|
+
|
|
371
|
+
while high - low > 0.5 {
|
|
372
|
+
let mid = (low + high) / 2
|
|
373
|
+
let font = makeFont(size: mid)
|
|
374
|
+
let textWidth = (fullText as NSString).size(withAttributes: [.font: font]).width
|
|
375
|
+
if textWidth <= availableWidth {
|
|
376
|
+
low = mid
|
|
377
|
+
} else {
|
|
378
|
+
high = mid
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
return low
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
private func findOptimalFontSizeMultiline(
|
|
386
|
+
fullText: String,
|
|
387
|
+
availableWidth: CGFloat,
|
|
388
|
+
availableHeight: CGFloat,
|
|
389
|
+
maxLines: Int,
|
|
390
|
+
minSize: CGFloat,
|
|
391
|
+
maxSize: CGFloat
|
|
392
|
+
) -> CGFloat {
|
|
393
|
+
var low = minSize
|
|
394
|
+
var high = maxSize
|
|
395
|
+
|
|
396
|
+
while high - low > 0.5 {
|
|
397
|
+
let mid = (low + high) / 2
|
|
398
|
+
let font = makeFont(size: mid)
|
|
399
|
+
let constraintSize = CGSize(width: availableWidth, height: .greatestFiniteMagnitude)
|
|
400
|
+
let boundingRect = (fullText as NSString).boundingRect(
|
|
401
|
+
with: constraintSize,
|
|
402
|
+
options: [.usesLineFragmentOrigin, .usesFontLeading],
|
|
403
|
+
attributes: [.font: font],
|
|
404
|
+
context: nil
|
|
405
|
+
)
|
|
406
|
+
let lineHeight = font.lineHeight
|
|
407
|
+
let lineCount = max(1, Int(ceil(boundingRect.height / lineHeight)))
|
|
408
|
+
|
|
409
|
+
if lineCount <= maxLines && boundingRect.height <= availableHeight {
|
|
410
|
+
low = mid
|
|
411
|
+
} else {
|
|
412
|
+
high = mid
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
return low
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
private func applyFontSize(_ size: CGFloat) {
|
|
420
|
+
guard abs(currentFontSize - size) > 0.25 else { return }
|
|
421
|
+
currentFontSize = size
|
|
422
|
+
let font = makeFont(size: size)
|
|
423
|
+
singleLineInput.font = font
|
|
424
|
+
multiLineInput.font = font
|
|
425
|
+
prefixLabel.font = font
|
|
426
|
+
suffixLabel.font = font
|
|
427
|
+
updatePlaceholder()
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
private func makeFont(size: CGFloat) -> UIFont {
|
|
431
|
+
let weight = fontWeightFrom(fontWeight)
|
|
432
|
+
|
|
433
|
+
if let family = fontFamily, !family.isEmpty {
|
|
434
|
+
if let font = UIFont(name: family, size: size) {
|
|
435
|
+
return font
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
return UIFont.systemFont(ofSize: size, weight: weight)
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
private func updatePlaceholder() {
|
|
443
|
+
let phText = placeholder ?? ""
|
|
444
|
+
let phColor = colorFromHex(placeholderColor) ?? .placeholderText
|
|
445
|
+
let font = makeFont(size: currentFontSize)
|
|
446
|
+
singleLineInput.attributedPlaceholder = NSAttributedString(
|
|
447
|
+
string: phText,
|
|
448
|
+
attributes: [.foregroundColor: phColor, .font: font]
|
|
449
|
+
)
|
|
450
|
+
// For multiline, placeholder would need custom handling
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// MARK: - Methods
|
|
454
|
+
|
|
455
|
+
func focus() throws {
|
|
456
|
+
if multiline == true {
|
|
457
|
+
multiLineInput.becomeFirstResponder()
|
|
458
|
+
} else {
|
|
459
|
+
singleLineInput.becomeFirstResponder()
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
func blur() throws {
|
|
464
|
+
if multiline == true {
|
|
465
|
+
multiLineInput.resignFirstResponder()
|
|
466
|
+
} else {
|
|
467
|
+
singleLineInput.resignFirstResponder()
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// MARK: - Delegate callbacks (called by InputDelegate)
|
|
472
|
+
|
|
473
|
+
fileprivate func handleTextFieldChange(_ textField: UITextField) {
|
|
474
|
+
guard !isUpdatingFromJS else { return }
|
|
475
|
+
recalculateFontSize()
|
|
476
|
+
onChangeText?(textField.text ?? "")
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
fileprivate func handleTextViewChange(_ textView: UITextView) {
|
|
480
|
+
guard !isUpdatingFromJS else { return }
|
|
481
|
+
recalculateFontSize()
|
|
482
|
+
onChangeText?(textView.text ?? "")
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
fileprivate func handleFocus() {
|
|
486
|
+
onFocus?()
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
fileprivate func handleBlur() {
|
|
490
|
+
onBlur?()
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// MARK: - Helpers
|
|
494
|
+
|
|
495
|
+
private func colorFromHex(_ hex: String?) -> UIColor? {
|
|
496
|
+
guard let hex = hex, !hex.isEmpty else { return nil }
|
|
497
|
+
var sanitized = hex.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
498
|
+
if sanitized.hasPrefix("#") {
|
|
499
|
+
sanitized.remove(at: sanitized.startIndex)
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
var color: UInt64 = 0
|
|
503
|
+
Scanner(string: sanitized).scanHexInt64(&color)
|
|
504
|
+
|
|
505
|
+
if sanitized.count == 8 {
|
|
506
|
+
// RRGGBBAA
|
|
507
|
+
let r = CGFloat((color >> 24) & 0xFF) / 255.0
|
|
508
|
+
let g = CGFloat((color >> 16) & 0xFF) / 255.0
|
|
509
|
+
let b = CGFloat((color >> 8) & 0xFF) / 255.0
|
|
510
|
+
let a = CGFloat(color & 0xFF) / 255.0
|
|
511
|
+
return UIColor(red: r, green: g, blue: b, alpha: a)
|
|
512
|
+
} else {
|
|
513
|
+
// RRGGBB
|
|
514
|
+
let r = CGFloat((color >> 16) & 0xFF) / 255.0
|
|
515
|
+
let g = CGFloat((color >> 8) & 0xFF) / 255.0
|
|
516
|
+
let b = CGFloat(color & 0xFF) / 255.0
|
|
517
|
+
return UIColor(red: r, green: g, blue: b, alpha: 1.0)
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
private func textAlignmentFrom(_ align: String?) -> NSTextAlignment {
|
|
522
|
+
switch align {
|
|
523
|
+
case "center": return .center
|
|
524
|
+
case "right": return .right
|
|
525
|
+
default: return .left
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
private func keyboardTypeFrom(_ type: String?) -> UIKeyboardType {
|
|
530
|
+
switch type {
|
|
531
|
+
case "numberPad": return .numberPad
|
|
532
|
+
case "decimalPad": return .decimalPad
|
|
533
|
+
case "emailAddress": return .emailAddress
|
|
534
|
+
case "phonePad": return .phonePad
|
|
535
|
+
case "url": return .URL
|
|
536
|
+
default: return .default
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
private func autoCapitalizeTypeFrom(_ type: String?) -> UITextAutocapitalizationType {
|
|
541
|
+
switch type {
|
|
542
|
+
case "characters": return .allCharacters
|
|
543
|
+
case "words": return .words
|
|
544
|
+
case "sentences": return .sentences
|
|
545
|
+
case "none": return .none
|
|
546
|
+
default: return .none
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
private func returnKeyTypeFrom(_ type: String?) -> UIReturnKeyType {
|
|
551
|
+
switch type {
|
|
552
|
+
case "done": return .done
|
|
553
|
+
case "go": return .go
|
|
554
|
+
case "next": return .next
|
|
555
|
+
case "search": return .search
|
|
556
|
+
case "send": return .send
|
|
557
|
+
default: return .default
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
private func fontWeightFrom(_ weight: String?) -> UIFont.Weight {
|
|
562
|
+
switch weight {
|
|
563
|
+
case "bold": return .bold
|
|
564
|
+
case "semibold": return .semibold
|
|
565
|
+
case "medium": return .medium
|
|
566
|
+
case "light": return .light
|
|
567
|
+
case "thin": return .thin
|
|
568
|
+
case "ultraLight": return .ultraLight
|
|
569
|
+
case "heavy": return .heavy
|
|
570
|
+
case "black": return .black
|
|
571
|
+
default: return .regular
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// MARK: - NSObject-based delegate for UITextField/UITextView
|
|
577
|
+
|
|
578
|
+
private class InputDelegate: NSObject, UITextFieldDelegate, UITextViewDelegate {
|
|
579
|
+
private weak var owner: HybridAutoSizeInput?
|
|
580
|
+
|
|
581
|
+
init(owner: HybridAutoSizeInput) {
|
|
582
|
+
self.owner = owner
|
|
583
|
+
super.init()
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
@objc func textFieldDidChange(_ textField: UITextField) {
|
|
587
|
+
owner?.handleTextFieldChange(textField)
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
func textFieldDidBeginEditing(_ textField: UITextField) {
|
|
591
|
+
owner?.handleFocus()
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
func textFieldDidEndEditing(_ textField: UITextField) {
|
|
595
|
+
owner?.handleBlur()
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
func textViewDidChange(_ textView: UITextView) {
|
|
599
|
+
owner?.handleTextViewChange(textView)
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
func textViewDidBeginEditing(_ textView: UITextView) {
|
|
603
|
+
owner?.handleFocus()
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
func textViewDidEndEditing(_ textView: UITextView) {
|
|
607
|
+
owner?.handleBlur()
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
// MARK: - Layout-aware UIView subclass
|
|
612
|
+
|
|
613
|
+
private class LayoutView: UIView {
|
|
614
|
+
var layoutCallback: (() -> Void)?
|
|
615
|
+
|
|
616
|
+
override func layoutSubviews() {
|
|
617
|
+
super.layoutSubviews()
|
|
618
|
+
layoutCallback?()
|
|
619
|
+
}
|
|
620
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":[],"sourceRoot":"../../src","sources":["AutoSizeInput.nitro.ts"],"mappings":"","ignoreList":[]}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { getHostComponent } from 'react-native-nitro-modules';
|
|
4
|
+
const AutoSizeInputConfig = require('../nitrogen/generated/shared/json/AutoSizeInputConfig.json');
|
|
5
|
+
export const AutoSizeInputView = getHostComponent('AutoSizeInput', () => AutoSizeInputConfig);
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["getHostComponent","AutoSizeInputConfig","require","AutoSizeInputView"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,SAASA,gBAAgB,QAAQ,4BAA4B;AAC7D,MAAMC,mBAAmB,GAAGC,OAAO,CAAC,4DAA4D,CAAC;AAQjG,OAAO,MAAMC,iBAAiB,GAAGH,gBAAgB,CAG/C,eAAe,EAAE,MAAMC,mBAAmB,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"module"}
|