@thelacanians/vue-native-cli 0.4.3 → 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 +34 -17
  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,153 @@
1
+ #if canImport(UIKit)
2
+ import UIKit
3
+ import ObjectiveC
4
+ import FlexLayout
5
+
6
+ /// Factory for VRefreshControl — the pull-to-refresh indicator component.
7
+ ///
8
+ /// Creates a lightweight UIView wrapper. When this view is inserted as a child
9
+ /// of a UIScrollView (via VScrollView or VList), the factory attaches a
10
+ /// UIRefreshControl to the parent scroll view's `.refreshControl` property.
11
+ final class VRefreshControlFactory: NativeComponentFactory {
12
+
13
+ // MARK: - Associated object keys
14
+
15
+ private static var refreshControlKey: UInt8 = 0
16
+ private static var refreshTargetKey: UInt8 = 1
17
+ private static var refreshHandlerKey: UInt8 = 2
18
+
19
+ // MARK: - NativeComponentFactory
20
+
21
+ func createView() -> UIView {
22
+ // The wrapper view is zero-sized and invisible.
23
+ // The actual UIRefreshControl is attached to the parent scroll view.
24
+ let wrapper = UIView()
25
+ wrapper.isHidden = true
26
+ wrapper.frame = .zero
27
+
28
+ // Create the UIRefreshControl and store it on the wrapper
29
+ let refreshControl = UIRefreshControl()
30
+ objc_setAssociatedObject(
31
+ wrapper,
32
+ &VRefreshControlFactory.refreshControlKey,
33
+ refreshControl,
34
+ .OBJC_ASSOCIATION_RETAIN_NONATOMIC
35
+ )
36
+
37
+ return wrapper
38
+ }
39
+
40
+ func updateProp(view: UIView, key: String, value: Any?) {
41
+ guard let refreshControl = objc_getAssociatedObject(
42
+ view,
43
+ &VRefreshControlFactory.refreshControlKey
44
+ ) as? UIRefreshControl else { return }
45
+
46
+ switch key {
47
+ case "refreshing":
48
+ let refreshing: Bool
49
+ if let b = value as? Bool { refreshing = b }
50
+ else if let n = value as? NSNumber { refreshing = n.boolValue }
51
+ else if let n = value as? Int { refreshing = n != 0 }
52
+ else { refreshing = false }
53
+
54
+ if refreshing {
55
+ refreshControl.beginRefreshing()
56
+ } else {
57
+ refreshControl.endRefreshing()
58
+ }
59
+
60
+ case "tintColor":
61
+ if let hex = value as? String {
62
+ refreshControl.tintColor = UIColor.fromHex(hex)
63
+ } else {
64
+ refreshControl.tintColor = nil
65
+ }
66
+
67
+ case "title":
68
+ if let text = value as? String {
69
+ refreshControl.attributedTitle = NSAttributedString(string: text)
70
+ } else {
71
+ refreshControl.attributedTitle = nil
72
+ }
73
+
74
+ default:
75
+ break
76
+ }
77
+ }
78
+
79
+ func addEventListener(view: UIView, event: String, handler: @escaping (Any?) -> Void) {
80
+ guard event == "refresh" else { return }
81
+ guard let refreshControl = objc_getAssociatedObject(
82
+ view,
83
+ &VRefreshControlFactory.refreshControlKey
84
+ ) as? UIRefreshControl else { return }
85
+
86
+ // Store handler reference
87
+ objc_setAssociatedObject(
88
+ view,
89
+ &VRefreshControlFactory.refreshHandlerKey,
90
+ handler as AnyObject,
91
+ .OBJC_ASSOCIATION_RETAIN_NONATOMIC
92
+ )
93
+
94
+ // Create ObjC-compatible target
95
+ let target = RefreshControlTarget(handler: handler)
96
+ objc_setAssociatedObject(
97
+ view,
98
+ &VRefreshControlFactory.refreshTargetKey,
99
+ target,
100
+ .OBJC_ASSOCIATION_RETAIN_NONATOMIC
101
+ )
102
+ refreshControl.addTarget(target, action: #selector(RefreshControlTarget.handleRefresh), for: .valueChanged)
103
+ }
104
+
105
+ func removeEventListener(view: UIView, event: String) {
106
+ guard event == "refresh" else { return }
107
+ guard let refreshControl = objc_getAssociatedObject(
108
+ view,
109
+ &VRefreshControlFactory.refreshControlKey
110
+ ) as? UIRefreshControl else { return }
111
+
112
+ refreshControl.removeTarget(nil, action: nil, for: .valueChanged)
113
+ objc_setAssociatedObject(view, &VRefreshControlFactory.refreshTargetKey, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
114
+ objc_setAssociatedObject(view, &VRefreshControlFactory.refreshHandlerKey, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
115
+ }
116
+
117
+ // MARK: - Child management
118
+
119
+ /// When inserted into a parent, attach the UIRefreshControl to the
120
+ /// nearest UIScrollView ancestor.
121
+ func insertChild(_ child: UIView, into parent: UIView, before anchor: UIView?) {
122
+ // Default: just add as subview (hidden, zero-frame)
123
+ if let anchor = anchor, let idx = parent.subviews.firstIndex(of: anchor) {
124
+ parent.insertSubview(child, at: idx)
125
+ } else {
126
+ parent.addSubview(child)
127
+ }
128
+ }
129
+
130
+ // MARK: - Static helpers
131
+
132
+ /// Retrieve the UIRefreshControl stored on a VRefreshControl wrapper view.
133
+ static func refreshControl(for view: UIView) -> UIRefreshControl? {
134
+ return objc_getAssociatedObject(view, &refreshControlKey) as? UIRefreshControl
135
+ }
136
+ }
137
+
138
+ // MARK: - RefreshControlTarget
139
+
140
+ /// ObjC-compatible target for UIRefreshControl .valueChanged action.
141
+ private final class RefreshControlTarget: NSObject {
142
+ private let handler: (Any?) -> Void
143
+
144
+ init(handler: @escaping (Any?) -> Void) {
145
+ self.handler = handler
146
+ super.init()
147
+ }
148
+
149
+ @objc func handleRefresh() {
150
+ handler(nil)
151
+ }
152
+ }
153
+ #endif
@@ -0,0 +1,56 @@
1
+ #if canImport(UIKit)
2
+ import UIKit
3
+ import FlexLayout
4
+
5
+ /// Factory for VSafeArea — a container that automatically applies safe area insets as padding.
6
+ /// Maps content insets to Yoga padding so children avoid the notch/home indicator.
7
+ final class VSafeAreaFactory: NativeComponentFactory {
8
+
9
+ func createView() -> UIView {
10
+ let view = SafeAreaView()
11
+ _ = view.flex
12
+ return view
13
+ }
14
+
15
+ func updateProp(view: UIView, key: String, value: Any?) {
16
+ StyleEngine.apply(key: key, value: value, to: view)
17
+ }
18
+
19
+ func addEventListener(view: UIView, event: String, handler: @escaping (Any?) -> Void) {}
20
+ func removeEventListener(view: UIView, event: String) {}
21
+ }
22
+
23
+ /// UIView subclass that applies safeAreaInsets as Yoga padding on every layout pass.
24
+ final class SafeAreaView: UIView {
25
+
26
+ override func safeAreaInsetsDidChange() {
27
+ super.safeAreaInsetsDidChange()
28
+ applyInsets()
29
+ }
30
+
31
+ override func didMoveToWindow() {
32
+ super.didMoveToWindow()
33
+ applyInsets()
34
+ }
35
+
36
+ private func applyInsets() {
37
+ let insets = safeAreaInsets
38
+ flex.paddingTop(insets.top)
39
+ flex.paddingBottom(insets.bottom)
40
+ flex.paddingLeft(insets.left)
41
+ flex.paddingRight(insets.right)
42
+ // Trigger layout recalculation
43
+ if let root = findFlexRoot() {
44
+ root.flex.layout(mode: .fitContainer)
45
+ }
46
+ }
47
+
48
+ private func findFlexRoot() -> UIView? {
49
+ var v: UIView? = self
50
+ while let parent = v?.superview {
51
+ v = parent
52
+ }
53
+ return v
54
+ }
55
+ }
56
+ #endif
@@ -0,0 +1,240 @@
1
+ #if canImport(UIKit)
2
+ import UIKit
3
+ import ObjectiveC
4
+ import FlexLayout
5
+
6
+ /// Factory for VScrollView — the scrollable container component.
7
+ ///
8
+ /// Maps to UIScrollView on iOS. An inner `contentView` UIView is created as
9
+ /// a subview and managed separately from the scroll view's Yoga node. All
10
+ /// children reported by the bridge are inserted into the contentView, not
11
+ /// the scroll view itself.
12
+ ///
13
+ /// After each main layout pass, NativeBridge calls `layoutContentView(for:)`
14
+ /// to compute the contentView's natural height (unconstrained) and update
15
+ /// the scroll view's contentSize.
16
+ final class VScrollViewFactory: NativeComponentFactory {
17
+
18
+ // MARK: - Associated object keys
19
+
20
+ static var contentViewKey: UInt8 = 0
21
+ static var delegateProxyKey: UInt8 = 1
22
+ static var onRefreshKey: UInt8 = 5
23
+ static var refreshTargetKey: UInt8 = 6
24
+
25
+ // MARK: - NativeComponentFactory
26
+
27
+ func createView() -> UIView {
28
+ let scrollView = UIScrollView()
29
+ scrollView.showsVerticalScrollIndicator = true
30
+ scrollView.showsHorizontalScrollIndicator = false
31
+ scrollView.alwaysBounceVertical = true
32
+ scrollView.clipsToBounds = true
33
+ _ = scrollView.flex
34
+
35
+ // Content view: children are added here so Yoga can measure
36
+ // their natural size without being constrained by the scroll view's bounds.
37
+ let contentView = UIView()
38
+ _ = contentView.flex
39
+ scrollView.addSubview(contentView)
40
+
41
+ objc_setAssociatedObject(
42
+ scrollView,
43
+ &VScrollViewFactory.contentViewKey,
44
+ contentView,
45
+ .OBJC_ASSOCIATION_RETAIN_NONATOMIC
46
+ )
47
+
48
+ return scrollView
49
+ }
50
+
51
+ func updateProp(view: UIView, key: String, value: Any?) {
52
+ guard let scrollView = view as? UIScrollView else {
53
+ StyleEngine.apply(key: key, value: value, to: view)
54
+ return
55
+ }
56
+
57
+ switch key {
58
+ case "horizontal":
59
+ let horizontal: Bool
60
+ if let h = value as? Bool { horizontal = h }
61
+ else if let h = value as? Int { horizontal = h != 0 }
62
+ else { horizontal = false }
63
+ scrollView.alwaysBounceHorizontal = horizontal
64
+ scrollView.alwaysBounceVertical = !horizontal
65
+
66
+ case "showsVerticalScrollIndicator":
67
+ if let shows = value as? Bool { scrollView.showsVerticalScrollIndicator = shows }
68
+
69
+ case "showsHorizontalScrollIndicator":
70
+ if let shows = value as? Bool { scrollView.showsHorizontalScrollIndicator = shows }
71
+
72
+ case "scrollEnabled":
73
+ if let enabled = value as? Bool { scrollView.isScrollEnabled = enabled }
74
+ else if let enabled = value as? Int { scrollView.isScrollEnabled = enabled != 0 }
75
+
76
+ case "bounces":
77
+ if let bounces = value as? Bool { scrollView.bounces = bounces }
78
+
79
+ case "pagingEnabled":
80
+ if let paging = value as? Bool { scrollView.isPagingEnabled = paging }
81
+
82
+ case "refreshing":
83
+ let refreshing: Bool
84
+ if let b = value as? Bool { refreshing = b }
85
+ else if let n = value as? NSNumber { refreshing = n.boolValue }
86
+ else { refreshing = false }
87
+
88
+ if refreshing {
89
+ // Add UIRefreshControl if not already present
90
+ if scrollView.refreshControl == nil {
91
+ let control = UIRefreshControl()
92
+ scrollView.refreshControl = control
93
+ }
94
+ scrollView.refreshControl?.beginRefreshing()
95
+ } else {
96
+ scrollView.refreshControl?.endRefreshing()
97
+ }
98
+
99
+ default:
100
+ StyleEngine.apply(key: key, value: value, to: view)
101
+ }
102
+ }
103
+
104
+ func addEventListener(view: UIView, event: String, handler: @escaping (Any?) -> Void) {
105
+ guard let scrollView = view as? UIScrollView else { return }
106
+ switch event {
107
+ case "scroll":
108
+ let proxy = ScrollDelegateProxy(handler: handler)
109
+ objc_setAssociatedObject(
110
+ scrollView,
111
+ &VScrollViewFactory.delegateProxyKey,
112
+ proxy,
113
+ .OBJC_ASSOCIATION_RETAIN_NONATOMIC
114
+ )
115
+ scrollView.delegate = proxy
116
+ case "refresh":
117
+ // Store the handler
118
+ objc_setAssociatedObject(
119
+ scrollView,
120
+ &VScrollViewFactory.onRefreshKey,
121
+ handler as AnyObject,
122
+ .OBJC_ASSOCIATION_RETAIN_NONATOMIC
123
+ )
124
+ // Create UIRefreshControl if not already present
125
+ if scrollView.refreshControl == nil {
126
+ scrollView.refreshControl = UIRefreshControl()
127
+ }
128
+ guard let refreshControl = scrollView.refreshControl else { break }
129
+ // Create a target helper and store it
130
+ let target = RefreshTarget(handler: handler)
131
+ objc_setAssociatedObject(
132
+ scrollView,
133
+ &VScrollViewFactory.refreshTargetKey,
134
+ target,
135
+ .OBJC_ASSOCIATION_RETAIN_NONATOMIC
136
+ )
137
+ refreshControl.addTarget(target, action: #selector(RefreshTarget.handleRefresh), for: .valueChanged)
138
+
139
+ default:
140
+ break
141
+ }
142
+ }
143
+
144
+ func removeEventListener(view: UIView, event: String) {
145
+ guard let scrollView = view as? UIScrollView, event == "scroll" else { return }
146
+ scrollView.delegate = nil
147
+ objc_setAssociatedObject(
148
+ scrollView,
149
+ &VScrollViewFactory.delegateProxyKey,
150
+ nil,
151
+ .OBJC_ASSOCIATION_RETAIN_NONATOMIC
152
+ )
153
+ }
154
+
155
+ // MARK: - Child management
156
+
157
+ func insertChild(_ child: UIView, into parent: UIView, before anchor: UIView?) {
158
+ if let anchor = anchor, let idx = parent.subviews.firstIndex(of: anchor) {
159
+ parent.insertSubview(child, at: idx)
160
+ } else {
161
+ parent.addSubview(child)
162
+ }
163
+ // Ensure content size is recalculated after child insertion
164
+ if let scrollView = parent.superview as? UIScrollView {
165
+ scrollView.setNeedsLayout()
166
+ }
167
+ }
168
+
169
+ func removeChild(_ child: UIView, from parent: UIView) {
170
+ child.removeFromSuperview()
171
+ // Ensure content size is recalculated after child removal
172
+ if let scrollView = parent.superview as? UIScrollView {
173
+ scrollView.setNeedsLayout()
174
+ }
175
+ }
176
+
177
+ // MARK: - Static helpers
178
+
179
+ /// Retrieve the inner content UIView for a scroll view.
180
+ /// NativeBridge calls this to redirect child insertions.
181
+ static func contentView(for scrollView: UIScrollView) -> UIView? {
182
+ return objc_getAssociatedObject(scrollView, &contentViewKey) as? UIView
183
+ }
184
+
185
+ /// Recompute the content view's layout and update the scroll view's contentSize.
186
+ /// Call this after the main Yoga layout pass, once the scroll view's frame is set.
187
+ @MainActor
188
+ static func layoutContentView(for scrollView: UIScrollView) {
189
+ guard let contentView = contentView(for: scrollView) else { return }
190
+
191
+ let scrollWidth = scrollView.bounds.width
192
+ guard scrollWidth > 0 else { return }
193
+
194
+ // Pin the content view's origin and width
195
+ contentView.frame = CGRect(x: 0, y: 0, width: scrollWidth, height: 0)
196
+
197
+ // Compute natural height — adjustHeight mode grows height to fit children
198
+ contentView.flex.layout(mode: .adjustHeight)
199
+
200
+ // Update the scroll view's scrollable content size
201
+ scrollView.contentSize = contentView.frame.size
202
+ }
203
+ }
204
+
205
+ // MARK: - RefreshTarget
206
+
207
+ /// ObjC-compatible target for UIRefreshControl value-changed actions.
208
+ private final class RefreshTarget: NSObject {
209
+ private let handler: (Any?) -> Void
210
+
211
+ init(handler: @escaping (Any?) -> Void) {
212
+ self.handler = handler
213
+ super.init()
214
+ }
215
+
216
+ @objc func handleRefresh() {
217
+ handler(nil)
218
+ }
219
+ }
220
+
221
+ // MARK: - ScrollDelegateProxy
222
+
223
+ /// UIScrollViewDelegate proxy that forwards scroll events to a JS handler.
224
+ private final class ScrollDelegateProxy: NSObject, UIScrollViewDelegate {
225
+ private let handler: (Any?) -> Void
226
+
227
+ init(handler: @escaping (Any?) -> Void) {
228
+ self.handler = handler
229
+ super.init()
230
+ }
231
+
232
+ func scrollViewDidScroll(_ scrollView: UIScrollView) {
233
+ let payload: [String: Any] = [
234
+ "x": scrollView.contentOffset.x,
235
+ "y": scrollView.contentOffset.y
236
+ ]
237
+ handler(payload)
238
+ }
239
+ }
240
+ #endif
@@ -0,0 +1,248 @@
1
+ #if canImport(UIKit)
2
+ import UIKit
3
+ import FlexLayout
4
+
5
+ // MARK: - VSectionListFactory
6
+
7
+ /// Factory for VSectionList — a sectioned list backed by UITableView with section support.
8
+ /// Children with `__sectionHeader` prop are treated as section headers.
9
+ /// All other children are treated as regular items, grouped under the preceding header.
10
+ final class VSectionListFactory: NativeComponentFactory {
11
+
12
+ func createView() -> UIView {
13
+ let container = VSectionListContainerView()
14
+ _ = container.flex
15
+ return container
16
+ }
17
+
18
+ func updateProp(view: UIView, key: String, value: Any?) {
19
+ guard let container = view as? VSectionListContainerView 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 "stickySectionHeaders":
27
+ // plain style = sticky headers (default), grouped = non-sticky
28
+ // This is set at creation time; we store the preference for reference
29
+ container.stickySectionHeaders = value as? Bool ?? true
30
+ case "showsScrollIndicator":
31
+ container.tableView.showsVerticalScrollIndicator = value as? Bool ?? true
32
+ case "bounces":
33
+ container.tableView.bounces = value as? Bool ?? true
34
+ default:
35
+ StyleEngine.apply(key: key, value: value, to: view)
36
+ }
37
+ }
38
+
39
+ func addEventListener(view: UIView, event: String, handler: @escaping (Any?) -> Void) {
40
+ guard let container = view as? VSectionListContainerView else { return }
41
+ switch event {
42
+ case "scroll":
43
+ container.onScroll = handler
44
+ case "endReached":
45
+ container.onEndReached = handler
46
+ default:
47
+ break
48
+ }
49
+ }
50
+
51
+ func removeEventListener(view: UIView, event: String) {
52
+ guard let container = view as? VSectionListContainerView else { return }
53
+ switch event {
54
+ case "scroll": container.onScroll = nil
55
+ case "endReached": container.onEndReached = nil
56
+ default: break
57
+ }
58
+ }
59
+
60
+ // MARK: - Custom child management
61
+
62
+ func insertChild(_ child: UIView, into parent: UIView, before anchor: UIView?) {
63
+ guard let container = parent as? VSectionListContainerView else {
64
+ if let anchor = anchor, let idx = parent.subviews.firstIndex(of: anchor) {
65
+ parent.insertSubview(child, at: idx)
66
+ } else {
67
+ parent.addSubview(child)
68
+ }
69
+ return
70
+ }
71
+ container.allChildren.append(child)
72
+ container.rebuildSections()
73
+ container.tableView.reloadData()
74
+ }
75
+
76
+ func removeChild(_ child: UIView, from parent: UIView) {
77
+ guard let container = parent as? VSectionListContainerView else {
78
+ child.removeFromSuperview()
79
+ return
80
+ }
81
+ container.allChildren.removeAll { $0 === child }
82
+ child.removeFromSuperview()
83
+ container.rebuildSections()
84
+ container.tableView.reloadData()
85
+ }
86
+ }
87
+
88
+ // MARK: - SectionData
89
+
90
+ /// Represents a section: an optional header view and an array of item views.
91
+ private struct SectionData {
92
+ var headerView: UIView?
93
+ var itemViews: [UIView]
94
+ }
95
+
96
+ // MARK: - VSectionListContainerView
97
+
98
+ /// Container view that hosts a UITableView with section support.
99
+ final class VSectionListContainerView: UIView {
100
+
101
+ let tableView: UITableView
102
+ var allChildren: [UIView] = []
103
+ var estimatedItemHeight: CGFloat = 44
104
+ var stickySectionHeaders: Bool = true
105
+ var onScroll: ((Any?) -> Void)?
106
+ var onEndReached: ((Any?) -> Void)?
107
+ fileprivate var firedEndReached = false
108
+ fileprivate var sections: [SectionData] = []
109
+ private lazy var internalDelegate = VSectionListInternalDelegate(container: self)
110
+
111
+ init() {
112
+ tableView = UITableView(frame: .zero, style: .plain)
113
+ super.init(frame: .zero)
114
+ tableView.separatorStyle = .none
115
+ tableView.tableFooterView = UIView()
116
+ tableView.dataSource = internalDelegate
117
+ tableView.delegate = internalDelegate
118
+ tableView.register(VListCell.self, forCellReuseIdentifier: "VListCell")
119
+ super.addSubview(tableView)
120
+ }
121
+
122
+ required init?(coder: NSCoder) { fatalError() }
123
+
124
+ /// Rebuild section data from the flat allChildren array.
125
+ /// Children that had the `__sectionHeader: true` prop set via the bridge start a new section.
126
+ func rebuildSections() {
127
+ sections = []
128
+ var currentSection = SectionData(headerView: nil, itemViews: [])
129
+
130
+ for child in allChildren {
131
+ let isSectionHeader = StyleEngine.getInternalProp("__sectionHeader", from: child) as? Bool ?? false
132
+ if isSectionHeader {
133
+ // Finalize previous section if it has items or a header
134
+ if currentSection.headerView != nil || !currentSection.itemViews.isEmpty {
135
+ sections.append(currentSection)
136
+ }
137
+ currentSection = SectionData(headerView: child, itemViews: [])
138
+ } else {
139
+ currentSection.itemViews.append(child)
140
+ }
141
+ }
142
+ // Finalize last section
143
+ if currentSection.headerView != nil || !currentSection.itemViews.isEmpty {
144
+ sections.append(currentSection)
145
+ }
146
+ }
147
+
148
+ override func layoutSubviews() {
149
+ super.layoutSubviews()
150
+ tableView.frame = bounds
151
+
152
+ let width = bounds.width
153
+ guard width > 0 else { return }
154
+
155
+ // Lay out all child views for height calculation
156
+ for child in allChildren {
157
+ if abs(child.frame.size.width - width) > 0.5 {
158
+ child.frame.size.width = width
159
+ child.flex.layout(mode: .adjustHeight)
160
+ }
161
+ }
162
+ tableView.reloadData()
163
+ }
164
+ }
165
+
166
+ // MARK: - VSectionListInternalDelegate
167
+
168
+ private final class VSectionListInternalDelegate: NSObject,
169
+ UITableViewDataSource, UITableViewDelegate
170
+ {
171
+ private weak var container: VSectionListContainerView?
172
+
173
+ init(container: VSectionListContainerView) {
174
+ self.container = container
175
+ }
176
+
177
+ func numberOfSections(in tableView: UITableView) -> Int {
178
+ container?.sections.count ?? 0
179
+ }
180
+
181
+ func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
182
+ guard let container = container, section < container.sections.count else { return 0 }
183
+ return container.sections[section].itemViews.count
184
+ }
185
+
186
+ func tableView(_ tableView: UITableView,
187
+ cellForRowAt indexPath: IndexPath) -> UITableViewCell {
188
+ let cell = tableView.dequeueReusableCell(
189
+ withIdentifier: "VListCell", for: indexPath) as! VListCell
190
+ guard let container = container,
191
+ indexPath.section < container.sections.count,
192
+ indexPath.row < container.sections[indexPath.section].itemViews.count else { return cell }
193
+ cell.setItemView(container.sections[indexPath.section].itemViews[indexPath.row])
194
+ return cell
195
+ }
196
+
197
+ func tableView(_ tableView: UITableView,
198
+ viewForHeaderInSection section: Int) -> UIView? {
199
+ guard let container = container, section < container.sections.count else { return nil }
200
+ return container.sections[section].headerView
201
+ }
202
+
203
+ func tableView(_ tableView: UITableView,
204
+ heightForHeaderInSection section: Int) -> CGFloat {
205
+ guard let container = container, section < container.sections.count,
206
+ let header = container.sections[section].headerView else { return 0 }
207
+ let h = header.frame.size.height
208
+ return h > 1 ? h : container.estimatedItemHeight
209
+ }
210
+
211
+ func tableView(_ tableView: UITableView,
212
+ estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
213
+ container?.estimatedItemHeight ?? 44
214
+ }
215
+
216
+ func tableView(_ tableView: UITableView,
217
+ heightForRowAt indexPath: IndexPath) -> CGFloat {
218
+ guard let container = container,
219
+ indexPath.section < container.sections.count,
220
+ indexPath.row < container.sections[indexPath.section].itemViews.count else {
221
+ return container?.estimatedItemHeight ?? 44
222
+ }
223
+ let h = container.sections[indexPath.section].itemViews[indexPath.row].frame.size.height
224
+ return h > 1 ? h : (container?.estimatedItemHeight ?? 44)
225
+ }
226
+
227
+ func scrollViewDidScroll(_ scrollView: UIScrollView) {
228
+ guard let container = container else { return }
229
+ let offset = scrollView.contentOffset
230
+ container.onScroll?(["x": Double(offset.x), "y": Double(offset.y)])
231
+
232
+ let contentH = scrollView.contentSize.height
233
+ let frameH = scrollView.frame.size.height
234
+ guard contentH > frameH else { return }
235
+ let distanceFromBottom = contentH - frameH - offset.y
236
+ let threshold = frameH * 0.2
237
+
238
+ if distanceFromBottom < threshold && !container.firedEndReached {
239
+ container.firedEndReached = true
240
+ container.onEndReached?(nil)
241
+ } else if distanceFromBottom >= threshold {
242
+ container.firedEndReached = false
243
+ }
244
+ }
245
+ }
246
+
247
+ // Reuses VListCell from VListFactory (same module)
248
+ #endif