@thelacanians/vue-native-cli 0.4.2 → 0.4.4

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 (172) hide show
  1. package/dist/cli.js +43 -23
  2. package/native/android/README.md +205 -0
  3. package/native/android/VueNativeCore/build.gradle.kts +100 -0
  4. package/native/android/VueNativeCore/consumer-rules.pro +12 -0
  5. package/native/android/VueNativeCore/proguard-rules.pro +33 -0
  6. package/native/android/VueNativeCore/src/main/AndroidManifest.xml +17 -0
  7. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Bridge/ErrorOverlayView.kt +94 -0
  8. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Bridge/HotReloadManager.kt +105 -0
  9. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Bridge/JSPolyfills.kt +652 -0
  10. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Bridge/JSRuntime.kt +207 -0
  11. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Bridge/NativeBridge.kt +417 -0
  12. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/ComponentRegistry.kt +76 -0
  13. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VActionSheetFactory.kt +78 -0
  14. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VActivityIndicatorFactory.kt +46 -0
  15. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VAlertDialogFactory.kt +84 -0
  16. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VButtonFactory.kt +73 -0
  17. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VCheckboxFactory.kt +93 -0
  18. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VDropdownFactory.kt +125 -0
  19. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VImageFactory.kt +75 -0
  20. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VInputFactory.kt +210 -0
  21. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VKeyboardAvoidingFactory.kt +31 -0
  22. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VListFactory.kt +183 -0
  23. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VModalFactory.kt +105 -0
  24. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VPickerFactory.kt +57 -0
  25. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VPressableFactory.kt +109 -0
  26. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VProgressBarFactory.kt +43 -0
  27. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VRadioFactory.kt +103 -0
  28. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VRefreshControlFactory.kt +73 -0
  29. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VRootFactory.kt +39 -0
  30. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VSafeAreaFactory.kt +48 -0
  31. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VScrollViewFactory.kt +105 -0
  32. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VSectionListFactory.kt +144 -0
  33. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VSegmentedControlFactory.kt +77 -0
  34. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VSliderFactory.kt +74 -0
  35. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VStatusBarFactory.kt +52 -0
  36. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VSwitchFactory.kt +62 -0
  37. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VTextFactory.kt +53 -0
  38. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VVideoFactory.kt +191 -0
  39. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VViewFactory.kt +48 -0
  40. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VWebViewFactory.kt +90 -0
  41. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/NativeComponentFactory.kt +40 -0
  42. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/VTextNodeView.kt +23 -0
  43. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Helpers/GestureHelper.kt +16 -0
  44. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Helpers/TouchableView.kt +105 -0
  45. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/AnimationModule.kt +292 -0
  46. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/AppStateModule.kt +41 -0
  47. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/AsyncStorageModule.kt +59 -0
  48. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/AudioModule.kt +331 -0
  49. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/BackgroundTaskModule.kt +166 -0
  50. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/BiometryModule.kt +56 -0
  51. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/BluetoothModule.kt +302 -0
  52. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/CalendarModule.kt +198 -0
  53. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/CameraModule.kt +64 -0
  54. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/ClipboardModule.kt +36 -0
  55. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/ContactsModule.kt +288 -0
  56. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/DatabaseModule.kt +229 -0
  57. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/DeviceInfoModule.kt +39 -0
  58. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/FileSystemModule.kt +193 -0
  59. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/GeolocationModule.kt +68 -0
  60. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/HapticsModule.kt +61 -0
  61. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/HttpModule.kt +111 -0
  62. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/IAPModule.kt +302 -0
  63. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/KeyboardModule.kt +26 -0
  64. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/LinkingModule.kt +43 -0
  65. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/NativeModule.kt +27 -0
  66. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/NativeModuleRegistry.kt +92 -0
  67. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/NetworkModule.kt +75 -0
  68. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/NotificationsModule.kt +181 -0
  69. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/OTAModule.kt +255 -0
  70. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/PerformanceModule.kt +147 -0
  71. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/PermissionsModule.kt +126 -0
  72. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/SecureStorageModule.kt +51 -0
  73. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/SensorsModule.kt +134 -0
  74. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/ShareModule.kt +36 -0
  75. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/SocialAuthModule.kt +160 -0
  76. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/WebSocketModule.kt +155 -0
  77. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Styling/StyleEngine.kt +802 -0
  78. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Tags.kt +43 -0
  79. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/VueNativeActivity.kt +169 -0
  80. package/native/android/VueNativeCore/src/main/res/values/ids.xml +8 -0
  81. package/native/android/app/build.gradle.kts +45 -0
  82. package/native/android/app/proguard-rules.pro +5 -0
  83. package/native/android/app/src/main/AndroidManifest.xml +25 -0
  84. package/native/android/app/src/main/assets/.gitkeep +0 -0
  85. package/native/android/app/src/main/kotlin/com/vuenative/example/counter/MainActivity.kt +14 -0
  86. package/native/android/app/src/main/res/layout/activity_main.xml +6 -0
  87. package/native/android/app/src/main/res/values/strings.xml +3 -0
  88. package/native/android/app/src/main/res/values/themes.xml +9 -0
  89. package/native/android/app/src/main/res/xml/network_security_config.xml +8 -0
  90. package/native/android/build.gradle.kts +6 -0
  91. package/native/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  92. package/native/android/gradle/wrapper/gradle-wrapper.properties +7 -0
  93. package/native/android/gradle.properties +4 -0
  94. package/native/android/gradlew +87 -0
  95. package/native/android/gradlew.bat +48 -0
  96. package/native/android/settings.gradle.kts +20 -0
  97. package/native/ios/VueNativeCore/Package.resolved +23 -0
  98. package/native/ios/VueNativeCore/Package.swift +32 -0
  99. package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/CertificatePinning.swift +132 -0
  100. package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/ErrorOverlayView.swift +92 -0
  101. package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/HotReloadManager.swift +147 -0
  102. package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/JSPolyfills.swift +711 -0
  103. package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/JSRuntime.swift +421 -0
  104. package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/NativeBridge.swift +891 -0
  105. package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/VueNativeViewController.swift +88 -0
  106. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/ComponentRegistry.swift +193 -0
  107. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VActionSheetFactory.swift +91 -0
  108. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VActivityIndicatorFactory.swift +74 -0
  109. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VAlertDialogFactory.swift +150 -0
  110. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VButtonFactory.swift +93 -0
  111. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VCheckboxFactory.swift +114 -0
  112. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VDropdownFactory.swift +112 -0
  113. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VImageFactory.swift +172 -0
  114. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VInputFactory.swift +357 -0
  115. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VKeyboardAvoidingFactory.swift +99 -0
  116. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VListFactory.swift +250 -0
  117. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VModalFactory.swift +112 -0
  118. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VPickerFactory.swift +96 -0
  119. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VPressableFactory.swift +168 -0
  120. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VProgressBarFactory.swift +39 -0
  121. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VRadioFactory.swift +167 -0
  122. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VRefreshControlFactory.swift +153 -0
  123. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VSafeAreaFactory.swift +56 -0
  124. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VScrollViewFactory.swift +240 -0
  125. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VSectionListFactory.swift +248 -0
  126. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VSegmentedControlFactory.swift +73 -0
  127. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VSliderFactory.swift +63 -0
  128. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VStatusBarFactory.swift +50 -0
  129. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VSwitchFactory.swift +108 -0
  130. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VTextFactory.swift +290 -0
  131. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VVideoFactory.swift +246 -0
  132. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VViewFactory.swift +157 -0
  133. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VWebViewFactory.swift +172 -0
  134. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/NativeComponentFactory.swift +53 -0
  135. package/native/ios/VueNativeCore/Sources/VueNativeCore/Helpers/GestureWrapper.swift +107 -0
  136. package/native/ios/VueNativeCore/Sources/VueNativeCore/Helpers/TouchableView.swift +136 -0
  137. package/native/ios/VueNativeCore/Sources/VueNativeCore/Helpers/UIColor+Hex.swift +80 -0
  138. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/AnimationModule.swift +291 -0
  139. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/AppStateModule.swift +65 -0
  140. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/AsyncStorageModule.swift +68 -0
  141. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/AudioModule.swift +366 -0
  142. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/BackgroundTaskModule.swift +135 -0
  143. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/BiometryModule.swift +61 -0
  144. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/BluetoothModule.swift +387 -0
  145. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/CalendarModule.swift +161 -0
  146. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/CameraModule.swift +318 -0
  147. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/ClipboardModule.swift +33 -0
  148. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/ContactsModule.swift +173 -0
  149. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/DatabaseModule.swift +259 -0
  150. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/DeviceInfoModule.swift +34 -0
  151. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/FileSystemModule.swift +233 -0
  152. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/GeolocationModule.swift +147 -0
  153. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/HapticsModule.swift +50 -0
  154. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/IAPModule.swift +194 -0
  155. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/KeyboardModule.swift +31 -0
  156. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/LinkingModule.swift +42 -0
  157. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/NativeModule.swift +28 -0
  158. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/NativeModuleRegistry.swift +78 -0
  159. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/NetworkModule.swift +62 -0
  160. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/NotificationsModule.swift +215 -0
  161. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/OTAModule.swift +281 -0
  162. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/PerformanceModule.swift +138 -0
  163. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/PermissionsModule.swift +190 -0
  164. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/SecureStorageModule.swift +118 -0
  165. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/SensorsModule.swift +103 -0
  166. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/ShareModule.swift +49 -0
  167. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/SocialAuthModule.swift +240 -0
  168. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/WebSocketModule.swift +213 -0
  169. package/native/ios/VueNativeCore/Sources/VueNativeCore/Resources/vue-native-placeholder.js +8 -0
  170. package/native/ios/VueNativeCore/Sources/VueNativeCore/Styling/StyleEngine.swift +885 -0
  171. package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/JSRuntimeTests.swift +362 -0
  172. package/package.json +3 -2
