@billdog.io/react-native 1.0.0-beta.2

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 (309) hide show
  1. package/BillDogReactNative.podspec +30 -0
  2. package/LICENSE +21 -0
  3. package/README.md +330 -0
  4. package/android/build.gradle +99 -0
  5. package/android/src/main/AndroidManifest.xml +1 -0
  6. package/android/src/main/kotlin/io/billdog/reactnative/BillDogABTestModule.kt +317 -0
  7. package/android/src/main/kotlin/io/billdog/reactnative/BillDogAnalyticsModule.kt +389 -0
  8. package/android/src/main/kotlin/io/billdog/reactnative/BillDogModule.kt +817 -0
  9. package/android/src/main/kotlin/io/billdog/reactnative/BillDogPaywallViewManager.kt +383 -0
  10. package/android/src/main/kotlin/io/billdog/reactnative/BillDogSurveyModule.kt +427 -0
  11. package/android/src/main/kotlin/io/billdog/reactnative/BillDogVirtualCurrencyModule.kt +407 -0
  12. package/android/src/main/kotlin/io/billdog/virtualcurrency/BillDogVirtualCurrencyPackage.kt +44 -0
  13. package/dist/AdvancedPaywallCache.d.ts +70 -0
  14. package/dist/AdvancedPaywallCache.js +344 -0
  15. package/dist/BillDog.d.ts +255 -0
  16. package/dist/BillDog.js +918 -0
  17. package/dist/BillDogConfiguration.d.ts +145 -0
  18. package/dist/BillDogConfiguration.js +229 -0
  19. package/dist/ComponentRenderer.d.ts +17 -0
  20. package/dist/ComponentRenderer.js +886 -0
  21. package/dist/LayoutEngine.d.ts +101 -0
  22. package/dist/LayoutEngine.js +394 -0
  23. package/dist/LocalizationResolver.d.ts +99 -0
  24. package/dist/LocalizationResolver.js +266 -0
  25. package/dist/NativePaywallView.d.ts +36 -0
  26. package/dist/NativePaywallView.js +59 -0
  27. package/dist/PaywallCache.d.ts +74 -0
  28. package/dist/PaywallCache.js +293 -0
  29. package/dist/PaywallStateManager.d.ts +62 -0
  30. package/dist/PaywallStateManager.js +147 -0
  31. package/dist/PaywallView.d.ts +42 -0
  32. package/dist/PaywallView.js +275 -0
  33. package/dist/ProductVariableProvider.d.ts +60 -0
  34. package/dist/ProductVariableProvider.js +239 -0
  35. package/dist/VariableResolver.d.ts +120 -0
  36. package/dist/VariableResolver.js +379 -0
  37. package/dist/WebViewPaywall.d.ts +75 -0
  38. package/dist/WebViewPaywall.js +360 -0
  39. package/dist/animations/presets.d.ts +49 -0
  40. package/dist/animations/presets.js +367 -0
  41. package/dist/components/AccoladeCpsComponent.d.ts +13 -0
  42. package/dist/components/AccoladeCpsComponent.js +248 -0
  43. package/dist/components/AccordionComponent.d.ts +10 -0
  44. package/dist/components/AccordionComponent.js +187 -0
  45. package/dist/components/AnimatedWrapper.d.ts +35 -0
  46. package/dist/components/AnimatedWrapper.js +277 -0
  47. package/dist/components/CarouselComponent.d.ts +23 -0
  48. package/dist/components/CarouselComponent.js +484 -0
  49. package/dist/components/ChartCurveComponent.d.ts +13 -0
  50. package/dist/components/ChartCurveComponent.js +333 -0
  51. package/dist/components/CheckboxComponent.d.ts +9 -0
  52. package/dist/components/CheckboxComponent.js +172 -0
  53. package/dist/components/ConfettiComponent.d.ts +9 -0
  54. package/dist/components/ConfettiComponent.js +172 -0
  55. package/dist/components/CountdownComponent.d.ts +16 -0
  56. package/dist/components/CountdownComponent.js +414 -0
  57. package/dist/components/DelayedXComponent.d.ts +12 -0
  58. package/dist/components/DelayedXComponent.js +240 -0
  59. package/dist/components/DrawerModalComponent.d.ts +20 -0
  60. package/dist/components/DrawerModalComponent.js +286 -0
  61. package/dist/components/GradientView.d.ts +38 -0
  62. package/dist/components/GradientView.js +126 -0
  63. package/dist/components/HapticFeedbackComponent.d.ts +16 -0
  64. package/dist/components/HapticFeedbackComponent.js +64 -0
  65. package/dist/components/HorizontalProductsComponent.d.ts +17 -0
  66. package/dist/components/HorizontalProductsComponent.js +284 -0
  67. package/dist/components/MultiChoiceGridComponent.d.ts +13 -0
  68. package/dist/components/MultiChoiceGridComponent.js +234 -0
  69. package/dist/components/NPSScaleComponent.d.ts +16 -0
  70. package/dist/components/NPSScaleComponent.js +260 -0
  71. package/dist/components/NavigationComponent.d.ts +26 -0
  72. package/dist/components/NavigationComponent.js +302 -0
  73. package/dist/components/OAuthButtonComponent.d.ts +20 -0
  74. package/dist/components/OAuthButtonComponent.js +237 -0
  75. package/dist/components/ProgressLoaderComponent.d.ts +13 -0
  76. package/dist/components/ProgressLoaderComponent.js +308 -0
  77. package/dist/components/ProgressStepperComponent.d.ts +18 -0
  78. package/dist/components/ProgressStepperComponent.js +228 -0
  79. package/dist/components/SegmentedPickerComponent.d.ts +10 -0
  80. package/dist/components/SegmentedPickerComponent.js +221 -0
  81. package/dist/components/ShareButtonsComponent.d.ts +21 -0
  82. package/dist/components/ShareButtonsComponent.js +265 -0
  83. package/dist/components/ShareCodeComponent.d.ts +16 -0
  84. package/dist/components/ShareCodeComponent.js +274 -0
  85. package/dist/components/SingleChoiceGridComponent.d.ts +13 -0
  86. package/dist/components/SingleChoiceGridComponent.js +210 -0
  87. package/dist/components/SliderComponent.d.ts +9 -0
  88. package/dist/components/SliderComponent.js +263 -0
  89. package/dist/components/StarRatingInputComponent.d.ts +20 -0
  90. package/dist/components/StarRatingInputComponent.js +198 -0
  91. package/dist/components/StickerCompositeComponent.d.ts +13 -0
  92. package/dist/components/StickerCompositeComponent.js +136 -0
  93. package/dist/components/TabsComponent.d.ts +19 -0
  94. package/dist/components/TabsComponent.js +431 -0
  95. package/dist/components/TextInputComponent.d.ts +18 -0
  96. package/dist/components/TextInputComponent.js +227 -0
  97. package/dist/components/TimelineComponent.d.ts +19 -0
  98. package/dist/components/TimelineComponent.js +104 -0
  99. package/dist/components/ToggleComponent.d.ts +14 -0
  100. package/dist/components/ToggleComponent.js +132 -0
  101. package/dist/components/VerticalProductsComponent.d.ts +17 -0
  102. package/dist/components/VerticalProductsComponent.js +311 -0
  103. package/dist/dynamicValues.d.ts +36 -0
  104. package/dist/dynamicValues.js +142 -0
  105. package/dist/filters/dateFilters.d.ts +50 -0
  106. package/dist/filters/dateFilters.js +390 -0
  107. package/dist/filters/index.d.ts +18 -0
  108. package/dist/filters/index.js +253 -0
  109. package/dist/hooks/useOfferings.d.ts +76 -0
  110. package/dist/hooks/useOfferings.js +99 -0
  111. package/dist/hooks/usePaywall.d.ts +27 -0
  112. package/dist/hooks/usePaywall.js +114 -0
  113. package/dist/hooks/usePlacement.d.ts +34 -0
  114. package/dist/hooks/usePlacement.js +89 -0
  115. package/dist/hooks/useSubscription.d.ts +40 -0
  116. package/dist/hooks/useSubscription.js +97 -0
  117. package/dist/index.d.ts +98 -0
  118. package/dist/index.js +198 -0
  119. package/dist/models/BillDogError.d.ts +40 -0
  120. package/dist/models/BillDogError.js +11 -0
  121. package/dist/models/CacheFetchPolicy.d.ts +8 -0
  122. package/dist/models/CacheFetchPolicy.js +4 -0
  123. package/dist/models/CustomProduct.d.ts +32 -0
  124. package/dist/models/CustomProduct.js +15 -0
  125. package/dist/models/CustomerInfo.d.ts +17 -0
  126. package/dist/models/CustomerInfo.js +11 -0
  127. package/dist/models/Delegate.d.ts +20 -0
  128. package/dist/models/Delegate.js +11 -0
  129. package/dist/models/EntitlementInfo.d.ts +19 -0
  130. package/dist/models/EntitlementInfo.js +2 -0
  131. package/dist/models/Placement.d.ts +54 -0
  132. package/dist/models/Placement.js +15 -0
  133. package/dist/models/PurchaseParams.d.ts +39 -0
  134. package/dist/models/PurchaseParams.js +60 -0
  135. package/dist/models/PurchaseResult.d.ts +30 -0
  136. package/dist/models/PurchaseResult.js +15 -0
  137. package/dist/models/RestoreResult.d.ts +30 -0
  138. package/dist/models/RestoreResult.js +22 -0
  139. package/dist/models/RestoredItem.d.ts +26 -0
  140. package/dist/models/RestoredItem.js +13 -0
  141. package/dist/modules/ABTest.d.ts +65 -0
  142. package/dist/modules/ABTest.js +113 -0
  143. package/dist/modules/Analytics.d.ts +91 -0
  144. package/dist/modules/Analytics.js +182 -0
  145. package/dist/modules/Survey.d.ts +65 -0
  146. package/dist/modules/Survey.js +82 -0
  147. package/dist/modules/VirtualCurrency.d.ts +63 -0
  148. package/dist/modules/VirtualCurrency.js +147 -0
  149. package/dist/murmur.d.ts +23 -0
  150. package/dist/murmur.js +104 -0
  151. package/dist/notifications/LiveActivitiesManager.d.ts +47 -0
  152. package/dist/notifications/LiveActivitiesManager.js +106 -0
  153. package/dist/notifications/NotificationScheduler.d.ts +55 -0
  154. package/dist/notifications/NotificationScheduler.js +199 -0
  155. package/dist/notifications/PurchaseNotificationHandler.d.ts +24 -0
  156. package/dist/notifications/PurchaseNotificationHandler.js +55 -0
  157. package/dist/notifications/useDeviceToken.d.ts +36 -0
  158. package/dist/notifications/useDeviceToken.js +97 -0
  159. package/dist/notifications/useLiveActivity.d.ts +39 -0
  160. package/dist/notifications/useLiveActivity.js +104 -0
  161. package/dist/review/ReviewAnalytics.d.ts +34 -0
  162. package/dist/review/ReviewAnalytics.js +73 -0
  163. package/dist/review/ReviewManager.d.ts +83 -0
  164. package/dist/review/ReviewManager.js +339 -0
  165. package/dist/review/ReviewTimingManager.d.ts +82 -0
  166. package/dist/review/ReviewTimingManager.js +224 -0
  167. package/dist/survey-core/types.d.ts +215 -0
  168. package/dist/survey-core/types.js +8 -0
  169. package/dist/types.d.ts +438 -0
  170. package/dist/types.js +158 -0
  171. package/dist/utils/ApiClient.d.ts +65 -0
  172. package/dist/utils/ApiClient.js +132 -0
  173. package/dist/utils/CSSConverter.d.ts +49 -0
  174. package/dist/utils/CSSConverter.js +470 -0
  175. package/dist/utils/animations.d.ts +41 -0
  176. package/dist/utils/animations.js +194 -0
  177. package/dist/utils/calendarIntegration.d.ts +36 -0
  178. package/dist/utils/calendarIntegration.js +89 -0
  179. package/dist/utils/configurationResolver.d.ts +23 -0
  180. package/dist/utils/configurationResolver.js +78 -0
  181. package/dist/utils/deviceDetection.d.ts +38 -0
  182. package/dist/utils/deviceDetection.js +59 -0
  183. package/dist/utils/hapticFeedback.d.ts +17 -0
  184. package/dist/utils/hapticFeedback.js +53 -0
  185. package/dist/utils/iconMapper.d.ts +26 -0
  186. package/dist/utils/iconMapper.js +113 -0
  187. package/dist/utils/notificationPermissions.d.ts +13 -0
  188. package/dist/utils/notificationPermissions.js +51 -0
  189. package/dist/utils/priceFormatting.d.ts +19 -0
  190. package/dist/utils/priceFormatting.js +99 -0
  191. package/dist/utils/shareSheet.d.ts +17 -0
  192. package/dist/utils/shareSheet.js +42 -0
  193. package/dist/utils/subscriptionUpgrade.d.ts +84 -0
  194. package/dist/utils/subscriptionUpgrade.js +100 -0
  195. package/dist/valueUtils.d.ts +48 -0
  196. package/dist/valueUtils.js +129 -0
  197. package/dist/xFlowTags.d.ts +18 -0
  198. package/dist/xFlowTags.js +385 -0
  199. package/ios/BillDogABTest.m +50 -0
  200. package/ios/BillDogABTest.swift +188 -0
  201. package/ios/BillDogAnalytics.m +105 -0
  202. package/ios/BillDogAnalytics.swift +324 -0
  203. package/ios/BillDogBridge.m +222 -0
  204. package/ios/BillDogBridge.swift +715 -0
  205. package/ios/BillDogLiveActivities.m +41 -0
  206. package/ios/BillDogLiveActivities.swift +104 -0
  207. package/ios/BillDogPaywallViewManager.m +39 -0
  208. package/ios/BillDogPaywallViewManager.swift +361 -0
  209. package/ios/BillDogSurvey.m +34 -0
  210. package/ios/BillDogSurvey.swift +257 -0
  211. package/ios/BillDogVirtualCurrencyBridge.m +63 -0
  212. package/ios/BillDogVirtualCurrencyBridge.swift +511 -0
  213. package/junit.xml +25 -0
  214. package/package.json +61 -0
  215. package/react-native.config.js +13 -0
  216. package/src/AdvancedPaywallCache.ts +433 -0
  217. package/src/BillDog.ts +983 -0
  218. package/src/BillDogConfiguration.ts +318 -0
  219. package/src/ComponentRenderer.tsx +1325 -0
  220. package/src/LayoutEngine.ts +459 -0
  221. package/src/LocalizationResolver.ts +311 -0
  222. package/src/NativePaywallView.tsx +132 -0
  223. package/src/PaywallCache.ts +358 -0
  224. package/src/PaywallStateManager.ts +154 -0
  225. package/src/PaywallView.tsx +328 -0
  226. package/src/ProductVariableProvider.ts +282 -0
  227. package/src/VariableResolver.ts +516 -0
  228. package/src/WebViewPaywall.tsx +454 -0
  229. package/src/animations/presets.ts +421 -0
  230. package/src/components/AccoladeCpsComponent.tsx +259 -0
  231. package/src/components/AccordionComponent.tsx +213 -0
  232. package/src/components/AnimatedWrapper.tsx +316 -0
  233. package/src/components/CarouselComponent.tsx +615 -0
  234. package/src/components/ChartCurveComponent.tsx +452 -0
  235. package/src/components/CheckboxComponent.tsx +185 -0
  236. package/src/components/ConfettiComponent.tsx +178 -0
  237. package/src/components/CountdownComponent.tsx +454 -0
  238. package/src/components/DelayedXComponent.tsx +266 -0
  239. package/src/components/DrawerModalComponent.tsx +324 -0
  240. package/src/components/GradientView.tsx +129 -0
  241. package/src/components/HapticFeedbackComponent.tsx +39 -0
  242. package/src/components/HorizontalProductsComponent.tsx +329 -0
  243. package/src/components/MultiChoiceGridComponent.tsx +265 -0
  244. package/src/components/NPSScaleComponent.tsx +294 -0
  245. package/src/components/NavigationComponent.tsx +329 -0
  246. package/src/components/OAuthButtonComponent.tsx +266 -0
  247. package/src/components/ProgressLoaderComponent.tsx +366 -0
  248. package/src/components/ProgressStepperComponent.tsx +250 -0
  249. package/src/components/SegmentedPickerComponent.tsx +245 -0
  250. package/src/components/ShareButtonsComponent.tsx +299 -0
  251. package/src/components/ShareCodeComponent.tsx +303 -0
  252. package/src/components/SingleChoiceGridComponent.tsx +229 -0
  253. package/src/components/SliderComponent.tsx +280 -0
  254. package/src/components/StarRatingInputComponent.tsx +223 -0
  255. package/src/components/StickerCompositeComponent.tsx +168 -0
  256. package/src/components/TabsComponent.tsx +496 -0
  257. package/src/components/TextInputComponent.tsx +258 -0
  258. package/src/components/TimelineComponent.tsx +129 -0
  259. package/src/components/ToggleComponent.tsx +142 -0
  260. package/src/components/VerticalProductsComponent.tsx +357 -0
  261. package/src/dynamicValues.ts +180 -0
  262. package/src/filters/dateFilters.ts +411 -0
  263. package/src/filters/index.ts +281 -0
  264. package/src/hooks/useOfferings.ts +192 -0
  265. package/src/hooks/usePaywall.ts +154 -0
  266. package/src/hooks/usePlacement.ts +143 -0
  267. package/src/hooks/useSubscription.ts +154 -0
  268. package/src/index.ts +245 -0
  269. package/src/models/BillDogError.ts +24 -0
  270. package/src/models/CacheFetchPolicy.ts +13 -0
  271. package/src/models/CustomProduct.ts +33 -0
  272. package/src/models/CustomerInfo.ts +26 -0
  273. package/src/models/Delegate.ts +22 -0
  274. package/src/models/EntitlementInfo.ts +27 -0
  275. package/src/models/Placement.ts +50 -0
  276. package/src/models/PurchaseParams.ts +81 -0
  277. package/src/models/PurchaseResult.ts +32 -0
  278. package/src/models/RestoreResult.ts +27 -0
  279. package/src/models/RestoredItem.ts +25 -0
  280. package/src/modules/ABTest.ts +131 -0
  281. package/src/modules/Analytics.ts +227 -0
  282. package/src/modules/Survey.ts +126 -0
  283. package/src/modules/VirtualCurrency.ts +223 -0
  284. package/src/murmur.ts +102 -0
  285. package/src/notifications/LiveActivitiesManager.ts +140 -0
  286. package/src/notifications/NotificationScheduler.ts +292 -0
  287. package/src/notifications/PurchaseNotificationHandler.ts +67 -0
  288. package/src/notifications/useDeviceToken.ts +116 -0
  289. package/src/notifications/useLiveActivity.ts +125 -0
  290. package/src/review/ReviewAnalytics.ts +89 -0
  291. package/src/review/ReviewManager.ts +355 -0
  292. package/src/review/ReviewTimingManager.ts +265 -0
  293. package/src/survey-core/types.ts +244 -0
  294. package/src/types/ambient.d.ts +9 -0
  295. package/src/types.ts +680 -0
  296. package/src/utils/ApiClient.ts +163 -0
  297. package/src/utils/CSSConverter.ts +552 -0
  298. package/src/utils/animations.ts +232 -0
  299. package/src/utils/calendarIntegration.ts +113 -0
  300. package/src/utils/configurationResolver.ts +106 -0
  301. package/src/utils/deviceDetection.ts +83 -0
  302. package/src/utils/hapticFeedback.ts +55 -0
  303. package/src/utils/iconMapper.ts +102 -0
  304. package/src/utils/notificationPermissions.ts +48 -0
  305. package/src/utils/priceFormatting.ts +119 -0
  306. package/src/utils/shareSheet.ts +55 -0
  307. package/src/utils/subscriptionUpgrade.ts +183 -0
  308. package/src/valueUtils.ts +108 -0
  309. package/src/xFlowTags.ts +463 -0
