@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,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,4 @@
1
+ "use strict";
2
+
3
+ export {};
4
+ //# sourceMappingURL=AutoSizeInput.nitro.js.map
@@ -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"}