@@ -0,0 +1,357 @@
1
+ #if canImport(UIKit)
2
+ import UIKit
3
+ import ObjectiveC
4
+ import FlexLayout
5
+
6
+ /// Factory for VInput — the text input component.
7
+ /// Maps to a UITextField with FlexLayout enabled.
8
+ /// Supports v-model via text prop and changetext event.
9
+ final class VInputFactory: NativeComponentFactory {
10
+
11
+ // MARK: - Associated object keys
12
+
13
+ private static var delegateKey: UInt8 = 0
14
+ private static var changeTextHandlerKey: UInt8 = 0
15
+ private static var focusHandlerKey: UInt8 = 0
16
+ private static var blurHandlerKey: UInt8 = 0
17
+ private static var submitHandlerKey: UInt8 = 0
18
+
19
+ // MARK: - Keyboard type mapping
20
+
21
+ static let keyboardTypeMap: [String: UIKeyboardType] = [
22
+ "default": .default,
23
+ "numeric": .numberPad,
24
+ "number-pad": .numberPad,
25
+ "decimal-pad": .decimalPad,
26
+ "email": .emailAddress,
27
+ "email-address": .emailAddress,
28
+ "phone": .phonePad,
29
+ "phone-pad": .phonePad,
30
+ "url": .URL,
31
+ "web-search": .webSearch,
32
+ "ascii": .asciiCapable,
33
+ ]
34
+
35
+ // MARK: - Return key mapping
36
+
37
+ static let returnKeyMap: [String: UIReturnKeyType] = [
38
+ "default": .default,
39
+ "done": .done,
40
+ "go": .go,
41
+ "next": .next,
42
+ "search": .search,
43
+ "send": .send,
44
+ "join": .join,
45
+ "route": .route,
46
+ ]
47
+
48
+ // MARK: - Auto-capitalize mapping
49
+
50
+ static let autoCapitalizeMap: [String: UITextAutocapitalizationType] = [
51
+ "none": .none,
52
+ "words": .words,
53
+ "sentences": .sentences,
54
+ "characters": .allCharacters,
55
+ ]
56
+
57
+ // MARK: - NativeComponentFactory
58
+
59
+ func createView() -> UIView {
60
+ let textField = UITextField()
61
+ textField.borderStyle = .none
62
+ // Accessing .flex automatically enables Yoga layout.
63
+ // Set a sensible default height so the text field is not collapsed.
64
+ textField.flex.height(44)
65
+ return textField
66
+ }
67
+
68
+ func updateProp(view: UIView, key: String, value: Any?) {
69
+ guard let textField = view as? UITextField else {
70
+ StyleEngine.apply(key: key, value: value, to: view)
71
+ return
72
+ }
73
+
74
+ switch key {
75
+ case "text", "value":
76
+ if let text = value as? String {
77
+ // Only update if different to avoid cursor jump
78
+ if textField.text != text {
79
+ textField.text = text
80
+ }
81
+ } else {
82
+ textField.text = nil
83
+ }
84
+
85
+ case "placeholder":
86
+ if let placeholder = value as? String {
87
+ textField.placeholder = placeholder
88
+ } else {
89
+ textField.placeholder = nil
90
+ }
91
+
92
+ case "placeholderColor", "placeholderTextColor":
93
+ if let colorStr = value as? String, let placeholder = textField.placeholder {
94
+ textField.attributedPlaceholder = NSAttributedString(
95
+ string: placeholder,
96
+ attributes: [.foregroundColor: UIColor.fromHex(colorStr)]
97
+ )
98
+ }
99
+
100
+ case "secureTextEntry":
101
+ if let secure = value as? Bool {
102
+ textField.isSecureTextEntry = secure
103
+ } else if let secure = value as? Int {
104
+ textField.isSecureTextEntry = secure != 0
105
+ } else {
106
+ textField.isSecureTextEntry = false
107
+ }
108
+
109
+ case "keyboardType":
110
+ if let typeStr = value as? String {
111
+ textField.keyboardType = VInputFactory.keyboardTypeMap[typeStr] ?? .default
112
+ } else {
113
+ textField.keyboardType = .default
114
+ }
115
+
116
+ case "returnKeyType":
117
+ if let typeStr = value as? String {
118
+ textField.returnKeyType = VInputFactory.returnKeyMap[typeStr] ?? .default
119
+ } else {
120
+ textField.returnKeyType = .default
121
+ }
122
+
123
+ case "autoCapitalize", "autocapitalize":
124
+ if let capStr = value as? String {
125
+ textField.autocapitalizationType = VInputFactory.autoCapitalizeMap[capStr] ?? .sentences
126
+ } else {
127
+ textField.autocapitalizationType = .sentences
128
+ }
129
+
130
+ case "autoCorrect", "autocorrect":
131
+ if let correct = value as? Bool {
132
+ textField.autocorrectionType = correct ? .yes : .no
133
+ } else {
134
+ textField.autocorrectionType = .default
135
+ }
136
+
137
+ case "editable":
138
+ if let editable = value as? Bool {
139
+ textField.isEnabled = editable
140
+ } else {
141
+ textField.isEnabled = true
142
+ }
143
+
144
+ case "maxLength":
145
+ // Store max length for use in delegate
146
+ if let maxLen = value as? Int {
147
+ storeMaxLength(maxLen, on: textField)
148
+ } else if let maxLen = value as? Double {
149
+ storeMaxLength(Int(maxLen), on: textField)
150
+ }
151
+
152
+ case "color":
153
+ if let colorStr = value as? String {
154
+ textField.textColor = UIColor.fromHex(colorStr)
155
+ } else {
156
+ textField.textColor = .label
157
+ }
158
+
159
+ case "fontSize":
160
+ if let size = value as? Double {
161
+ textField.font = UIFont.systemFont(ofSize: CGFloat(size))
162
+ } else if let size = value as? Int {
163
+ textField.font = UIFont.systemFont(ofSize: CGFloat(size))
164
+ }
165
+
166
+ case "textAlign":
167
+ if let alignStr = value as? String {
168
+ switch alignStr {
169
+ case "left": textField.textAlignment = .left
170
+ case "center": textField.textAlignment = .center
171
+ case "right": textField.textAlignment = .right
172
+ default: textField.textAlignment = .natural
173
+ }
174
+ }
175
+
176
+ default:
177
+ StyleEngine.apply(key: key, value: value, to: view)
178
+ }
179
+ }
180
+
181
+ func addEventListener(view: UIView, event: String, handler: @escaping (Any?) -> Void) {
182
+ guard let textField = view as? UITextField else { return }
183
+
184
+ // Ensure we have a delegate proxy set up
185
+ let delegate = ensureDelegate(for: textField)
186
+
187
+ switch event {
188
+ case "changetext":
189
+ delegate.onChangeText = handler
190
+ // Add editingChanged target
191
+ textField.addTarget(
192
+ delegate,
193
+ action: #selector(InputDelegateProxy.textFieldEditingChanged(_:)),
194
+ for: .editingChanged
195
+ )
196
+ objc_setAssociatedObject(
197
+ view,
198
+ &VInputFactory.changeTextHandlerKey,
199
+ handler as AnyObject,
200
+ .OBJC_ASSOCIATION_RETAIN_NONATOMIC
201
+ )
202
+
203
+ case "focus":
204
+ delegate.onFocus = handler
205
+ textField.addTarget(
206
+ delegate,
207
+ action: #selector(InputDelegateProxy.textFieldDidBeginEditing(_:)),
208
+ for: .editingDidBegin
209
+ )
210
+ objc_setAssociatedObject(
211
+ view,
212
+ &VInputFactory.focusHandlerKey,
213
+ handler as AnyObject,
214
+ .OBJC_ASSOCIATION_RETAIN_NONATOMIC
215
+ )
216
+
217
+ case "blur":
218
+ delegate.onBlur = handler
219
+ textField.addTarget(
220
+ delegate,
221
+ action: #selector(InputDelegateProxy.textFieldDidEndEditing(_:)),
222
+ for: .editingDidEnd
223
+ )
224
+ objc_setAssociatedObject(
225
+ view,
226
+ &VInputFactory.blurHandlerKey,
227
+ handler as AnyObject,
228
+ .OBJC_ASSOCIATION_RETAIN_NONATOMIC
229
+ )
230
+
231
+ case "submit":
232
+ delegate.onSubmit = handler
233
+ textField.addTarget(
234
+ delegate,
235
+ action: #selector(InputDelegateProxy.textFieldDidReturn(_:)),
236
+ for: .editingDidEndOnExit
237
+ )
238
+ objc_setAssociatedObject(
239
+ view,
240
+ &VInputFactory.submitHandlerKey,
241
+ handler as AnyObject,
242
+ .OBJC_ASSOCIATION_RETAIN_NONATOMIC
243
+ )
244
+
245
+ default:
246
+ break
247
+ }
248
+ }
249
+
250
+ func removeEventListener(view: UIView, event: String) {
251
+ guard let textField = view as? UITextField else { return }
252
+ guard let delegate = objc_getAssociatedObject(textField, &VInputFactory.delegateKey) as? InputDelegateProxy else { return }
253
+
254
+ switch event {
255
+ case "changetext":
256
+ delegate.onChangeText = nil
257
+ textField.removeTarget(delegate, action: #selector(InputDelegateProxy.textFieldEditingChanged(_:)), for: .editingChanged)
258
+ case "focus":
259
+ delegate.onFocus = nil
260
+ textField.removeTarget(delegate, action: #selector(InputDelegateProxy.textFieldDidBeginEditing(_:)), for: .editingDidBegin)
261
+ case "blur":
262
+ delegate.onBlur = nil
263
+ textField.removeTarget(delegate, action: #selector(InputDelegateProxy.textFieldDidEndEditing(_:)), for: .editingDidEnd)
264
+ case "submit":
265
+ delegate.onSubmit = nil
266
+ textField.removeTarget(delegate, action: #selector(InputDelegateProxy.textFieldDidReturn(_:)), for: .editingDidEndOnExit)
267
+ default:
268
+ break
269
+ }
270
+ }
271
+
272
+ // MARK: - Private helpers
273
+
274
+ private func ensureDelegate(for textField: UITextField) -> InputDelegateProxy {
275
+ if let existing = objc_getAssociatedObject(textField, &VInputFactory.delegateKey) as? InputDelegateProxy {
276
+ return existing
277
+ }
278
+
279
+ let delegate = InputDelegateProxy()
280
+ delegate.maxLengthProvider = { [weak textField] in
281
+ guard let tf = textField else { return nil }
282
+ return self.storedMaxLength(on: tf)
283
+ }
284
+ textField.delegate = delegate
285
+ objc_setAssociatedObject(
286
+ textField,
287
+ &VInputFactory.delegateKey,
288
+ delegate,
289
+ .OBJC_ASSOCIATION_RETAIN_NONATOMIC
290
+ )
291
+ return delegate
292
+ }
293
+
294
+ // MARK: - Max length storage
295
+
296
+ private static var maxLengthKey: UInt8 = 0
297
+
298
+ private func storeMaxLength(_ length: Int, on view: UIView) {
299
+ objc_setAssociatedObject(view, &VInputFactory.maxLengthKey, length, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
300
+ }
301
+
302
+ private func storedMaxLength(on view: UIView) -> Int? {
303
+ return objc_getAssociatedObject(view, &VInputFactory.maxLengthKey) as? Int
304
+ }
305
+ }
306
+
307
+ // MARK: - InputDelegateProxy
308
+
309
+ /// UITextFieldDelegate proxy that routes text field events to closure-based handlers.
310
+ /// Stored as an associated object on the UITextField.
311
+ final class InputDelegateProxy: NSObject, UITextFieldDelegate {
312
+
313
+ var onChangeText: ((Any?) -> Void)?
314
+ var onFocus: ((Any?) -> Void)?
315
+ var onBlur: ((Any?) -> Void)?
316
+ var onSubmit: ((Any?) -> Void)?
317
+ var maxLengthProvider: (() -> Int?)?
318
+
319
+ @objc func textFieldEditingChanged(_ textField: UITextField) {
320
+ onChangeText?(textField.text ?? "")
321
+ }
322
+
323
+ @objc func textFieldDidBeginEditing(_ textField: UITextField) {
324
+ onFocus?(nil)
325
+ }
326
+
327
+ @objc func textFieldDidEndEditing(_ textField: UITextField) {
328
+ onBlur?(nil)
329
+ }
330
+
331
+ @objc func textFieldDidReturn(_ textField: UITextField) {
332
+ onSubmit?(textField.text ?? "")
333
+ }
334
+
335
+ // MARK: - UITextFieldDelegate
336
+
337
+ func textField(
338
+ _ textField: UITextField,
339
+ shouldChangeCharactersIn range: NSRange,
340
+ replacementString string: String
341
+ ) -> Bool {
342
+ // Enforce max length if set
343
+ if let maxLength = maxLengthProvider?() {
344
+ let currentText = textField.text ?? ""
345
+ let newLength = currentText.count + string.count - range.length
346
+ return newLength <= maxLength
347
+ }
348
+ return true
349
+ }
350
+
351
+ func textFieldShouldReturn(_ textField: UITextField) -> Bool {
352
+ // Allow return key to trigger editingDidEndOnExit
353
+ textField.resignFirstResponder()
354
+ return true
355
+ }
356
+ }
357
+ #endif
@@ -0,0 +1,99 @@
1
+ #if canImport(UIKit)
2
+ import UIKit
3
+ import FlexLayout
4
+
5
+ /// Factory for VKeyboardAvoiding — a container that adjusts its bottom padding
6
+ /// to avoid the system keyboard.
7
+ ///
8
+ /// Listens to UIResponder.keyboardWillShowNotification / keyboardWillHideNotification
9
+ /// and updates FlexLayout bottom padding accordingly.
10
+ final class VKeyboardAvoidingFactory: NativeComponentFactory {
11
+
12
+ // MARK: - Associated object keys
13
+
14
+ private static var observerKey: UInt8 = 0
15
+
16
+ // MARK: - NativeComponentFactory
17
+
18
+ func createView() -> UIView {
19
+ let view = KeyboardAvoidingView()
20
+ _ = view.flex
21
+ return view
22
+ }
23
+
24
+ func updateProp(view: UIView, key: String, value: Any?) {
25
+ StyleEngine.apply(key: key, value: value, to: view)
26
+ }
27
+
28
+ func addEventListener(view: UIView, event: String, handler: @escaping (Any?) -> Void) {
29
+ // No events exposed for keyboard avoiding view
30
+ }
31
+ }
32
+
33
+ // MARK: - KeyboardAvoidingView
34
+
35
+ /// UIView subclass that automatically adjusts its Yoga bottom padding
36
+ /// based on keyboard visibility.
37
+ private final class KeyboardAvoidingView: UIView {
38
+
39
+ private var showObserver: NSObjectProtocol?
40
+ private var hideObserver: NSObjectProtocol?
41
+
42
+ override init(frame: CGRect) {
43
+ super.init(frame: frame)
44
+ setupKeyboardObservers()
45
+ }
46
+
47
+ required init?(coder: NSCoder) {
48
+ super.init(coder: coder)
49
+ setupKeyboardObservers()
50
+ }
51
+
52
+ deinit {
53
+ if let obs = showObserver { NotificationCenter.default.removeObserver(obs) }
54
+ if let obs = hideObserver { NotificationCenter.default.removeObserver(obs) }
55
+ }
56
+
57
+ private func setupKeyboardObservers() {
58
+ showObserver = NotificationCenter.default.addObserver(
59
+ forName: UIResponder.keyboardWillShowNotification,
60
+ object: nil,
61
+ queue: .main
62
+ ) { [weak self] notification in
63
+ self?.handleKeyboardShow(notification)
64
+ }
65
+
66
+ hideObserver = NotificationCenter.default.addObserver(
67
+ forName: UIResponder.keyboardWillHideNotification,
68
+ object: nil,
69
+ queue: .main
70
+ ) { [weak self] _ in
71
+ self?.handleKeyboardHide()
72
+ }
73
+ }
74
+
75
+ private func handleKeyboardShow(_ notification: Notification) {
76
+ guard let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else {
77
+ return
78
+ }
79
+ let keyboardHeight = keyboardFrame.height
80
+ guard keyboardHeight > 0 else { return }
81
+ flex.paddingBottom(keyboardHeight)
82
+ triggerLayout()
83
+ }
84
+
85
+ private func handleKeyboardHide() {
86
+ flex.paddingBottom(0)
87
+ triggerLayout()
88
+ }
89
+
90
+ private func triggerLayout() {
91
+ // Walk up to find the root flex view and trigger layout
92
+ var view: UIView? = self
93
+ while let v = view?.superview {
94
+ view = v
95
+ }
96
+ view?.flex.layout()
97
+ }
98
+ }
99
+ #endif
@@ -0,0 +1,250 @@
1
+ #if canImport(UIKit)
2
+ import UIKit
3
+ import FlexLayout
4
+
5
+ // MARK: - VListFactory
6
+
7
+ /// Factory for VList — a virtualized scrollable list backed by UITableView.
8
+ /// Children inserted via the bridge are stored as table cells, not regular subviews.
9
+ /// Supports scroll and endReached events.
10
+ final class VListFactory: NativeComponentFactory {
11
+
12
+ func createView() -> UIView {
13
+ let container = VListContainerView()
14
+ _ = container.flex
15
+ return container
16
+ }
17
+
18
+ func updateProp(view: UIView, key: String, value: Any?) {
19
+ guard let container = view as? VListContainerView else {
20
+ StyleEngine.apply(key: key, value: value, to: view)
21
+ return
22
+ }
23
+ switch key {
24
+ case "estimatedItemHeight":
25
+ container.estimatedItemHeight = CGFloat(value as? Double ?? 44)
26
+ case "showsScrollIndicator":
27
+ container.tableView.showsVerticalScrollIndicator = value as? Bool ?? true
28
+ case "bounces":
29
+ container.tableView.bounces = value as? Bool ?? true
30
+ default:
31
+ StyleEngine.apply(key: key, value: value, to: view)
32
+ }
33
+ }
34
+
35
+ func addEventListener(view: UIView, event: String, handler: @escaping (Any?) -> Void) {
36
+ guard let container = view as? VListContainerView else { return }
37
+ switch event {
38
+ case "scroll":
39
+ container.onScroll = handler
40
+ case "endReached":
41
+ container.onEndReached = handler
42
+ default:
43
+ break
44
+ }
45
+ }
46
+
47
+ func removeEventListener(view: UIView, event: String) {
48
+ guard let container = view as? VListContainerView else { return }
49
+ switch event {
50
+ case "scroll": container.onScroll = nil
51
+ case "endReached": container.onEndReached = nil
52
+ default: break
53
+ }
54
+ }
55
+
56
+ // MARK: - Custom child management
57
+
58
+ func insertChild(_ child: UIView, into parent: UIView, before anchor: UIView?) {
59
+ guard let container = parent as? VListContainerView else {
60
+ // Fallback for non-VList parents (shouldn't happen)
61
+ if let anchor = anchor, let idx = parent.subviews.firstIndex(of: anchor) {
62
+ parent.insertSubview(child, at: idx)
63
+ } else {
64
+ parent.addSubview(child)
65
+ }
66
+ return
67
+ }
68
+ let insertIdx: Int
69
+ if let anchor = anchor, let idx = container.itemViews.firstIndex(where: { $0 === anchor }) {
70
+ container.itemViews.insert(child, at: idx)
71
+ insertIdx = idx
72
+ } else {
73
+ insertIdx = container.itemViews.count
74
+ container.itemViews.append(child)
75
+ }
76
+ // Trigger layout so Yoga can calculate item height before the cell is displayed
77
+ container.setNeedsLayout()
78
+ // Use targeted insert rather than reloadData to avoid triggering layoutSubviews recursively
79
+ container.tableView.insertRows(at: [IndexPath(row: insertIdx, section: 0)], with: .none)
80
+ }
81
+
82
+ func removeChild(_ child: UIView, from parent: UIView) {
83
+ guard let container = parent as? VListContainerView else {
84
+ child.removeFromSuperview()
85
+ return
86
+ }
87
+ guard let idx = container.itemViews.firstIndex(where: { $0 === child }) else {
88
+ child.removeFromSuperview()
89
+ return
90
+ }
91
+ container.itemViews.remove(at: idx)
92
+ // Remove from any cell it's currently displayed in
93
+ child.removeFromSuperview()
94
+ // Use targeted delete rather than reloadData
95
+ container.tableView.deleteRows(at: [IndexPath(row: idx, section: 0)], with: .none)
96
+ }
97
+ }
98
+
99
+ // MARK: - VListContainerView
100
+
101
+ /// Container view that hosts a UITableView filling its bounds.
102
+ /// Item views are managed in itemViews array (not as regular subviews).
103
+ final class VListContainerView: UIView {
104
+
105
+ let tableView: UITableView
106
+ var itemViews: [UIView] = []
107
+ var estimatedItemHeight: CGFloat = 44
108
+ var onScroll: ((Any?) -> Void)?
109
+ var onEndReached: ((Any?) -> Void)?
110
+ fileprivate var firedEndReached = false
111
+ private lazy var internalDelegate = VListInternalDelegate(container: self)
112
+
113
+ init() {
114
+ tableView = UITableView(frame: .zero, style: .plain)
115
+ super.init(frame: .zero)
116
+ tableView.separatorStyle = .none
117
+ tableView.tableFooterView = UIView()
118
+ tableView.dataSource = internalDelegate
119
+ tableView.delegate = internalDelegate
120
+ tableView.register(VListCell.self, forCellReuseIdentifier: "VListCell")
121
+ // Add tableView as a real subview of self
122
+ super.addSubview(tableView)
123
+
124
+ // Accessibility: let VoiceOver navigate to individual children within the list
125
+ isAccessibilityElement = false
126
+ shouldGroupAccessibilityChildren = true
127
+ }
128
+
129
+ required init?(coder: NSCoder) { fatalError() }
130
+
131
+ override func layoutSubviews() {
132
+ super.layoutSubviews()
133
+ tableView.frame = bounds
134
+
135
+ // Run Yoga layout on each item view to compute its height
136
+ let width = bounds.width
137
+ guard width > 0 else { return }
138
+
139
+ var changedIndexPaths: [IndexPath] = []
140
+ for (idx, itemView) in itemViews.enumerated() {
141
+ // Only recompute if width changed (avoids re-entrant layout loops)
142
+ if abs(itemView.frame.size.width - width) > 0.5 {
143
+ itemView.frame.size.width = width
144
+ itemView.flex.layout(mode: .adjustHeight)
145
+ changedIndexPaths.append(IndexPath(row: idx, section: 0))
146
+ }
147
+ }
148
+ if !changedIndexPaths.isEmpty {
149
+ // Reload only the rows whose heights changed, not the entire table
150
+ tableView.reloadRows(at: changedIndexPaths, with: .none)
151
+ }
152
+ }
153
+ }
154
+
155
+ // MARK: - VListInternalDelegate
156
+
157
+ private final class VListInternalDelegate: NSObject,
158
+ UITableViewDataSource, UITableViewDelegate
159
+ {
160
+ private weak var container: VListContainerView?
161
+
162
+ init(container: VListContainerView) {
163
+ self.container = container
164
+ }
165
+
166
+ func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
167
+ container?.itemViews.count ?? 0
168
+ }
169
+
170
+ func tableView(_ tableView: UITableView,
171
+ cellForRowAt indexPath: IndexPath) -> UITableViewCell {
172
+ let cell = tableView.dequeueReusableCell(
173
+ withIdentifier: "VListCell", for: indexPath) as! VListCell
174
+ guard let container = container,
175
+ indexPath.row < container.itemViews.count else { return cell }
176
+ cell.setItemView(container.itemViews[indexPath.row])
177
+ return cell
178
+ }
179
+
180
+ func tableView(_ tableView: UITableView,
181
+ estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
182
+ container?.estimatedItemHeight ?? 44
183
+ }
184
+
185
+ func tableView(_ tableView: UITableView,
186
+ heightForRowAt indexPath: IndexPath) -> CGFloat {
187
+ guard let container = container,
188
+ indexPath.row < container.itemViews.count else {
189
+ return container?.estimatedItemHeight ?? 44
190
+ }
191
+ let h = container.itemViews[indexPath.row].frame.size.height
192
+ return h > 1 ? h : (container.estimatedItemHeight)
193
+ }
194
+
195
+ func scrollViewDidScroll(_ scrollView: UIScrollView) {
196
+ guard let container = container else { return }
197
+ let offset = scrollView.contentOffset
198
+ container.onScroll?(["x": Double(offset.x), "y": Double(offset.y)])
199
+
200
+ // endReached detection (threshold = 20% from bottom)
201
+ let contentH = scrollView.contentSize.height
202
+ let frameH = scrollView.frame.size.height
203
+ guard contentH > frameH else { return }
204
+ let distanceFromBottom = contentH - frameH - offset.y
205
+ let threshold = frameH * 0.2
206
+
207
+ if distanceFromBottom < threshold && !container.firedEndReached {
208
+ container.firedEndReached = true
209
+ container.onEndReached?(nil)
210
+ } else if distanceFromBottom >= threshold {
211
+ container.firedEndReached = false
212
+ }
213
+ }
214
+ }
215
+
216
+ // MARK: - VListCell
217
+
218
+ private final class VListCell: UITableViewCell {
219
+
220
+ private var currentView: UIView?
221
+
222
+ override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
223
+ super.init(style: style, reuseIdentifier: reuseIdentifier)
224
+ // Accessibility: cells are containers — VoiceOver should navigate to children inside
225
+ isAccessibilityElement = false
226
+ accessibilityTraits = .none
227
+ }
228
+
229
+ required init?(coder: NSCoder) { fatalError() }
230
+
231
+ func setItemView(_ view: UIView) {
232
+ guard currentView !== view else { return }
233
+ currentView?.removeFromSuperview()
234
+ currentView = view
235
+ contentView.addSubview(view)
236
+ view.frame = contentView.bounds
237
+ }
238
+
239
+ override func layoutSubviews() {
240
+ super.layoutSubviews()
241
+ currentView?.frame = contentView.bounds
242
+ }
243
+
244
+ override func prepareForReuse() {
245
+ super.prepareForReuse()
246
+ currentView?.removeFromSuperview()
247
+ currentView = nil
248
+ }
249
+ }
250
+ #endif