@@ -0,0 +1,250 @@
1
+ /**
2
+ * ProgressStepperComponent - React Native
3
+ *
4
+ * Multi-step progress indicator with customizable appearance.
5
+ * Features:
6
+ * - Horizontal/vertical orientation
7
+ * - Animated transitions
8
+ * - State manager integration
9
+ */
10
+
11
+ import React, { useState, useEffect, useCallback } from 'react';
12
+ import {
13
+ View,
14
+ Text,
15
+ StyleSheet,
16
+ Animated,
17
+ } from 'react-native';
18
+ import { PaywallComponent } from '../types';
19
+
20
+ interface ProgressStepperComponentProps {
21
+ component: PaywallComponent;
22
+ stateManager?: any;
23
+ isPreviewMode?: boolean;
24
+ }
25
+
26
+ // Helper to extract color from fill value
27
+ const getColorFromFill = (fill: any, fallback: string): string => {
28
+ if (!fill) return fallback;
29
+ if (typeof fill === 'string') return fill;
30
+ if (fill.type === 'color' && fill.color) return fill.color;
31
+ return fallback;
32
+ };
33
+
34
+ export const ProgressStepperComponent: React.FC<ProgressStepperComponentProps> = ({
35
+ component,
36
+ stateManager,
37
+ isPreviewMode = false,
38
+ }) => {
39
+ const props = component.properties as any;
40
+ const componentId = component.id;
41
+
42
+ // Configuration
43
+ const totalSteps = Math.max(2, Math.min(10, props.totalSteps ?? 4));
44
+ const initialStep = Math.max(1, Math.min(totalSteps, props.currentStep ?? 1));
45
+ const orientation = props.orientation ?? 'horizontal';
46
+
47
+ // Labels
48
+ const showLabels = props.showLabels ?? false;
49
+ const showNumbers = props.showNumbers ?? true;
50
+ const labels: string[] = [];
51
+ for (let i = 1; i <= totalSteps; i++) {
52
+ labels.push(props[`step${i}Label`] ?? `Step ${i}`);
53
+ }
54
+
55
+ // Styling
56
+ const stepSize = props.stepSize ?? 32;
57
+ const lineThickness = props.lineThickness ?? 2;
58
+ const stepGap = props.stepGap ?? 8;
59
+
60
+ // Colors
61
+ const completedColor = getColorFromFill(props.completedFill, '#3B82F6');
62
+ const activeColor = getColorFromFill(props.activeFill, '#3B82F6');
63
+ const inactiveColor = getColorFromFill(props.inactiveFill, '#E5E7EB');
64
+ const lineColor = getColorFromFill(props.lineFill, '#E5E7EB');
65
+ const completedLineColor = getColorFromFill(props.completedLineFill, '#3B82F6');
66
+ const textColor = getColorFromFill(props.textFill, '#FFFFFF');
67
+ const labelColor = getColorFromFill(props.labelFill, '#6B7280');
68
+
69
+ // Animation
70
+ const animated = props.animated ?? true;
71
+
72
+ const [currentStep, setCurrentStep] = useState(initialStep);
73
+ const [animatedValues] = useState(() =>
74
+ Array.from({ length: totalSteps }, () => new Animated.Value(1))
75
+ );
76
+
77
+ // Connect to state manager
78
+ useEffect(() => {
79
+ if (stateManager) {
80
+ // Initialize state
81
+ stateManager.updateState(`element:${componentId}.currentStep`, currentStep);
82
+ stateManager.updateState(`element:${componentId}.totalSteps`, totalSteps);
83
+ stateManager.updateState(`element:${componentId}.progress`, currentStep / totalSteps);
84
+ stateManager.updateState(`element:${componentId}.isComplete`, currentStep >= totalSteps);
85
+
86
+ // Subscribe to external changes
87
+ return stateManager.subscribeToState(
88
+ `element:${componentId}.currentStep`,
89
+ (value: number) => {
90
+ if (typeof value === 'number' && value !== currentStep) {
91
+ setCurrentStep(Math.min(Math.max(1, value), totalSteps));
92
+ }
93
+ }
94
+ );
95
+ }
96
+ }, [componentId, totalSteps, currentStep, stateManager]);
97
+
98
+ // Animate when step changes
99
+ useEffect(() => {
100
+ if (animated) {
101
+ // Animate current step
102
+ Animated.sequence([
103
+ Animated.timing(animatedValues[currentStep - 1], {
104
+ toValue: 1.15,
105
+ duration: 100,
106
+ useNativeDriver: true,
107
+ }),
108
+ Animated.timing(animatedValues[currentStep - 1], {
109
+ toValue: 1,
110
+ duration: 100,
111
+ useNativeDriver: true,
112
+ }),
113
+ ]).start();
114
+ }
115
+
116
+ if (stateManager) {
117
+ stateManager.updateState(`element:${componentId}.currentStep`, currentStep);
118
+ stateManager.updateState(`element:${componentId}.progress`, currentStep / totalSteps);
119
+ stateManager.updateState(`element:${componentId}.isComplete`, currentStep >= totalSteps);
120
+ }
121
+ }, [currentStep, animated, animatedValues, componentId, totalSteps, stateManager]);
122
+
123
+ const isHorizontal = orientation === 'horizontal';
124
+
125
+ return (
126
+ <View
127
+ style={[
128
+ styles.container,
129
+ {
130
+ flexDirection: isHorizontal ? 'row' : 'column',
131
+ gap: stepGap,
132
+ },
133
+ ]}
134
+ >
135
+ {Array.from({ length: totalSteps }).map((_, index) => {
136
+ const stepNumber = index + 1;
137
+ const isCompleted = stepNumber < currentStep;
138
+ const isActive = stepNumber === currentStep;
139
+ const isLast = index === totalSteps - 1;
140
+
141
+ const bgColor = isCompleted ? completedColor : isActive ? activeColor : inactiveColor;
142
+ const currentLineColor = isCompleted ? completedLineColor : lineColor;
143
+
144
+ return (
145
+ <View
146
+ key={index}
147
+ style={[
148
+ styles.stepContainer,
149
+ {
150
+ flexDirection: isHorizontal ? 'row' : 'column',
151
+ flex: isLast ? undefined : 1,
152
+ },
153
+ ]}
154
+ >
155
+ {/* Step with optional label */}
156
+ <View style={styles.stepContent}>
157
+ {/* Step Circle */}
158
+ <Animated.View
159
+ style={[
160
+ styles.stepCircle,
161
+ {
162
+ width: stepSize,
163
+ height: stepSize,
164
+ borderRadius: stepSize / 2,
165
+ backgroundColor: bgColor,
166
+ transform: [{ scale: animatedValues[index] }],
167
+ },
168
+ ]}
169
+ >
170
+ {isCompleted ? (
171
+ <Text style={[styles.stepIcon, { color: textColor, fontSize: stepSize * 0.5 }]}>
172
+
173
+ </Text>
174
+ ) : showNumbers ? (
175
+ <Text style={[styles.stepNumber, { color: isActive ? textColor : '#6B7280', fontSize: stepSize * 0.4 }]}>
176
+ {stepNumber}
177
+ </Text>
178
+ ) : null}
179
+ </Animated.View>
180
+
181
+ {/* Step Label */}
182
+ {showLabels && (
183
+ <Text
184
+ style={[
185
+ styles.stepLabel,
186
+ {
187
+ color: isCompleted || isActive ? textColor : labelColor,
188
+ fontSize: 12,
189
+ },
190
+ ]}
191
+ numberOfLines={1}
192
+ >
193
+ {labels[index]}
194
+ </Text>
195
+ )}
196
+ </View>
197
+
198
+ {/* Connecting Line */}
199
+ {!isLast && (
200
+ <View
201
+ style={[
202
+ styles.line,
203
+ {
204
+ backgroundColor: currentLineColor,
205
+ height: isHorizontal ? lineThickness : undefined,
206
+ width: isHorizontal ? undefined : lineThickness,
207
+ flex: 1,
208
+ minWidth: isHorizontal ? 20 : undefined,
209
+ minHeight: isHorizontal ? undefined : 20,
210
+ marginHorizontal: isHorizontal ? stepGap : 0,
211
+ marginVertical: isHorizontal ? 0 : stepGap,
212
+ borderRadius: lineThickness / 2,
213
+ },
214
+ ]}
215
+ />
216
+ )}
217
+ </View>
218
+ );
219
+ })}
220
+ </View>
221
+ );
222
+ };
223
+
224
+ const styles = StyleSheet.create({
225
+ container: {
226
+ width: '100%',
227
+ alignItems: 'center',
228
+ },
229
+ stepContainer: {
230
+ alignItems: 'center',
231
+ },
232
+ stepContent: {
233
+ alignItems: 'center',
234
+ gap: 4,
235
+ },
236
+ stepCircle: {
237
+ alignItems: 'center',
238
+ justifyContent: 'center',
239
+ },
240
+ stepNumber: {
241
+ fontWeight: '600',
242
+ },
243
+ stepIcon: {
244
+ fontWeight: 'bold',
245
+ },
246
+ stepLabel: {
247
+ textAlign: 'center',
248
+ },
249
+ line: {},
250
+ });
@@ -0,0 +1,245 @@
1
+ import React, { useState, useEffect, useMemo } from 'react';
2
+ import {
3
+ View,
4
+ Text,
5
+ TouchableOpacity,
6
+ StyleSheet,
7
+ ViewStyle,
8
+ TextStyle,
9
+ } from 'react-native';
10
+ import { PaywallComponent } from '../types';
11
+
12
+ interface SegmentedPickerComponentProps {
13
+ component: PaywallComponent;
14
+ stateManager?: any;
15
+ isPreviewMode?: boolean;
16
+ renderChild?: (child: PaywallComponent, index: number, isSelected: boolean) => React.ReactNode;
17
+ }
18
+
19
+ // Helper to extract color from FillValue or string
20
+ const extractColor = (fill: any, fallback: string = 'transparent'): string => {
21
+ if (!fill) return fallback;
22
+ if (typeof fill === 'string') return fill;
23
+ if (fill.type === 'color' && fill.color) return fill.color;
24
+ if (fill.resolvedColor) return fill.resolvedColor;
25
+ return fallback;
26
+ };
27
+
28
+ // Helper to apply selectedAppearance to child when selected
29
+ const applySelectedAppearanceToChild = (
30
+ child: PaywallComponent,
31
+ isSelected: boolean
32
+ ): PaywallComponent => {
33
+ if (!isSelected || !child.selectedAppearance) {
34
+ return child;
35
+ }
36
+ // Merge selectedAppearance into appearance for selected state
37
+ return {
38
+ ...child,
39
+ appearance: child.selectedAppearance,
40
+ };
41
+ };
42
+
43
+ export const SegmentedPickerComponent: React.FC<SegmentedPickerComponentProps> = ({
44
+ component,
45
+ stateManager,
46
+ isPreviewMode = false,
47
+ renderChild,
48
+ }) => {
49
+ const props = component.properties as any;
50
+ // Children arrive fully styled with selectedAppearance for state changes
51
+
52
+ // Selection properties
53
+ const defaultIndex = props.defaultSelectedIndex ?? 0;
54
+ const disabled = props.disabled ?? false;
55
+ const selectableSegments = props.selectableSegments !== false;
56
+ const bindToPackages = props.bindToPackages ?? false;
57
+
58
+ const [selectedIndex, setSelectedIndex] = useState(defaultIndex);
59
+
60
+ // Get segment children (filter by role)
61
+ const segmentChildren = useMemo(() => {
62
+ if (!component.children) return [];
63
+ return component.children.filter(child => {
64
+ const role = (child as any).role;
65
+ return role?.startsWith('segment-') && role !== 'segment-label';
66
+ });
67
+ }, [component.children]);
68
+
69
+ // Register initial state
70
+ useEffect(() => {
71
+ if (stateManager) {
72
+ stateManager.updateState(`element:${component.id}.selectedIndex`, selectedIndex);
73
+ }
74
+ }, []);
75
+
76
+ // Subscribe to external state changes
77
+ useEffect(() => {
78
+ if (stateManager) {
79
+ return stateManager.subscribeToState(
80
+ `element:${component.id}.selectedIndex`,
81
+ (value: number) => {
82
+ if (value !== selectedIndex) {
83
+ setSelectedIndex(value);
84
+ }
85
+ }
86
+ );
87
+ }
88
+ }, [component.id, stateManager]);
89
+
90
+ const handleSelect = (index: number) => {
91
+ if (!disabled && selectableSegments) {
92
+ setSelectedIndex(index);
93
+ if (stateManager) {
94
+ stateManager.updateState(`element:${component.id}.selectedIndex`, index);
95
+ }
96
+ }
97
+ };
98
+
99
+ // Container styling from props
100
+ const containerBackground = extractColor(props.containerBackground, '#f0f0f0');
101
+ const containerBorderRadius = props.borderRadius ?? 8;
102
+ const containerPadding = props.padding ?? 4;
103
+ const gap = props.gap ?? 4;
104
+
105
+ // Get styling from component properties and appearance (direct properties architecture)
106
+ const componentAppearance = (component as any).appearance || {};
107
+ const componentBorder = (component as any).border || {};
108
+
109
+ // Segment styling - read from component.properties (canonical names)
110
+ const segmentFill = props.segmentFill;
111
+ const selectedSegmentFill = props.selectedSegmentFill;
112
+
113
+ const segmentBackground = extractColor(segmentFill, extractColor(componentAppearance.fill, 'transparent'));
114
+ const selectedBackground = extractColor(selectedSegmentFill, '#007AFF');
115
+ const segmentCornerRadius = componentAppearance.cornerRadius ?? 6;
116
+ const segmentPadding = props.padding ?? 8;
117
+ const segmentBorderColor = extractColor(componentBorder.color, 'transparent');
118
+ const segmentBorderWidth = componentBorder.width ?? 0;
119
+ const selectedBorderColor = props.selectedBorderColor ?? 'transparent';
120
+ const selectedBorderWidth = props.selectedBorderWidth ?? 0;
121
+
122
+ // Render with children (child-based architecture)
123
+ if (segmentChildren.length > 0 && renderChild) {
124
+ return (
125
+ <View style={[
126
+ styles.container,
127
+ {
128
+ backgroundColor: containerBackground,
129
+ borderRadius: containerBorderRadius,
130
+ padding: containerPadding,
131
+ gap,
132
+ },
133
+ disabled && styles.disabled,
134
+ ]}>
135
+ {segmentChildren.map((child, index) => {
136
+ const isActive = index === selectedIndex;
137
+
138
+ // Use child.selectedAppearance when active (children already have base styling)
139
+ const appearance = isActive ? child.selectedAppearance : child.appearance;
140
+
141
+ const bgColor = extractColor(appearance?.fill, isActive ? selectedBackground : segmentBackground);
142
+ const borderColor = isActive
143
+ ? extractColor(appearance?.borderColor, selectedBorderColor)
144
+ : extractColor(componentBorder.color, segmentBorderColor);
145
+ const borderWidth = isActive
146
+ ? (appearance?.borderWidth ?? selectedBorderWidth)
147
+ : segmentBorderWidth;
148
+
149
+ // Apply selectedAppearance to child for rendering
150
+ const styledChild = applySelectedAppearanceToChild(child, isActive);
151
+
152
+ return (
153
+ <TouchableOpacity
154
+ key={child.id}
155
+ onPress={() => handleSelect(index)}
156
+ disabled={disabled || !selectableSegments}
157
+ activeOpacity={0.7}
158
+ style={[
159
+ styles.segment,
160
+ {
161
+ backgroundColor: bgColor,
162
+ borderRadius: appearance?.cornerRadius ?? segmentCornerRadius,
163
+ padding: segmentPadding,
164
+ borderWidth,
165
+ borderColor,
166
+ },
167
+ ]}
168
+ >
169
+ {renderChild(styledChild, index, isActive)}
170
+ </TouchableOpacity>
171
+ );
172
+ })}
173
+ </View>
174
+ );
175
+ }
176
+
177
+ // Fallback: render simple text segments from legacy segments property or defaults
178
+ const legacySegments: string[] = props.segments ?? ['Option 1', 'Option 2'];
179
+ const activeColor = extractColor(selectedSegmentFill, props.activeColor ?? '#007AFF');
180
+ const inactiveColor = extractColor(segmentFill, props.inactiveColor ?? 'transparent');
181
+ const activeTextColor = props.selectedTextColor ?? props.activeTextColor ?? '#fff';
182
+ const inactiveTextColor = props.textColor ?? props.inactiveTextColor ?? '#333';
183
+
184
+ return (
185
+ <View style={[
186
+ styles.container,
187
+ {
188
+ backgroundColor: containerBackground,
189
+ borderRadius: containerBorderRadius,
190
+ padding: containerPadding,
191
+ },
192
+ disabled && styles.disabled,
193
+ ]}>
194
+ {legacySegments.map((segment, index) => {
195
+ const isActive = index === selectedIndex;
196
+ return (
197
+ <TouchableOpacity
198
+ key={index}
199
+ onPress={() => handleSelect(index)}
200
+ disabled={disabled || !selectableSegments}
201
+ activeOpacity={0.7}
202
+ style={[
203
+ styles.segment,
204
+ {
205
+ backgroundColor: isActive ? activeColor : inactiveColor,
206
+ borderRadius: segmentCornerRadius,
207
+ padding: segmentPadding,
208
+ },
209
+ ]}
210
+ >
211
+ <Text
212
+ style={[
213
+ styles.segmentText,
214
+ {
215
+ color: isActive ? activeTextColor : inactiveTextColor,
216
+ fontSize: props.labelFontSize ?? 14,
217
+ fontWeight: isActive ? '600' : (props.labelFontWeight ?? 'normal'),
218
+ },
219
+ ]}
220
+ >
221
+ {segment}
222
+ </Text>
223
+ </TouchableOpacity>
224
+ );
225
+ })}
226
+ </View>
227
+ );
228
+ };
229
+
230
+ const styles = StyleSheet.create({
231
+ container: {
232
+ flexDirection: 'row',
233
+ } as ViewStyle,
234
+ disabled: {
235
+ opacity: 0.5,
236
+ } as ViewStyle,
237
+ segment: {
238
+ flex: 1,
239
+ alignItems: 'center',
240
+ justifyContent: 'center',
241
+ } as ViewStyle,
242
+ segmentText: {
243
+ textAlign: 'center',
244
+ } as TextStyle,
245
+ });