@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,258 @@
1
+ /**
2
+ * TextInputComponent - React Native
3
+ *
4
+ * Form field for text/email/password input with validation.
5
+ * Features:
6
+ * - Multiple field types (text, email, password, phone, number)
7
+ * - Real-time validation
8
+ * - State manager integration for form data collection
9
+ */
10
+
11
+ import React, { useState, useCallback, useEffect, useMemo } from 'react';
12
+ import {
13
+ View,
14
+ Text,
15
+ TextInput,
16
+ TouchableOpacity,
17
+ StyleSheet,
18
+ KeyboardTypeOptions,
19
+ } from 'react-native';
20
+ import { PaywallComponent } from '../types';
21
+
22
+ interface TextInputComponentProps {
23
+ component: PaywallComponent;
24
+ stateManager?: any;
25
+ isPreviewMode?: boolean;
26
+ }
27
+
28
+ // Validation patterns
29
+ const VALIDATION_PATTERNS = {
30
+ email: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
31
+ phone: /^[\d\s\-+()]{7,20}$/,
32
+ url: /^(https?:\/\/)?([\da-z.-]+)\.([a-z.]{2,6})([/\w .-]*)*\/?$/,
33
+ };
34
+
35
+ export const TextInputComponent: React.FC<TextInputComponentProps> = ({
36
+ component,
37
+ stateManager,
38
+ isPreviewMode = false,
39
+ }) => {
40
+ const props = component.properties as any;
41
+ const componentId = component.id;
42
+
43
+ // Extract props
44
+ const fieldType = props.fieldType || 'text';
45
+ const placeholder = props.placeholder || 'Enter text...';
46
+ const label = props.label;
47
+ const helperText = props.helperText;
48
+ const defaultValue = props.defaultValue || '';
49
+
50
+ const required = props.required || false;
51
+ const minLength = props.minLength || 0;
52
+ const maxLength = props.maxLength || 255;
53
+
54
+ const fontSize = props.fontSize || 16;
55
+ const textColor = props.textColor || '#000000';
56
+ const placeholderColor = props.placeholderColor || '#9CA3AF';
57
+ const labelColor = props.labelColor || '#374151';
58
+ const errorColor = props.errorColor || '#EF4444';
59
+
60
+ const backgroundColor = props.backgroundColor?.color || '#FFFFFF';
61
+ const borderColor = props.borderColor || '#D1D5DB';
62
+ const focusBorderColor = props.focusBorderColor || '#3B82F6';
63
+ const errorBorderColor = props.errorBorderColor || '#EF4444';
64
+ const borderRadius = props.borderRadius || 8;
65
+ const borderWidth = props.borderWidth || 1;
66
+
67
+ const inputHeight = props.inputHeight || 48;
68
+
69
+ const [value, setValue] = useState(defaultValue);
70
+ const [isFocused, setIsFocused] = useState(false);
71
+ const [showPassword, setShowPassword] = useState(false);
72
+ const [errors, setErrors] = useState<string[]>([]);
73
+ const [touched, setTouched] = useState(false);
74
+
75
+ // Validate value
76
+ const validate = useCallback((val: string): string[] => {
77
+ const validationErrors: string[] = [];
78
+
79
+ if (required && !val.trim()) {
80
+ validationErrors.push('This field is required');
81
+ }
82
+ if (val.length > 0 && minLength > 0 && val.length < minLength) {
83
+ validationErrors.push(`Must be at least ${minLength} characters`);
84
+ }
85
+ if (val.length > maxLength) {
86
+ validationErrors.push(`Must be less than ${maxLength} characters`);
87
+ }
88
+ if (fieldType === 'email' && val && !VALIDATION_PATTERNS.email.test(val)) {
89
+ validationErrors.push('Please enter a valid email address');
90
+ }
91
+ if (fieldType === 'phone' && val && !VALIDATION_PATTERNS.phone.test(val)) {
92
+ validationErrors.push('Please enter a valid phone number');
93
+ }
94
+
95
+ return validationErrors;
96
+ }, [required, minLength, maxLength, fieldType]);
97
+
98
+ // Update state manager when value changes
99
+ useEffect(() => {
100
+ if (stateManager) {
101
+ const validationErrors = validate(value);
102
+ const isValid = validationErrors.length === 0;
103
+
104
+ stateManager.updateState(`element:${componentId}.value`, value);
105
+ stateManager.updateState(`element:${componentId}.isValid`, isValid);
106
+ stateManager.updateState(`element:${componentId}.fieldData`, {
107
+ fieldId: componentId,
108
+ fieldType,
109
+ value,
110
+ isValid,
111
+ validationErrors,
112
+ });
113
+ }
114
+ }, [value, componentId, fieldType, stateManager, validate]);
115
+
116
+ // Subscribe to external state changes
117
+ useEffect(() => {
118
+ if (stateManager) {
119
+ return stateManager.subscribeToState(
120
+ `element:${componentId}.value`,
121
+ (newValue: string) => {
122
+ if (newValue !== value) {
123
+ setValue(newValue);
124
+ }
125
+ }
126
+ );
127
+ }
128
+ }, [componentId, stateManager, value]);
129
+
130
+ // Handle input change
131
+ const handleChange = useCallback((text: string) => {
132
+ setValue(text);
133
+ if (touched) {
134
+ setErrors(validate(text));
135
+ }
136
+ }, [touched, validate]);
137
+
138
+ // Handle blur
139
+ const handleBlur = useCallback(() => {
140
+ setIsFocused(false);
141
+ setTouched(true);
142
+ setErrors(validate(value));
143
+ }, [value, validate]);
144
+
145
+ // Determine keyboard type
146
+ const keyboardType: KeyboardTypeOptions = useMemo(() => {
147
+ switch (fieldType) {
148
+ case 'email': return 'email-address';
149
+ case 'phone': return 'phone-pad';
150
+ case 'number': return 'numeric';
151
+ default: return 'default';
152
+ }
153
+ }, [fieldType]);
154
+
155
+ // Current border color
156
+ const currentBorderColor = errors.length > 0 && touched
157
+ ? errorBorderColor
158
+ : isFocused
159
+ ? focusBorderColor
160
+ : borderColor;
161
+
162
+ const hasError = errors.length > 0 && touched;
163
+
164
+ return (
165
+ <View style={styles.container}>
166
+ {/* Label */}
167
+ {label && (
168
+ <Text style={[styles.label, { fontSize: fontSize - 2, color: labelColor }]}>
169
+ {label}
170
+ {required && <Text style={{ color: errorColor }}> *</Text>}
171
+ </Text>
172
+ )}
173
+
174
+ {/* Input Container */}
175
+ <View style={styles.inputContainer}>
176
+ <TextInput
177
+ value={value}
178
+ onChangeText={handleChange}
179
+ onFocus={() => setIsFocused(true)}
180
+ onBlur={handleBlur}
181
+ placeholder={placeholder}
182
+ placeholderTextColor={placeholderColor}
183
+ maxLength={maxLength}
184
+ keyboardType={keyboardType}
185
+ secureTextEntry={fieldType === 'password' && !showPassword}
186
+ autoCapitalize={fieldType === 'email' ? 'none' : 'sentences'}
187
+ autoCorrect={fieldType !== 'email' && fieldType !== 'password'}
188
+ style={[
189
+ styles.input,
190
+ {
191
+ height: inputHeight,
192
+ fontSize,
193
+ color: textColor,
194
+ backgroundColor,
195
+ borderColor: currentBorderColor,
196
+ borderRadius,
197
+ borderWidth,
198
+ paddingRight: fieldType === 'password' ? 48 : 16,
199
+ },
200
+ ]}
201
+ />
202
+
203
+ {/* Password Toggle */}
204
+ {fieldType === 'password' && (
205
+ <TouchableOpacity
206
+ style={styles.passwordToggle}
207
+ onPress={() => setShowPassword(!showPassword)}
208
+ >
209
+ <Text style={{ color: placeholderColor, fontSize: 12 }}>
210
+ {showPassword ? 'HIDE' : 'SHOW'}
211
+ </Text>
212
+ </TouchableOpacity>
213
+ )}
214
+ </View>
215
+
216
+ {/* Helper Text / Error */}
217
+ {(helperText || hasError) && (
218
+ <Text
219
+ style={[
220
+ styles.helperText,
221
+ {
222
+ fontSize: fontSize - 4,
223
+ color: hasError ? errorColor : placeholderColor,
224
+ },
225
+ ]}
226
+ >
227
+ {hasError ? errors[0] : helperText}
228
+ </Text>
229
+ )}
230
+ </View>
231
+ );
232
+ };
233
+
234
+ const styles = StyleSheet.create({
235
+ container: {
236
+ width: '100%',
237
+ },
238
+ label: {
239
+ fontWeight: '500',
240
+ marginBottom: 6,
241
+ },
242
+ inputContainer: {
243
+ position: 'relative',
244
+ },
245
+ input: {
246
+ paddingHorizontal: 16,
247
+ },
248
+ passwordToggle: {
249
+ position: 'absolute',
250
+ right: 16,
251
+ top: 0,
252
+ bottom: 0,
253
+ justifyContent: 'center',
254
+ },
255
+ helperText: {
256
+ marginTop: 4,
257
+ },
258
+ });
@@ -0,0 +1,129 @@
1
+ /**
2
+ * TimelineComponent - React Native
3
+ *
4
+ * Renders a connected timeline with event markers and content.
5
+ * Features:
6
+ * - Vertical timeline layout with connecting lines
7
+ * - Customizable markers and line styles
8
+ * - Support for icons in markers
9
+ */
10
+
11
+ import React from 'react';
12
+ import {
13
+ View,
14
+ StyleSheet,
15
+ } from 'react-native';
16
+ import { PaywallComponent } from '../types';
17
+
18
+ interface TimelineComponentProps {
19
+ component: PaywallComponent;
20
+ stateManager?: any;
21
+ isPreviewMode?: boolean;
22
+ renderComponent?: (component: PaywallComponent) => React.ReactNode;
23
+ }
24
+
25
+ // Helper to parse color from various formats
26
+ const parseColor = (value: any, fallback: string): string => {
27
+ if (!value) return fallback;
28
+ if (typeof value === 'string') return value;
29
+ if (typeof value === 'object' && value.color) return value.color;
30
+ return fallback;
31
+ };
32
+
33
+ export const TimelineComponent: React.FC<TimelineComponentProps> = ({
34
+ component,
35
+ stateManager,
36
+ isPreviewMode = false,
37
+ renderComponent,
38
+ }) => {
39
+ const props = component.properties as any;
40
+
41
+ // Extract timeline-specific styling
42
+ const markerColor = parseColor(props.markerColor, '#3B82F6');
43
+ const markerSize = props.markerSize || 32;
44
+ const markerRadius = props.markerRadius ?? markerSize / 2;
45
+
46
+ const lineColor = parseColor(props.lineColor, '#3B82F6');
47
+ const lineWidthRaw = props.lineWidth;
48
+ const lineWidth = typeof lineWidthRaw === 'object' ? lineWidthRaw.value : (lineWidthRaw || 2);
49
+ const lineStyle = props.lineStyle || 'solid';
50
+
51
+ const eventGap = props.eventGap || 0;
52
+ const contentGap = props.contentGap || 16;
53
+
54
+ const children = component.children || [];
55
+
56
+ return (
57
+ <View style={[styles.container, { gap: eventGap }]}>
58
+ {children.map((child, index) => {
59
+ const isLast = index === children.length - 1;
60
+
61
+ return (
62
+ <View key={child.id || index} style={styles.eventRow}>
63
+ {/* Marker Column */}
64
+ <View style={[styles.markerColumn, { width: markerSize }]}>
65
+ {/* Marker Circle */}
66
+ <View
67
+ style={[
68
+ styles.marker,
69
+ {
70
+ width: markerSize,
71
+ height: markerSize,
72
+ borderRadius: markerRadius,
73
+ backgroundColor: markerColor,
74
+ },
75
+ ]}
76
+ />
77
+
78
+ {/* Connecting Line */}
79
+ {!isLast && (
80
+ <View
81
+ style={[
82
+ styles.line,
83
+ {
84
+ width: lineWidth,
85
+ backgroundColor: lineStyle === 'solid' ? lineColor : undefined,
86
+ borderStyle: lineStyle === 'dashed' ? 'dashed' : lineStyle === 'dotted' ? 'dotted' : undefined,
87
+ borderWidth: lineStyle !== 'solid' ? lineWidth : 0,
88
+ borderColor: lineColor,
89
+ },
90
+ ]}
91
+ />
92
+ )}
93
+ </View>
94
+
95
+ {/* Content Column */}
96
+ <View style={[styles.contentColumn, { marginLeft: contentGap }]}>
97
+ {renderComponent?.(child)}
98
+ </View>
99
+ </View>
100
+ );
101
+ })}
102
+ </View>
103
+ );
104
+ };
105
+
106
+ const styles = StyleSheet.create({
107
+ container: {
108
+ width: '100%',
109
+ },
110
+ eventRow: {
111
+ flexDirection: 'row',
112
+ },
113
+ markerColumn: {
114
+ alignItems: 'center',
115
+ },
116
+ marker: {
117
+ justifyContent: 'center',
118
+ alignItems: 'center',
119
+ },
120
+ line: {
121
+ flex: 1,
122
+ marginTop: 4,
123
+ marginBottom: 4,
124
+ },
125
+ contentColumn: {
126
+ flex: 1,
127
+ paddingBottom: 16,
128
+ },
129
+ });
@@ -0,0 +1,142 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import {
3
+ View,
4
+ Switch,
5
+ StyleSheet,
6
+ } from 'react-native';
7
+ import { PaywallComponent } from '../types';
8
+
9
+ interface ToggleComponentProps {
10
+ component: PaywallComponent;
11
+ stateManager?: any;
12
+ isPreviewMode?: boolean;
13
+ renderComponent?: (component: PaywallComponent) => React.ReactNode;
14
+ }
15
+
16
+ // Size mappings matching web schema
17
+ const SIZE_MAP = {
18
+ sm: { width: 40, height: 20 },
19
+ md: { width: 50, height: 26 },
20
+ lg: { width: 60, height: 32 },
21
+ };
22
+
23
+ /**
24
+ * ToggleComponent - Hybrid component that renders children (label/description) + switch
25
+ * Children are rendered via renderComponent callback, switch is functional
26
+ */
27
+ export const ToggleComponent: React.FC<ToggleComponentProps> = ({
28
+ component,
29
+ stateManager,
30
+ isPreviewMode = false,
31
+ renderComponent,
32
+ }) => {
33
+ const props = component.properties as any;
34
+
35
+ const defaultChecked = props.defaultValue ?? false;
36
+ const disabled = props.disabled ?? false;
37
+ const size = props.size ?? 'md';
38
+
39
+ const trackOnColor = props.trackOnColor ?? '#007AFF';
40
+ const trackOffColor = props.trackOffColor ?? '#767577';
41
+
42
+ const [isChecked, setIsChecked] = useState(defaultChecked);
43
+
44
+ // Register initial state
45
+ useEffect(() => {
46
+ if (stateManager) {
47
+ stateManager.updateState(`element:${component.id}.isChecked`, isChecked);
48
+ }
49
+ }, []);
50
+
51
+ // Subscribe to external state changes
52
+ useEffect(() => {
53
+ if (stateManager) {
54
+ return stateManager.subscribeToState(
55
+ `element:${component.id}.isChecked`,
56
+ (value: boolean) => {
57
+ if (value !== isChecked) {
58
+ setIsChecked(value);
59
+ }
60
+ }
61
+ );
62
+ }
63
+ }, [component.id, stateManager]);
64
+
65
+ const handleToggle = (value: boolean) => {
66
+ if (!disabled) {
67
+ const previousValue = isChecked;
68
+ setIsChecked(value);
69
+ if (stateManager) {
70
+ stateManager.updateState(`element:${component.id}.isChecked`, value);
71
+
72
+ // Emit toggle_change event for analytics
73
+ stateManager.trackEvent?.('toggle_change', {
74
+ componentId: component.id,
75
+ previousValue,
76
+ newValue: value,
77
+ });
78
+ }
79
+ }
80
+ };
81
+
82
+ // Structural inference: content container is the first child where type === 'stack'
83
+ // Label container is the first stack child within content container
84
+ const contentContainer = component.children?.find(child => child.type === 'stack');
85
+ const labelContainer = contentContainer?.children?.find(child => child.type === 'stack');
86
+
87
+ // Fallback: find text children directly if no nested structure
88
+ const textChildren = !labelContainer && contentContainer?.children?.filter(child =>
89
+ child.type === 'text'
90
+ );
91
+
92
+ // Get size dimensions
93
+ const sizeConfig = SIZE_MAP[size as keyof typeof SIZE_MAP] ?? SIZE_MAP.md;
94
+
95
+ return (
96
+ <View style={[styles.container, disabled && styles.disabled]}>
97
+ {/* Render label-container children */}
98
+ {labelContainer && renderComponent && (
99
+ <View style={styles.labelContainer}>
100
+ {renderComponent(labelContainer)}
101
+ </View>
102
+ )}
103
+
104
+ {/* Backward compatibility: render text children directly */}
105
+ {!labelContainer && textChildren && renderComponent && textChildren.map(child => (
106
+ <View key={child.id} style={styles.labelContainer}>
107
+ {renderComponent(child)}
108
+ </View>
109
+ ))}
110
+
111
+ {/* Functional Switch control */}
112
+ <Switch
113
+ value={isChecked}
114
+ onValueChange={handleToggle}
115
+ disabled={disabled}
116
+ trackColor={{ false: trackOffColor, true: trackOnColor }}
117
+ thumbColor={isChecked ? '#fff' : '#f4f3f4'}
118
+ style={{
119
+ transform: [
120
+ { scaleX: sizeConfig.width / 50 },
121
+ { scaleY: sizeConfig.height / 26 }
122
+ ]
123
+ }}
124
+ />
125
+ </View>
126
+ );
127
+ };
128
+
129
+ const styles = StyleSheet.create({
130
+ container: {
131
+ flexDirection: 'row',
132
+ alignItems: 'center',
133
+ justifyContent: 'space-between',
134
+ gap: 12,
135
+ },
136
+ disabled: {
137
+ opacity: 0.5,
138
+ },
139
+ labelContainer: {
140
+ flex: 1,
141
+ },
142
+ });