@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,229 @@
1
+ /**
2
+ * Single Choice Grid Component for React Native
3
+ * Radio button style grid selection (single selection)
4
+ */
5
+
6
+ import React, { useState, useEffect } from 'react';
7
+ import {
8
+ View,
9
+ Text,
10
+ TouchableOpacity,
11
+ StyleSheet,
12
+ ViewStyle,
13
+ TextStyle,
14
+ DimensionValue,
15
+ } from 'react-native';
16
+ import { PaywallComponent } from '../types';
17
+
18
+ interface SingleChoiceGridProps {
19
+ component: PaywallComponent;
20
+ stateManager?: any;
21
+ isPreviewMode?: boolean;
22
+ }
23
+
24
+ const SIZE_MAP = { sm: 16, md: 20, lg: 24 };
25
+
26
+ export const SingleChoiceGridComponent: React.FC<SingleChoiceGridProps> = ({
27
+ component,
28
+ stateManager,
29
+ isPreviewMode = false,
30
+ }) => {
31
+ const props = component.properties || {};
32
+
33
+ // Parse options
34
+ const optionsStr = (props as any).options || 'Option 1,Option 2,Option 3,Option 4';
35
+ const options = optionsStr.split(',').map((o: string) => o.trim()).filter(Boolean);
36
+ const descriptionsStr = (props as any).optionDescriptions || '';
37
+ const descriptions = descriptionsStr.split(',').map((d: string) => d.trim());
38
+
39
+ // Grid settings
40
+ const columns = (props as any).columns || 2;
41
+ const gap = parseFloat((props as any).gap?.value || (props as any).gap) || 12;
42
+ const minItemHeight = parseFloat((props as any).minItemHeight?.value || (props as any).minItemHeight) || 80;
43
+
44
+ // Selection settings
45
+ const defaultIndex = (props as any).defaultSelectedIndex ?? 0;
46
+ const allowDeselect = (props as any).allowDeselect ?? false;
47
+ const disabled = (props as any).disabled ?? false;
48
+
49
+ // State
50
+ const [selectedIndex, setSelectedIndex] = useState<number>(defaultIndex);
51
+
52
+ // State manager integration
53
+ useEffect(() => {
54
+ if (!stateManager) return;
55
+
56
+ const stateId = `element:${component.id}.selectedIndex`;
57
+ stateManager.registerState?.(stateId, selectedIndex);
58
+
59
+ return stateManager.subscribeToState?.(stateId, (value: number) => {
60
+ if (value !== selectedIndex) setSelectedIndex(value);
61
+ });
62
+ }, [component.id, selectedIndex, stateManager]);
63
+
64
+ const handleSelect = (index: number) => {
65
+ if (disabled) return;
66
+
67
+ const newIndex = (allowDeselect && selectedIndex === index) ? -1 : index;
68
+ setSelectedIndex(newIndex);
69
+
70
+ stateManager?.updateState?.(`element:${component.id}.selectedIndex`, newIndex);
71
+ stateManager?.updateState?.(`element:${component.id}.selectedValue`, newIndex >= 0 ? options[newIndex] : '');
72
+ };
73
+
74
+ // Styling
75
+ const showIndicator = (props as any).showIndicator ?? true;
76
+ const indicatorPosition = (props as any).indicatorPosition || 'top-right';
77
+ const indicatorSize = SIZE_MAP[(props as any).indicatorSize as keyof typeof SIZE_MAP] || SIZE_MAP.md;
78
+ const indicatorColor = (props as any).indicatorColor || '#007AFF';
79
+ const indicatorBorderColor = (props as any).indicatorBorderColor || '#d1d5db';
80
+
81
+ const cardBorderRadius = parseFloat((props as any).cardBorderRadius?.value || (props as any).cardBorderRadius) || 12;
82
+ const cardBorderColor = (props as any).cardBorderColor || '#e5e7eb';
83
+ const selectedBorderColor = (props as any).selectedBorderColor || '#007AFF';
84
+
85
+ const labelFontSize = parseFloat((props as any).labelFontSize?.value || (props as any).labelFontSize) || 16;
86
+ const labelFontWeight = String((props as any).labelFontWeight || '500') as TextStyle['fontWeight'];
87
+ const labelColor = (props as any).labelColor || '#1f2937';
88
+ const selectedLabelColor = (props as any).selectedLabelColor || '#007AFF';
89
+ const descriptionFontSize = parseFloat((props as any).descriptionFontSize?.value || (props as any).descriptionFontSize) || 12;
90
+ const descriptionColor = (props as any).descriptionColor || '#6b7280';
91
+
92
+ // Background color helper
93
+ const getBackgroundColor = (fill: any): string => {
94
+ if (!fill) return '#ffffff';
95
+ if (typeof fill === 'string') return fill;
96
+ if (fill.type === 'color') return fill.color || '#ffffff';
97
+ if (fill.resolvedColor) return fill.resolvedColor;
98
+ return '#ffffff';
99
+ };
100
+
101
+ const cardBackground = getBackgroundColor((props as any).cardBackground);
102
+ const selectedCardBackground = getBackgroundColor((props as any).selectedCardBackground);
103
+
104
+ // Indicator position style
105
+ const getIndicatorPositionStyle = (): ViewStyle => {
106
+ const base: ViewStyle = { position: 'absolute' };
107
+ switch (indicatorPosition) {
108
+ case 'top-left': return { ...base, top: 8, left: 8 };
109
+ case 'top-right': return { ...base, top: 8, right: 8 };
110
+ case 'bottom-left': return { ...base, bottom: 8, left: 8 };
111
+ case 'bottom-right': return { ...base, bottom: 8, right: 8 };
112
+ default: return { ...base, top: 8, right: 8 };
113
+ }
114
+ };
115
+
116
+ // Calculate item width based on columns
117
+ const itemWidth: DimensionValue = `${(100 - (columns - 1) * 2) / columns}%`;
118
+
119
+ return (
120
+ <View style={[styles.container, { opacity: disabled ? 0.6 : 1 }]}>
121
+ <View style={[styles.grid, { gap }]}>
122
+ {options.map((option: string, index: number) => {
123
+ const isSelected = selectedIndex === index;
124
+ const description = descriptions[index] || '';
125
+
126
+ return (
127
+ <TouchableOpacity
128
+ key={index}
129
+ onPress={() => handleSelect(index)}
130
+ disabled={disabled}
131
+ activeOpacity={0.7}
132
+ style={[
133
+ styles.card,
134
+ {
135
+ width: itemWidth,
136
+ minHeight: minItemHeight,
137
+ backgroundColor: isSelected ? selectedCardBackground : cardBackground,
138
+ borderColor: isSelected ? selectedBorderColor : cardBorderColor,
139
+ borderRadius: cardBorderRadius,
140
+ },
141
+ ]}
142
+ >
143
+ {/* Radio Indicator */}
144
+ {showIndicator && (
145
+ <View
146
+ style={[
147
+ getIndicatorPositionStyle(),
148
+ {
149
+ width: indicatorSize,
150
+ height: indicatorSize,
151
+ borderRadius: indicatorSize / 2,
152
+ borderWidth: 2,
153
+ borderColor: isSelected ? indicatorColor : indicatorBorderColor,
154
+ backgroundColor: isSelected ? indicatorColor : 'transparent',
155
+ alignItems: 'center',
156
+ justifyContent: 'center',
157
+ },
158
+ ]}
159
+ >
160
+ {isSelected && (
161
+ <View
162
+ style={{
163
+ width: indicatorSize * 0.4,
164
+ height: indicatorSize * 0.4,
165
+ borderRadius: indicatorSize * 0.2,
166
+ backgroundColor: '#ffffff',
167
+ }}
168
+ />
169
+ )}
170
+ </View>
171
+ )}
172
+
173
+ {/* Label */}
174
+ <Text
175
+ style={[
176
+ styles.label,
177
+ {
178
+ fontSize: labelFontSize,
179
+ fontWeight: labelFontWeight,
180
+ color: isSelected ? selectedLabelColor : labelColor,
181
+ },
182
+ ]}
183
+ >
184
+ {option}
185
+ </Text>
186
+
187
+ {/* Description */}
188
+ {description ? (
189
+ <Text
190
+ style={[
191
+ styles.description,
192
+ {
193
+ fontSize: descriptionFontSize,
194
+ color: descriptionColor,
195
+ },
196
+ ]}
197
+ >
198
+ {description}
199
+ </Text>
200
+ ) : null}
201
+ </TouchableOpacity>
202
+ );
203
+ })}
204
+ </View>
205
+ </View>
206
+ );
207
+ };
208
+
209
+ const styles = StyleSheet.create({
210
+ container: {
211
+ width: '100%',
212
+ },
213
+ grid: {
214
+ flexDirection: 'row',
215
+ flexWrap: 'wrap',
216
+ },
217
+ card: {
218
+ position: 'relative',
219
+ borderWidth: 2,
220
+ padding: 16,
221
+ justifyContent: 'center',
222
+ },
223
+ label: {
224
+ marginTop: 4,
225
+ },
226
+ description: {
227
+ marginTop: 4,
228
+ },
229
+ });
@@ -0,0 +1,280 @@
1
+ import React, { useState, useEffect, useRef, useCallback } from 'react';
2
+ import {
3
+ View,
4
+ Text,
5
+ StyleSheet,
6
+ PanResponder,
7
+ Animated,
8
+ LayoutChangeEvent,
9
+ } from 'react-native';
10
+ import { PaywallComponent } from '../types';
11
+
12
+ interface SliderComponentProps {
13
+ component: PaywallComponent;
14
+ stateManager?: any;
15
+ isPreviewMode?: boolean;
16
+ }
17
+
18
+ export const SliderComponent: React.FC<SliderComponentProps> = ({
19
+ component,
20
+ stateManager,
21
+ isPreviewMode = false,
22
+ }) => {
23
+ const props = component.properties as any;
24
+ const minValue = props.minValue ?? 0;
25
+ const maxValue = props.maxValue ?? 100;
26
+ const step = props.step ?? 1;
27
+ const defaultValue = props.defaultValue ?? minValue;
28
+ const disabled = props.disabled ?? false;
29
+ const showValue = props.showValue !== false;
30
+ const label = props.sliderLabel || props.label;
31
+ const valuePrefix = props.valuePrefix ?? '';
32
+ const valueSuffix = props.valueSuffix ?? '';
33
+
34
+ // Colors
35
+ const activeColor = props.activeColor ?? '#007AFF';
36
+ const trackColor = props.trackColor ?? '#e0e0e0';
37
+ const thumbColor = props.thumbColor ?? activeColor;
38
+
39
+ const [currentValue, setCurrentValue] = useState(defaultValue);
40
+ const [trackWidth, setTrackWidth] = useState(0);
41
+ const animatedValue = useRef(new Animated.Value(0)).current;
42
+ const isDragging = useRef(false);
43
+
44
+ // Calculate value from position
45
+ const positionToValue = useCallback((position: number): number => {
46
+ if (trackWidth === 0) return minValue;
47
+ const ratio = Math.max(0, Math.min(1, position / trackWidth));
48
+ const rawValue = minValue + ratio * (maxValue - minValue);
49
+ // Apply step
50
+ const steppedValue = Math.round(rawValue / step) * step;
51
+ return Math.max(minValue, Math.min(maxValue, steppedValue));
52
+ }, [trackWidth, minValue, maxValue, step]);
53
+
54
+ // Calculate position from value
55
+ const valueToPosition = useCallback((value: number): number => {
56
+ if (maxValue === minValue) return 0;
57
+ const ratio = (value - minValue) / (maxValue - minValue);
58
+ return ratio * trackWidth;
59
+ }, [trackWidth, minValue, maxValue]);
60
+
61
+ // Update animated value when currentValue or trackWidth changes
62
+ useEffect(() => {
63
+ const position = valueToPosition(currentValue);
64
+ animatedValue.setValue(position);
65
+ }, [currentValue, trackWidth, valueToPosition, animatedValue]);
66
+
67
+ // Register initial state
68
+ useEffect(() => {
69
+ if (stateManager) {
70
+ stateManager.updateState(`element:${component.id}.currentValue`, currentValue);
71
+ }
72
+ }, []);
73
+
74
+ // Subscribe to external state changes
75
+ useEffect(() => {
76
+ if (stateManager) {
77
+ return stateManager.subscribeToState(
78
+ `element:${component.id}.currentValue`,
79
+ (value: number) => {
80
+ if (value !== currentValue && !isDragging.current) {
81
+ setCurrentValue(value);
82
+ }
83
+ }
84
+ );
85
+ }
86
+ }, [component.id, stateManager, currentValue]);
87
+
88
+ // Pan responder for drag handling
89
+ const panResponder = useRef(
90
+ PanResponder.create({
91
+ onStartShouldSetPanResponder: () => !disabled,
92
+ onMoveShouldSetPanResponder: () => !disabled,
93
+ onPanResponderGrant: (_, gestureState) => {
94
+ isDragging.current = true;
95
+ // Calculate initial position from touch
96
+ const touchX = gestureState.x0;
97
+ // This needs adjustment based on actual component position
98
+ },
99
+ onPanResponderMove: (_, gestureState) => {
100
+ if (disabled || trackWidth === 0) return;
101
+
102
+ // Calculate new position based on accumulated movement
103
+ const currentPosition = valueToPosition(currentValue);
104
+ const newPosition = Math.max(0, Math.min(trackWidth, currentPosition + gestureState.dx));
105
+ const newValue = positionToValue(newPosition);
106
+
107
+ if (newValue !== currentValue) {
108
+ setCurrentValue(newValue);
109
+ animatedValue.setValue(newPosition);
110
+ }
111
+ },
112
+ onPanResponderRelease: () => {
113
+ isDragging.current = false;
114
+ // Update state manager on release
115
+ if (stateManager) {
116
+ stateManager.updateState(`element:${component.id}.currentValue`, currentValue);
117
+ }
118
+ },
119
+ onPanResponderTerminate: () => {
120
+ isDragging.current = false;
121
+ },
122
+ })
123
+ ).current;
124
+
125
+ // Handle track layout
126
+ const onTrackLayout = (event: LayoutChangeEvent) => {
127
+ const { width } = event.nativeEvent.layout;
128
+ setTrackWidth(width);
129
+ };
130
+
131
+ // Handle direct tap on track
132
+ const handleTrackPress = (event: any) => {
133
+ if (disabled || trackWidth === 0) return;
134
+
135
+ const touchX = event.nativeEvent.locationX;
136
+ const newValue = positionToValue(touchX);
137
+ setCurrentValue(newValue);
138
+
139
+ if (stateManager) {
140
+ stateManager.updateState(`element:${component.id}.currentValue`, newValue);
141
+ }
142
+ };
143
+
144
+ const formatValue = (value: number): string => {
145
+ if (step >= 1) {
146
+ return Math.round(value).toString();
147
+ } else if (step >= 0.1) {
148
+ return value.toFixed(1);
149
+ }
150
+ return value.toFixed(2);
151
+ };
152
+
153
+ // Calculate progress percentage
154
+ const progress = trackWidth > 0 ? ((currentValue - minValue) / (maxValue - minValue)) * 100 : 0;
155
+
156
+ return (
157
+ <View style={[styles.container, disabled && styles.disabled]}>
158
+ {(label || showValue) && (
159
+ <View style={styles.header}>
160
+ {label && <Text style={styles.label}>{label}</Text>}
161
+ {showValue && (
162
+ <Text style={styles.value}>
163
+ {valuePrefix}{formatValue(currentValue)}{valueSuffix}
164
+ </Text>
165
+ )}
166
+ </View>
167
+ )}
168
+
169
+ <View
170
+ style={styles.sliderContainer}
171
+ onStartShouldSetResponder={() => !disabled}
172
+ onResponderGrant={handleTrackPress}
173
+ {...panResponder.panHandlers}
174
+ >
175
+ <View
176
+ style={[styles.track, { backgroundColor: trackColor }]}
177
+ onLayout={onTrackLayout}
178
+ >
179
+ <Animated.View
180
+ style={[
181
+ styles.fill,
182
+ {
183
+ backgroundColor: activeColor,
184
+ width: animatedValue.interpolate({
185
+ inputRange: [0, trackWidth || 1],
186
+ outputRange: ['0%', '100%'],
187
+ extrapolate: 'clamp',
188
+ }),
189
+ }
190
+ ]}
191
+ />
192
+ </View>
193
+ <Animated.View
194
+ style={[
195
+ styles.thumb,
196
+ {
197
+ backgroundColor: thumbColor,
198
+ transform: [{
199
+ translateX: animatedValue.interpolate({
200
+ inputRange: [0, trackWidth || 1],
201
+ outputRange: [0, trackWidth || 0],
202
+ extrapolate: 'clamp',
203
+ }),
204
+ }],
205
+ }
206
+ ]}
207
+ />
208
+ </View>
209
+
210
+ <View style={styles.rangeLabels}>
211
+ <Text style={styles.rangeLabel}>{minValue}</Text>
212
+ <Text style={styles.rangeLabel}>{maxValue}</Text>
213
+ </View>
214
+ </View>
215
+ );
216
+ };
217
+
218
+ const styles = StyleSheet.create({
219
+ container: {
220
+ width: '100%',
221
+ paddingVertical: 8,
222
+ },
223
+ disabled: {
224
+ opacity: 0.5,
225
+ },
226
+ header: {
227
+ flexDirection: 'row',
228
+ justifyContent: 'space-between',
229
+ alignItems: 'center',
230
+ marginBottom: 12,
231
+ },
232
+ label: {
233
+ fontSize: 16,
234
+ fontWeight: '500',
235
+ color: '#333',
236
+ },
237
+ value: {
238
+ fontSize: 16,
239
+ fontWeight: '600',
240
+ color: '#007AFF',
241
+ },
242
+ sliderContainer: {
243
+ height: 44,
244
+ justifyContent: 'center',
245
+ paddingHorizontal: 10, // Padding for thumb
246
+ },
247
+ track: {
248
+ height: 6,
249
+ borderRadius: 3,
250
+ overflow: 'hidden',
251
+ },
252
+ fill: {
253
+ height: '100%',
254
+ borderRadius: 3,
255
+ },
256
+ thumb: {
257
+ position: 'absolute',
258
+ width: 24,
259
+ height: 24,
260
+ borderRadius: 12,
261
+ marginLeft: -2, // Center thumb on track edge
262
+ shadowColor: '#000',
263
+ shadowOffset: { width: 0, height: 2 },
264
+ shadowOpacity: 0.25,
265
+ shadowRadius: 4,
266
+ elevation: 4,
267
+ borderWidth: 2,
268
+ borderColor: '#fff',
269
+ },
270
+ rangeLabels: {
271
+ flexDirection: 'row',
272
+ justifyContent: 'space-between',
273
+ marginTop: 8,
274
+ paddingHorizontal: 4,
275
+ },
276
+ rangeLabel: {
277
+ fontSize: 12,
278
+ color: '#999',
279
+ },
280
+ });