@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.
- package/dist/cli.js +43 -23
- package/native/android/README.md +205 -0
- package/native/android/VueNativeCore/build.gradle.kts +100 -0
- package/native/android/VueNativeCore/consumer-rules.pro +12 -0
- package/native/android/VueNativeCore/proguard-rules.pro +33 -0
- package/native/android/VueNativeCore/src/main/AndroidManifest.xml +17 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Bridge/ErrorOverlayView.kt +94 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Bridge/HotReloadManager.kt +105 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Bridge/JSPolyfills.kt +652 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Bridge/JSRuntime.kt +207 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Bridge/NativeBridge.kt +417 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/ComponentRegistry.kt +76 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VActionSheetFactory.kt +78 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VActivityIndicatorFactory.kt +46 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VAlertDialogFactory.kt +84 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VButtonFactory.kt +73 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VCheckboxFactory.kt +93 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VDropdownFactory.kt +125 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VImageFactory.kt +75 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VInputFactory.kt +210 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VKeyboardAvoidingFactory.kt +31 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VListFactory.kt +183 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VModalFactory.kt +105 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VPickerFactory.kt +57 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VPressableFactory.kt +109 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VProgressBarFactory.kt +43 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VRadioFactory.kt +103 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VRefreshControlFactory.kt +73 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VRootFactory.kt +39 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VSafeAreaFactory.kt +48 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VScrollViewFactory.kt +105 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VSectionListFactory.kt +144 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VSegmentedControlFactory.kt +77 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VSliderFactory.kt +74 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VStatusBarFactory.kt +52 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VSwitchFactory.kt +62 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VTextFactory.kt +53 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VVideoFactory.kt +191 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VViewFactory.kt +48 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VWebViewFactory.kt +90 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/NativeComponentFactory.kt +40 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/VTextNodeView.kt +23 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Helpers/GestureHelper.kt +16 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Helpers/TouchableView.kt +105 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/AnimationModule.kt +292 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/AppStateModule.kt +41 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/AsyncStorageModule.kt +59 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/AudioModule.kt +331 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/BackgroundTaskModule.kt +166 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/BiometryModule.kt +56 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/BluetoothModule.kt +302 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/CalendarModule.kt +198 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/CameraModule.kt +64 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/ClipboardModule.kt +36 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/ContactsModule.kt +288 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/DatabaseModule.kt +229 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/DeviceInfoModule.kt +39 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/FileSystemModule.kt +193 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/GeolocationModule.kt +68 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/HapticsModule.kt +61 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/HttpModule.kt +111 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/IAPModule.kt +302 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/KeyboardModule.kt +26 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/LinkingModule.kt +43 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/NativeModule.kt +27 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/NativeModuleRegistry.kt +92 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/NetworkModule.kt +75 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/NotificationsModule.kt +181 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/OTAModule.kt +255 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/PerformanceModule.kt +147 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/PermissionsModule.kt +126 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/SecureStorageModule.kt +51 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/SensorsModule.kt +134 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/ShareModule.kt +36 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/SocialAuthModule.kt +160 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/WebSocketModule.kt +155 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Styling/StyleEngine.kt +802 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Tags.kt +43 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/VueNativeActivity.kt +169 -0
- package/native/android/VueNativeCore/src/main/res/values/ids.xml +8 -0
- package/native/android/app/build.gradle.kts +45 -0
- package/native/android/app/proguard-rules.pro +5 -0
- package/native/android/app/src/main/AndroidManifest.xml +25 -0
- package/native/android/app/src/main/assets/.gitkeep +0 -0
- package/native/android/app/src/main/kotlin/com/vuenative/example/counter/MainActivity.kt +14 -0
- package/native/android/app/src/main/res/layout/activity_main.xml +6 -0
- package/native/android/app/src/main/res/values/strings.xml +3 -0
- package/native/android/app/src/main/res/values/themes.xml +9 -0
- package/native/android/app/src/main/res/xml/network_security_config.xml +8 -0
- package/native/android/build.gradle.kts +6 -0
- package/native/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/native/android/gradle/wrapper/gradle-wrapper.properties +7 -0
- package/native/android/gradle.properties +4 -0
- package/native/android/gradlew +87 -0
- package/native/android/gradlew.bat +48 -0
- package/native/android/settings.gradle.kts +20 -0
- package/native/ios/VueNativeCore/Package.resolved +23 -0
- package/native/ios/VueNativeCore/Package.swift +32 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/CertificatePinning.swift +132 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/ErrorOverlayView.swift +92 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/HotReloadManager.swift +147 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/JSPolyfills.swift +711 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/JSRuntime.swift +421 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/NativeBridge.swift +891 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/VueNativeViewController.swift +88 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/ComponentRegistry.swift +193 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VActionSheetFactory.swift +91 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VActivityIndicatorFactory.swift +74 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VAlertDialogFactory.swift +150 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VButtonFactory.swift +93 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VCheckboxFactory.swift +114 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VDropdownFactory.swift +112 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VImageFactory.swift +172 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VInputFactory.swift +357 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VKeyboardAvoidingFactory.swift +99 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VListFactory.swift +250 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VModalFactory.swift +112 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VPickerFactory.swift +96 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VPressableFactory.swift +168 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VProgressBarFactory.swift +39 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VRadioFactory.swift +167 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VRefreshControlFactory.swift +153 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VSafeAreaFactory.swift +56 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VScrollViewFactory.swift +240 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VSectionListFactory.swift +248 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VSegmentedControlFactory.swift +73 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VSliderFactory.swift +63 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VStatusBarFactory.swift +50 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VSwitchFactory.swift +108 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VTextFactory.swift +290 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VVideoFactory.swift +246 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VViewFactory.swift +157 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VWebViewFactory.swift +172 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/NativeComponentFactory.swift +53 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Helpers/GestureWrapper.swift +107 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Helpers/TouchableView.swift +136 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Helpers/UIColor+Hex.swift +80 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/AnimationModule.swift +291 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/AppStateModule.swift +65 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/AsyncStorageModule.swift +68 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/AudioModule.swift +366 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/BackgroundTaskModule.swift +135 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/BiometryModule.swift +61 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/BluetoothModule.swift +387 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/CalendarModule.swift +161 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/CameraModule.swift +318 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/ClipboardModule.swift +33 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/ContactsModule.swift +173 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/DatabaseModule.swift +259 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/DeviceInfoModule.swift +34 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/FileSystemModule.swift +233 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/GeolocationModule.swift +147 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/HapticsModule.swift +50 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/IAPModule.swift +194 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/KeyboardModule.swift +31 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/LinkingModule.swift +42 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/NativeModule.swift +28 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/NativeModuleRegistry.swift +78 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/NetworkModule.swift +62 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/NotificationsModule.swift +215 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/OTAModule.swift +281 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/PerformanceModule.swift +138 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/PermissionsModule.swift +190 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/SecureStorageModule.swift +118 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/SensorsModule.swift +103 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/ShareModule.swift +49 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/SocialAuthModule.swift +240 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/WebSocketModule.swift +213 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Resources/vue-native-placeholder.js +8 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Styling/StyleEngine.swift +885 -0
- package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/JSRuntimeTests.swift +362 -0
- package/package.json +3 -2
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
package com.vuenative.core
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.view.MotionEvent
|
|
5
|
+
import android.view.View
|
|
6
|
+
import android.view.ViewGroup
|
|
7
|
+
import com.google.android.flexbox.FlexboxLayout
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Factory for VPressable — a generic pressable container component.
|
|
11
|
+
*
|
|
12
|
+
* Like VButton but without built-in text/label support. Uses PressableView,
|
|
13
|
+
* a subclass of TouchableView that adds pressIn/pressOut callbacks.
|
|
14
|
+
*/
|
|
15
|
+
class VPressableFactory : NativeComponentFactory {
|
|
16
|
+
|
|
17
|
+
override fun createView(context: Context): View {
|
|
18
|
+
return PressableView(context)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
override fun updateProp(view: View, key: String, value: Any?) {
|
|
22
|
+
val pressable = view as? PressableView
|
|
23
|
+
if (pressable == null) {
|
|
24
|
+
StyleEngine.apply(key, value, view)
|
|
25
|
+
return
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
when (key) {
|
|
29
|
+
"disabled" -> {
|
|
30
|
+
pressable.isDisabled = value == true || value == "true" ||
|
|
31
|
+
(value is Number && value.toInt() != 0)
|
|
32
|
+
}
|
|
33
|
+
"activeOpacity" -> {
|
|
34
|
+
pressable.activeOpacity = when (value) {
|
|
35
|
+
is Double -> value.toFloat()
|
|
36
|
+
is Float -> value
|
|
37
|
+
is Int -> value.toFloat()
|
|
38
|
+
is String -> value.toFloatOrNull() ?: 0.7f
|
|
39
|
+
else -> 0.7f
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
else -> StyleEngine.apply(key, value, view)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
override fun addEventListener(view: View, event: String, handler: (Any?) -> Unit) {
|
|
47
|
+
val pressable = view as? PressableView ?: return
|
|
48
|
+
|
|
49
|
+
when (event) {
|
|
50
|
+
"press" -> pressable.onPress = { handler(null) }
|
|
51
|
+
"longPress", "longpress" -> pressable.onLongPress = { handler(null) }
|
|
52
|
+
"pressIn", "pressin" -> pressable.onPressIn = { handler(null) }
|
|
53
|
+
"pressOut", "pressout" -> pressable.onPressOut = { handler(null) }
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
override fun removeEventListener(view: View, event: String) {
|
|
58
|
+
val pressable = view as? PressableView ?: return
|
|
59
|
+
|
|
60
|
+
when (event) {
|
|
61
|
+
"press" -> pressable.onPress = null
|
|
62
|
+
"longPress", "longpress" -> pressable.onLongPress = null
|
|
63
|
+
"pressIn", "pressin" -> pressable.onPressIn = null
|
|
64
|
+
"pressOut", "pressout" -> pressable.onPressOut = null
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
override fun insertChild(parent: View, child: View, index: Int) {
|
|
69
|
+
val flex = parent as? FlexboxLayout ?: return
|
|
70
|
+
val lp = StyleEngine.buildFlexLayoutParams(child)
|
|
71
|
+
if (index >= flex.childCount) flex.addView(child, lp)
|
|
72
|
+
else flex.addView(child, index, lp)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
override fun removeChild(parent: View, child: View) {
|
|
76
|
+
(parent as? FlexboxLayout)?.removeView(child)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Extension of TouchableView that adds pressIn and pressOut callbacks.
|
|
82
|
+
* Fires pressIn when a touch begins and pressOut when the touch ends or is cancelled.
|
|
83
|
+
*/
|
|
84
|
+
class PressableView(context: android.content.Context) : TouchableView(context) {
|
|
85
|
+
|
|
86
|
+
/** Called when a touch begins inside the view bounds. */
|
|
87
|
+
var onPressIn: (() -> Unit)? = null
|
|
88
|
+
|
|
89
|
+
/** Called when a touch ends or is cancelled. */
|
|
90
|
+
var onPressOut: (() -> Unit)? = null
|
|
91
|
+
|
|
92
|
+
override fun onTouchEvent(event: MotionEvent): Boolean {
|
|
93
|
+
if (isDisabled) return false
|
|
94
|
+
|
|
95
|
+
when (event.action) {
|
|
96
|
+
MotionEvent.ACTION_DOWN -> {
|
|
97
|
+
onPressIn?.invoke()
|
|
98
|
+
}
|
|
99
|
+
MotionEvent.ACTION_UP -> {
|
|
100
|
+
onPressOut?.invoke()
|
|
101
|
+
}
|
|
102
|
+
MotionEvent.ACTION_CANCEL -> {
|
|
103
|
+
onPressOut?.invoke()
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return super.onTouchEvent(event)
|
|
108
|
+
}
|
|
109
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
package com.vuenative.core
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.view.View
|
|
5
|
+
import android.view.ViewGroup
|
|
6
|
+
import android.widget.ProgressBar
|
|
7
|
+
|
|
8
|
+
class VProgressBarFactory : NativeComponentFactory {
|
|
9
|
+
override fun createView(context: Context): View {
|
|
10
|
+
return ProgressBar(context, null, android.R.attr.progressBarStyleHorizontal).apply {
|
|
11
|
+
isIndeterminate = false
|
|
12
|
+
max = 1000
|
|
13
|
+
progress = 0
|
|
14
|
+
layoutParams = ViewGroup.LayoutParams(
|
|
15
|
+
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
16
|
+
ViewGroup.LayoutParams.WRAP_CONTENT
|
|
17
|
+
)
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
override fun updateProp(view: View, key: String, value: Any?) {
|
|
22
|
+
val pb = view as? ProgressBar ?: return
|
|
23
|
+
when (key) {
|
|
24
|
+
"progress" -> {
|
|
25
|
+
val f = StyleEngine.toFloat(value, 0f).coerceIn(0f, 1f)
|
|
26
|
+
pb.progress = (f * pb.max).toInt()
|
|
27
|
+
}
|
|
28
|
+
"progressTintColor" -> {
|
|
29
|
+
val color = StyleEngine.parseColor(value) ?: return
|
|
30
|
+
pb.progressTintList = android.content.res.ColorStateList.valueOf(color)
|
|
31
|
+
}
|
|
32
|
+
"trackTintColor" -> {
|
|
33
|
+
val color = StyleEngine.parseColor(value) ?: return
|
|
34
|
+
pb.progressBackgroundTintList = android.content.res.ColorStateList.valueOf(color)
|
|
35
|
+
}
|
|
36
|
+
"animated" -> {} // Android ProgressBar animates by default
|
|
37
|
+
else -> StyleEngine.apply(key, value, view)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
override fun addEventListener(view: View, event: String, handler: (Any?) -> Unit) {}
|
|
42
|
+
override fun removeEventListener(view: View, event: String) {}
|
|
43
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
package com.vuenative.core
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.view.View
|
|
5
|
+
import android.view.ViewGroup
|
|
6
|
+
import android.widget.RadioButton
|
|
7
|
+
import android.widget.RadioGroup
|
|
8
|
+
import org.json.JSONArray
|
|
9
|
+
|
|
10
|
+
class VRadioFactory : NativeComponentFactory {
|
|
11
|
+
private val changeHandlers = mutableMapOf<RadioGroup, (Any?) -> Unit>()
|
|
12
|
+
private val optionValues = mutableMapOf<RadioGroup, List<String>>()
|
|
13
|
+
|
|
14
|
+
override fun createView(context: Context): View {
|
|
15
|
+
return RadioGroup(context).apply {
|
|
16
|
+
orientation = RadioGroup.VERTICAL
|
|
17
|
+
layoutParams = ViewGroup.LayoutParams(
|
|
18
|
+
ViewGroup.LayoutParams.WRAP_CONTENT,
|
|
19
|
+
ViewGroup.LayoutParams.WRAP_CONTENT
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
override fun updateProp(view: View, key: String, value: Any?) {
|
|
25
|
+
val rg = view as? RadioGroup ?: return
|
|
26
|
+
when (key) {
|
|
27
|
+
"options" -> {
|
|
28
|
+
val items = when (value) {
|
|
29
|
+
is JSONArray -> (0 until value.length()).map { i ->
|
|
30
|
+
val obj = value.getJSONObject(i)
|
|
31
|
+
Pair(obj.optString("label", ""), obj.optString("value", ""))
|
|
32
|
+
}
|
|
33
|
+
is List<*> -> value.filterIsInstance<Map<*, *>>().map { m ->
|
|
34
|
+
Pair(m["label"]?.toString() ?: "", m["value"]?.toString() ?: "")
|
|
35
|
+
}
|
|
36
|
+
else -> emptyList()
|
|
37
|
+
}
|
|
38
|
+
optionValues[rg] = items.map { it.second }
|
|
39
|
+
rg.removeAllViews()
|
|
40
|
+
items.forEachIndexed { i, (label, _) ->
|
|
41
|
+
val btn = RadioButton(rg.context).apply {
|
|
42
|
+
text = label
|
|
43
|
+
id = View.generateViewId()
|
|
44
|
+
setPadding(0, 16, 24, 16)
|
|
45
|
+
}
|
|
46
|
+
rg.addView(btn)
|
|
47
|
+
}
|
|
48
|
+
// Re-apply selection
|
|
49
|
+
applySelection(rg)
|
|
50
|
+
}
|
|
51
|
+
"selectedValue" -> {
|
|
52
|
+
rg.tag = value?.toString()
|
|
53
|
+
applySelection(rg)
|
|
54
|
+
}
|
|
55
|
+
"disabled" -> {
|
|
56
|
+
val disabled = value == true || value == "true"
|
|
57
|
+
rg.isEnabled = !disabled
|
|
58
|
+
rg.alpha = if (disabled) 0.4f else 1.0f
|
|
59
|
+
(0 until rg.childCount).forEach {
|
|
60
|
+
rg.getChildAt(it).isEnabled = !disabled
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
"tintColor" -> {
|
|
64
|
+
val color = StyleEngine.parseColor(value) ?: return
|
|
65
|
+
(0 until rg.childCount).forEach { i ->
|
|
66
|
+
(rg.getChildAt(i) as? RadioButton)?.buttonTintList =
|
|
67
|
+
android.content.res.ColorStateList.valueOf(color)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
else -> StyleEngine.apply(key, value, view)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
override fun addEventListener(view: View, event: String, handler: (Any?) -> Unit) {
|
|
75
|
+
val rg = view as? RadioGroup ?: return
|
|
76
|
+
if (event == "change") {
|
|
77
|
+
changeHandlers[rg] = handler
|
|
78
|
+
rg.setOnCheckedChangeListener { group, checkedId ->
|
|
79
|
+
val idx = (0 until group.childCount).indexOfFirst { group.getChildAt(it).id == checkedId }
|
|
80
|
+
val value = optionValues[group]?.getOrNull(idx) ?: ""
|
|
81
|
+
changeHandlers[group]?.invoke(mapOf("value" to value))
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
override fun removeEventListener(view: View, event: String) {
|
|
87
|
+
val rg = view as? RadioGroup ?: return
|
|
88
|
+
if (event == "change") {
|
|
89
|
+
changeHandlers.remove(rg)
|
|
90
|
+
rg.setOnCheckedChangeListener(null)
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
private fun applySelection(rg: RadioGroup) {
|
|
95
|
+
val selected = rg.tag as? String ?: return
|
|
96
|
+
val values = optionValues[rg] ?: return
|
|
97
|
+
val idx = values.indexOf(selected)
|
|
98
|
+
if (idx >= 0 && idx < rg.childCount) {
|
|
99
|
+
val child = rg.getChildAt(idx) as? RadioButton
|
|
100
|
+
child?.id?.let { rg.check(it) }
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
package com.vuenative.core
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.graphics.Color
|
|
5
|
+
import android.view.View
|
|
6
|
+
import android.view.ViewGroup
|
|
7
|
+
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Factory for VRefreshControl — pull-to-refresh indicator.
|
|
11
|
+
*
|
|
12
|
+
* Creates a SwipeRefreshLayout that wraps its children. When used as a child
|
|
13
|
+
* of VScrollView, the bridge should attach this to the parent's refresh mechanism.
|
|
14
|
+
* For standalone usage, this provides its own SwipeRefreshLayout.
|
|
15
|
+
*/
|
|
16
|
+
class VRefreshControlFactory : NativeComponentFactory {
|
|
17
|
+
|
|
18
|
+
override fun createView(context: Context): View {
|
|
19
|
+
// Create a zero-sized invisible view as a marker.
|
|
20
|
+
// The actual refresh behavior is wired by the parent VScrollView.
|
|
21
|
+
val marker = View(context).apply {
|
|
22
|
+
visibility = View.GONE
|
|
23
|
+
layoutParams = ViewGroup.LayoutParams(0, 0)
|
|
24
|
+
}
|
|
25
|
+
return marker
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
override fun updateProp(view: View, key: String, value: Any?) {
|
|
29
|
+
// VRefreshControl props are handled by the parent VScrollView.
|
|
30
|
+
// The bridge forwards refreshing/tintColor to the parent's SwipeRefreshLayout.
|
|
31
|
+
val parent = view.parent as? SwipeRefreshLayout
|
|
32
|
+
when (key) {
|
|
33
|
+
"refreshing" -> {
|
|
34
|
+
parent?.isRefreshing = value == true || value == "true"
|
|
35
|
+
}
|
|
36
|
+
"tintColor" -> {
|
|
37
|
+
val colorStr = value as? String ?: return
|
|
38
|
+
try {
|
|
39
|
+
val color = Color.parseColor(colorStr)
|
|
40
|
+
parent?.setColorSchemeColors(color)
|
|
41
|
+
} catch (_: IllegalArgumentException) {
|
|
42
|
+
// Ignore invalid color strings
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// "title" is iOS-only (UIRefreshControl attributedTitle)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
override fun addEventListener(view: View, event: String, handler: (Any?) -> Unit) {
|
|
50
|
+
if (event == "refresh") {
|
|
51
|
+
// Store handler on the view for the parent factory to wire up
|
|
52
|
+
view.setTag(TAG_EVENT_HANDLER, handler)
|
|
53
|
+
|
|
54
|
+
// If already inside a SwipeRefreshLayout, wire it up directly
|
|
55
|
+
val parent = view.parent as? SwipeRefreshLayout
|
|
56
|
+
if (parent != null) {
|
|
57
|
+
parent.isEnabled = true
|
|
58
|
+
parent.setOnRefreshListener { handler(null) }
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
override fun removeEventListener(view: View, event: String) {
|
|
64
|
+
if (event == "refresh") {
|
|
65
|
+
view.setTag(TAG_EVENT_HANDLER, null)
|
|
66
|
+
val parent = view.parent as? SwipeRefreshLayout
|
|
67
|
+
if (parent != null) {
|
|
68
|
+
parent.setOnRefreshListener(null)
|
|
69
|
+
parent.isEnabled = false
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
package com.vuenative.core
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.view.View
|
|
5
|
+
import com.google.android.flexbox.FlexDirection
|
|
6
|
+
import com.google.android.flexbox.FlexboxLayout
|
|
7
|
+
|
|
8
|
+
class VRootFactory : NativeComponentFactory {
|
|
9
|
+
override fun createView(context: Context): View {
|
|
10
|
+
return FlexboxLayout(context).apply {
|
|
11
|
+
flexDirection = FlexDirection.COLUMN
|
|
12
|
+
layoutParams = android.view.ViewGroup.LayoutParams(
|
|
13
|
+
android.view.ViewGroup.LayoutParams.MATCH_PARENT,
|
|
14
|
+
android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
|
15
|
+
)
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
override fun updateProp(view: View, key: String, value: Any?) {
|
|
20
|
+
StyleEngine.apply(key, value, view)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
override fun addEventListener(view: View, event: String, handler: (Any?) -> Unit) {
|
|
24
|
+
if (event == "press") {
|
|
25
|
+
view.setOnClickListener { handler(null) }
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
override fun removeEventListener(view: View, event: String) {
|
|
30
|
+
if (event == "press") view.setOnClickListener(null)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
override fun insertChild(parent: View, child: View, index: Int) {
|
|
34
|
+
val flex = parent as? FlexboxLayout ?: return
|
|
35
|
+
val lp = StyleEngine.buildFlexLayoutParams(child)
|
|
36
|
+
if (index >= flex.childCount) flex.addView(child, lp)
|
|
37
|
+
else flex.addView(child, index, lp)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
package com.vuenative.core
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.os.Build
|
|
5
|
+
import android.view.View
|
|
6
|
+
import android.view.ViewGroup
|
|
7
|
+
import android.view.WindowInsets
|
|
8
|
+
import com.google.android.flexbox.FlexDirection
|
|
9
|
+
import com.google.android.flexbox.FlexboxLayout
|
|
10
|
+
|
|
11
|
+
class VSafeAreaFactory : NativeComponentFactory {
|
|
12
|
+
override fun createView(context: Context): View {
|
|
13
|
+
val flex = FlexboxLayout(context).apply {
|
|
14
|
+
flexDirection = FlexDirection.COLUMN
|
|
15
|
+
layoutParams = ViewGroup.LayoutParams(
|
|
16
|
+
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
17
|
+
ViewGroup.LayoutParams.MATCH_PARENT
|
|
18
|
+
)
|
|
19
|
+
}
|
|
20
|
+
// Apply safe area insets
|
|
21
|
+
flex.setOnApplyWindowInsetsListener { v, insets ->
|
|
22
|
+
val top = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
23
|
+
insets.getInsets(WindowInsets.Type.systemBars()).top
|
|
24
|
+
} else {
|
|
25
|
+
@Suppress("DEPRECATION")
|
|
26
|
+
insets.systemWindowInsetTop
|
|
27
|
+
}
|
|
28
|
+
val bottom = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
29
|
+
insets.getInsets(WindowInsets.Type.systemBars()).bottom
|
|
30
|
+
} else {
|
|
31
|
+
@Suppress("DEPRECATION")
|
|
32
|
+
insets.systemWindowInsetBottom
|
|
33
|
+
}
|
|
34
|
+
v.setPadding(v.paddingLeft, top, v.paddingRight, bottom)
|
|
35
|
+
insets
|
|
36
|
+
}
|
|
37
|
+
return flex
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
override fun updateProp(view: View, key: String, value: Any?) = StyleEngine.apply(key, value, view)
|
|
41
|
+
override fun addEventListener(view: View, event: String, handler: (Any?) -> Unit) {}
|
|
42
|
+
override fun removeEventListener(view: View, event: String) {}
|
|
43
|
+
override fun insertChild(parent: View, child: View, index: Int) {
|
|
44
|
+
val flex = parent as? FlexboxLayout ?: return
|
|
45
|
+
val lp = StyleEngine.buildFlexLayoutParams(child)
|
|
46
|
+
if (index >= flex.childCount) flex.addView(child, lp) else flex.addView(child, index, lp)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
package com.vuenative.core
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.view.View
|
|
5
|
+
import android.view.ViewGroup
|
|
6
|
+
import android.widget.ScrollView
|
|
7
|
+
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
|
8
|
+
import com.google.android.flexbox.FlexDirection
|
|
9
|
+
import com.google.android.flexbox.FlexboxLayout
|
|
10
|
+
|
|
11
|
+
class VScrollViewFactory : NativeComponentFactory {
|
|
12
|
+
|
|
13
|
+
// Tracks the inner ScrollView and content FlexboxLayout per SwipeRefreshLayout root
|
|
14
|
+
private data class ScrollState(val scrollView: ScrollView, val content: FlexboxLayout)
|
|
15
|
+
private val states = mutableMapOf<SwipeRefreshLayout, ScrollState>()
|
|
16
|
+
|
|
17
|
+
override fun createView(context: Context): View {
|
|
18
|
+
val content = FlexboxLayout(context).apply {
|
|
19
|
+
flexDirection = FlexDirection.COLUMN
|
|
20
|
+
layoutParams = ViewGroup.LayoutParams(
|
|
21
|
+
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
22
|
+
ViewGroup.LayoutParams.WRAP_CONTENT
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
val scroll = ScrollView(context).apply {
|
|
26
|
+
isFillViewport = true
|
|
27
|
+
layoutParams = ViewGroup.LayoutParams(
|
|
28
|
+
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
29
|
+
ViewGroup.LayoutParams.MATCH_PARENT
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
scroll.addView(content)
|
|
33
|
+
|
|
34
|
+
val srf = SwipeRefreshLayout(context).apply {
|
|
35
|
+
layoutParams = ViewGroup.LayoutParams(
|
|
36
|
+
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
37
|
+
ViewGroup.LayoutParams.MATCH_PARENT
|
|
38
|
+
)
|
|
39
|
+
// Disabled by default; enabled when @refresh listener is added
|
|
40
|
+
isEnabled = false
|
|
41
|
+
}
|
|
42
|
+
srf.addView(scroll)
|
|
43
|
+
states[srf] = ScrollState(scroll, content)
|
|
44
|
+
return srf
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
override fun updateProp(view: View, key: String, value: Any?) {
|
|
48
|
+
val srf = view as? SwipeRefreshLayout ?: return
|
|
49
|
+
val scroll = states[srf]?.scrollView
|
|
50
|
+
when (key) {
|
|
51
|
+
"refreshing" -> {
|
|
52
|
+
srf.isRefreshing = value == true || value == "true"
|
|
53
|
+
}
|
|
54
|
+
"showsVerticalScrollIndicator" -> {
|
|
55
|
+
scroll?.isVerticalScrollBarEnabled = value != false && value != "false"
|
|
56
|
+
}
|
|
57
|
+
"showsHorizontalScrollIndicator" -> {
|
|
58
|
+
scroll?.isHorizontalScrollBarEnabled = value != false && value != "false"
|
|
59
|
+
}
|
|
60
|
+
"scrollEnabled" -> {
|
|
61
|
+
// ScrollView doesn't expose a direct enable toggle; no-op
|
|
62
|
+
}
|
|
63
|
+
else -> scroll?.let { StyleEngine.apply(key, value, it) }
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
override fun addEventListener(view: View, event: String, handler: (Any?) -> Unit) {
|
|
68
|
+
val srf = view as? SwipeRefreshLayout ?: return
|
|
69
|
+
val scroll = states[srf]?.scrollView
|
|
70
|
+
when (event) {
|
|
71
|
+
"refresh" -> {
|
|
72
|
+
srf.isEnabled = true
|
|
73
|
+
srf.setOnRefreshListener { handler(null) }
|
|
74
|
+
}
|
|
75
|
+
"scroll" -> {
|
|
76
|
+
scroll?.viewTreeObserver?.addOnScrollChangedListener {
|
|
77
|
+
handler(mapOf(
|
|
78
|
+
"contentOffset" to mapOf("x" to scroll.scrollX, "y" to scroll.scrollY)
|
|
79
|
+
))
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
override fun removeEventListener(view: View, event: String) {
|
|
86
|
+
val srf = view as? SwipeRefreshLayout ?: return
|
|
87
|
+
if (event == "refresh") {
|
|
88
|
+
srf.setOnRefreshListener(null)
|
|
89
|
+
srf.isEnabled = false
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
override fun insertChild(parent: View, child: View, index: Int) {
|
|
94
|
+
val srf = parent as? SwipeRefreshLayout ?: return
|
|
95
|
+
val content = states[srf]?.content ?: return
|
|
96
|
+
val lp = StyleEngine.buildFlexLayoutParams(child)
|
|
97
|
+
if (index >= content.childCount) content.addView(child, lp)
|
|
98
|
+
else content.addView(child, index, lp)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
override fun removeChild(parent: View, child: View) {
|
|
102
|
+
val srf = parent as? SwipeRefreshLayout ?: return
|
|
103
|
+
states[srf]?.content?.removeView(child)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
package com.vuenative.core
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.view.View
|
|
5
|
+
import android.view.ViewGroup
|
|
6
|
+
import androidx.recyclerview.widget.LinearLayoutManager
|
|
7
|
+
import androidx.recyclerview.widget.RecyclerView
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Factory for VSectionList — a sectioned list backed by RecyclerView.
|
|
11
|
+
* Children marked with the `__sectionHeader` internal prop are treated as section headers.
|
|
12
|
+
* All other children are regular items grouped under the preceding header.
|
|
13
|
+
*/
|
|
14
|
+
class VSectionListFactory : NativeComponentFactory {
|
|
15
|
+
private val childViews = mutableMapOf<RecyclerView, MutableList<View>>()
|
|
16
|
+
private val scrollHandlers = mutableMapOf<RecyclerView, (Any?) -> Unit>()
|
|
17
|
+
private val endReachedHandlers = mutableMapOf<RecyclerView, (Any?) -> Unit>()
|
|
18
|
+
private val scrollListeners = mutableMapOf<RecyclerView, RecyclerView.OnScrollListener>()
|
|
19
|
+
private val firedEndReached = mutableMapOf<RecyclerView, Boolean>()
|
|
20
|
+
|
|
21
|
+
override fun createView(context: Context): View {
|
|
22
|
+
val rv = RecyclerView(context).apply {
|
|
23
|
+
layoutManager = LinearLayoutManager(context)
|
|
24
|
+
layoutParams = ViewGroup.LayoutParams(
|
|
25
|
+
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
26
|
+
ViewGroup.LayoutParams.MATCH_PARENT
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
childViews[rv] = mutableListOf()
|
|
30
|
+
firedEndReached[rv] = false
|
|
31
|
+
rv.adapter = VSectionListAdapter(childViews[rv]!!)
|
|
32
|
+
return rv
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
override fun updateProp(view: View, key: String, value: Any?) {
|
|
36
|
+
val rv = view as? RecyclerView ?: return
|
|
37
|
+
when (key) {
|
|
38
|
+
"bounces" -> {
|
|
39
|
+
rv.overScrollMode = if (value == false || value == "false")
|
|
40
|
+
View.OVER_SCROLL_NEVER else View.OVER_SCROLL_ALWAYS
|
|
41
|
+
}
|
|
42
|
+
"showsScrollIndicator" -> {
|
|
43
|
+
val show = value != false && value != "false"
|
|
44
|
+
rv.isVerticalScrollBarEnabled = show
|
|
45
|
+
}
|
|
46
|
+
"stickySectionHeaders" -> {
|
|
47
|
+
// RecyclerView sticky headers would require an ItemDecoration;
|
|
48
|
+
// for now we store the preference but don't implement sticky behavior
|
|
49
|
+
}
|
|
50
|
+
"estimatedItemHeight" -> { /* Used for initial sizing hints */ }
|
|
51
|
+
else -> StyleEngine.apply(key, value, view)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
override fun addEventListener(view: View, event: String, handler: (Any?) -> Unit) {
|
|
56
|
+
val rv = view as? RecyclerView ?: return
|
|
57
|
+
when (event) {
|
|
58
|
+
"scroll" -> {
|
|
59
|
+
scrollHandlers[rv] = handler
|
|
60
|
+
ensureScrollListener(rv)
|
|
61
|
+
}
|
|
62
|
+
"endReached" -> {
|
|
63
|
+
endReachedHandlers[rv] = handler
|
|
64
|
+
ensureScrollListener(rv)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
override fun removeEventListener(view: View, event: String) {
|
|
70
|
+
val rv = view as? RecyclerView ?: return
|
|
71
|
+
when (event) {
|
|
72
|
+
"scroll" -> scrollHandlers.remove(rv)
|
|
73
|
+
"endReached" -> endReachedHandlers.remove(rv)
|
|
74
|
+
}
|
|
75
|
+
if (!scrollHandlers.containsKey(rv) && !endReachedHandlers.containsKey(rv)) {
|
|
76
|
+
scrollListeners.remove(rv)?.let { rv.removeOnScrollListener(it) }
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private fun ensureScrollListener(rv: RecyclerView) {
|
|
81
|
+
if (scrollListeners.containsKey(rv)) return
|
|
82
|
+
var cumulativeX = 0
|
|
83
|
+
var cumulativeY = 0
|
|
84
|
+
val listener = object : RecyclerView.OnScrollListener() {
|
|
85
|
+
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
|
86
|
+
cumulativeX += dx
|
|
87
|
+
cumulativeY += dy
|
|
88
|
+
scrollHandlers[recyclerView]?.invoke(mapOf("x" to cumulativeX, "y" to cumulativeY))
|
|
89
|
+
|
|
90
|
+
val lm = recyclerView.layoutManager as? LinearLayoutManager ?: return
|
|
91
|
+
val totalItems = lm.itemCount
|
|
92
|
+
if (totalItems == 0) return
|
|
93
|
+
val lastVisible = lm.findLastVisibleItemPosition()
|
|
94
|
+
val threshold = (totalItems * 0.2).toInt().coerceAtLeast(1)
|
|
95
|
+
|
|
96
|
+
if (lastVisible >= totalItems - threshold) {
|
|
97
|
+
if (firedEndReached[recyclerView] != true) {
|
|
98
|
+
firedEndReached[recyclerView] = true
|
|
99
|
+
endReachedHandlers[recyclerView]?.invoke(null)
|
|
100
|
+
}
|
|
101
|
+
} else {
|
|
102
|
+
firedEndReached[recyclerView] = false
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
scrollListeners[rv] = listener
|
|
107
|
+
rv.addOnScrollListener(listener)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
override fun insertChild(parent: View, child: View, index: Int) {
|
|
111
|
+
val rv = parent as? RecyclerView ?: return
|
|
112
|
+
val list = childViews[rv] ?: return
|
|
113
|
+
if (index >= list.size) list.add(child) else list.add(index, child)
|
|
114
|
+
rv.adapter?.notifyItemInserted(if (index >= list.size - 1) list.size - 1 else index)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
override fun removeChild(parent: View, child: View) {
|
|
118
|
+
val rv = parent as? RecyclerView ?: return
|
|
119
|
+
val list = childViews[rv] ?: return
|
|
120
|
+
val idx = list.indexOf(child)
|
|
121
|
+
if (idx >= 0) {
|
|
122
|
+
list.removeAt(idx)
|
|
123
|
+
rv.adapter?.notifyItemRemoved(idx)
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
private class VSectionListAdapter(private val items: List<View>) : RecyclerView.Adapter<VSectionListAdapter.VH>() {
|
|
129
|
+
class VH(val view: View) : RecyclerView.ViewHolder(view)
|
|
130
|
+
|
|
131
|
+
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
|
|
132
|
+
val v = items.getOrNull(viewType) ?: android.widget.FrameLayout(parent.context)
|
|
133
|
+
(v.parent as? ViewGroup)?.removeView(v)
|
|
134
|
+
v.layoutParams = RecyclerView.LayoutParams(
|
|
135
|
+
RecyclerView.LayoutParams.MATCH_PARENT,
|
|
136
|
+
RecyclerView.LayoutParams.WRAP_CONTENT
|
|
137
|
+
)
|
|
138
|
+
return VH(v)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
override fun onBindViewHolder(holder: VH, position: Int) {}
|
|
142
|
+
override fun getItemCount(): Int = items.size
|
|
143
|
+
override fun getItemViewType(position: Int): Int = position
|
|
144
|
+
}
|