@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.
- package/BillDogReactNative.podspec +30 -0
- package/LICENSE +21 -0
- package/README.md +330 -0
- package/android/build.gradle +99 -0
- package/android/src/main/AndroidManifest.xml +1 -0
- package/android/src/main/kotlin/io/billdog/reactnative/BillDogABTestModule.kt +317 -0
- package/android/src/main/kotlin/io/billdog/reactnative/BillDogAnalyticsModule.kt +389 -0
- package/android/src/main/kotlin/io/billdog/reactnative/BillDogModule.kt +817 -0
- package/android/src/main/kotlin/io/billdog/reactnative/BillDogPaywallViewManager.kt +383 -0
- package/android/src/main/kotlin/io/billdog/reactnative/BillDogSurveyModule.kt +427 -0
- package/android/src/main/kotlin/io/billdog/reactnative/BillDogVirtualCurrencyModule.kt +407 -0
- package/android/src/main/kotlin/io/billdog/virtualcurrency/BillDogVirtualCurrencyPackage.kt +44 -0
- package/dist/AdvancedPaywallCache.d.ts +70 -0
- package/dist/AdvancedPaywallCache.js +344 -0
- package/dist/BillDog.d.ts +255 -0
- package/dist/BillDog.js +918 -0
- package/dist/BillDogConfiguration.d.ts +145 -0
- package/dist/BillDogConfiguration.js +229 -0
- package/dist/ComponentRenderer.d.ts +17 -0
- package/dist/ComponentRenderer.js +886 -0
- package/dist/LayoutEngine.d.ts +101 -0
- package/dist/LayoutEngine.js +394 -0
- package/dist/LocalizationResolver.d.ts +99 -0
- package/dist/LocalizationResolver.js +266 -0
- package/dist/NativePaywallView.d.ts +36 -0
- package/dist/NativePaywallView.js +59 -0
- package/dist/PaywallCache.d.ts +74 -0
- package/dist/PaywallCache.js +293 -0
- package/dist/PaywallStateManager.d.ts +62 -0
- package/dist/PaywallStateManager.js +147 -0
- package/dist/PaywallView.d.ts +42 -0
- package/dist/PaywallView.js +275 -0
- package/dist/ProductVariableProvider.d.ts +60 -0
- package/dist/ProductVariableProvider.js +239 -0
- package/dist/VariableResolver.d.ts +120 -0
- package/dist/VariableResolver.js +379 -0
- package/dist/WebViewPaywall.d.ts +75 -0
- package/dist/WebViewPaywall.js +360 -0
- package/dist/animations/presets.d.ts +49 -0
- package/dist/animations/presets.js +367 -0
- package/dist/components/AccoladeCpsComponent.d.ts +13 -0
- package/dist/components/AccoladeCpsComponent.js +248 -0
- package/dist/components/AccordionComponent.d.ts +10 -0
- package/dist/components/AccordionComponent.js +187 -0
- package/dist/components/AnimatedWrapper.d.ts +35 -0
- package/dist/components/AnimatedWrapper.js +277 -0
- package/dist/components/CarouselComponent.d.ts +23 -0
- package/dist/components/CarouselComponent.js +484 -0
- package/dist/components/ChartCurveComponent.d.ts +13 -0
- package/dist/components/ChartCurveComponent.js +333 -0
- package/dist/components/CheckboxComponent.d.ts +9 -0
- package/dist/components/CheckboxComponent.js +172 -0
- package/dist/components/ConfettiComponent.d.ts +9 -0
- package/dist/components/ConfettiComponent.js +172 -0
- package/dist/components/CountdownComponent.d.ts +16 -0
- package/dist/components/CountdownComponent.js +414 -0
- package/dist/components/DelayedXComponent.d.ts +12 -0
- package/dist/components/DelayedXComponent.js +240 -0
- package/dist/components/DrawerModalComponent.d.ts +20 -0
- package/dist/components/DrawerModalComponent.js +286 -0
- package/dist/components/GradientView.d.ts +38 -0
- package/dist/components/GradientView.js +126 -0
- package/dist/components/HapticFeedbackComponent.d.ts +16 -0
- package/dist/components/HapticFeedbackComponent.js +64 -0
- package/dist/components/HorizontalProductsComponent.d.ts +17 -0
- package/dist/components/HorizontalProductsComponent.js +284 -0
- package/dist/components/MultiChoiceGridComponent.d.ts +13 -0
- package/dist/components/MultiChoiceGridComponent.js +234 -0
- package/dist/components/NPSScaleComponent.d.ts +16 -0
- package/dist/components/NPSScaleComponent.js +260 -0
- package/dist/components/NavigationComponent.d.ts +26 -0
- package/dist/components/NavigationComponent.js +302 -0
- package/dist/components/OAuthButtonComponent.d.ts +20 -0
- package/dist/components/OAuthButtonComponent.js +237 -0
- package/dist/components/ProgressLoaderComponent.d.ts +13 -0
- package/dist/components/ProgressLoaderComponent.js +308 -0
- package/dist/components/ProgressStepperComponent.d.ts +18 -0
- package/dist/components/ProgressStepperComponent.js +228 -0
- package/dist/components/SegmentedPickerComponent.d.ts +10 -0
- package/dist/components/SegmentedPickerComponent.js +221 -0
- package/dist/components/ShareButtonsComponent.d.ts +21 -0
- package/dist/components/ShareButtonsComponent.js +265 -0
- package/dist/components/ShareCodeComponent.d.ts +16 -0
- package/dist/components/ShareCodeComponent.js +274 -0
- package/dist/components/SingleChoiceGridComponent.d.ts +13 -0
- package/dist/components/SingleChoiceGridComponent.js +210 -0
- package/dist/components/SliderComponent.d.ts +9 -0
- package/dist/components/SliderComponent.js +263 -0
- package/dist/components/StarRatingInputComponent.d.ts +20 -0
- package/dist/components/StarRatingInputComponent.js +198 -0
- package/dist/components/StickerCompositeComponent.d.ts +13 -0
- package/dist/components/StickerCompositeComponent.js +136 -0
- package/dist/components/TabsComponent.d.ts +19 -0
- package/dist/components/TabsComponent.js +431 -0
- package/dist/components/TextInputComponent.d.ts +18 -0
- package/dist/components/TextInputComponent.js +227 -0
- package/dist/components/TimelineComponent.d.ts +19 -0
- package/dist/components/TimelineComponent.js +104 -0
- package/dist/components/ToggleComponent.d.ts +14 -0
- package/dist/components/ToggleComponent.js +132 -0
- package/dist/components/VerticalProductsComponent.d.ts +17 -0
- package/dist/components/VerticalProductsComponent.js +311 -0
- package/dist/dynamicValues.d.ts +36 -0
- package/dist/dynamicValues.js +142 -0
- package/dist/filters/dateFilters.d.ts +50 -0
- package/dist/filters/dateFilters.js +390 -0
- package/dist/filters/index.d.ts +18 -0
- package/dist/filters/index.js +253 -0
- package/dist/hooks/useOfferings.d.ts +76 -0
- package/dist/hooks/useOfferings.js +99 -0
- package/dist/hooks/usePaywall.d.ts +27 -0
- package/dist/hooks/usePaywall.js +114 -0
- package/dist/hooks/usePlacement.d.ts +34 -0
- package/dist/hooks/usePlacement.js +89 -0
- package/dist/hooks/useSubscription.d.ts +40 -0
- package/dist/hooks/useSubscription.js +97 -0
- package/dist/index.d.ts +98 -0
- package/dist/index.js +198 -0
- package/dist/models/BillDogError.d.ts +40 -0
- package/dist/models/BillDogError.js +11 -0
- package/dist/models/CacheFetchPolicy.d.ts +8 -0
- package/dist/models/CacheFetchPolicy.js +4 -0
- package/dist/models/CustomProduct.d.ts +32 -0
- package/dist/models/CustomProduct.js +15 -0
- package/dist/models/CustomerInfo.d.ts +17 -0
- package/dist/models/CustomerInfo.js +11 -0
- package/dist/models/Delegate.d.ts +20 -0
- package/dist/models/Delegate.js +11 -0
- package/dist/models/EntitlementInfo.d.ts +19 -0
- package/dist/models/EntitlementInfo.js +2 -0
- package/dist/models/Placement.d.ts +54 -0
- package/dist/models/Placement.js +15 -0
- package/dist/models/PurchaseParams.d.ts +39 -0
- package/dist/models/PurchaseParams.js +60 -0
- package/dist/models/PurchaseResult.d.ts +30 -0
- package/dist/models/PurchaseResult.js +15 -0
- package/dist/models/RestoreResult.d.ts +30 -0
- package/dist/models/RestoreResult.js +22 -0
- package/dist/models/RestoredItem.d.ts +26 -0
- package/dist/models/RestoredItem.js +13 -0
- package/dist/modules/ABTest.d.ts +65 -0
- package/dist/modules/ABTest.js +113 -0
- package/dist/modules/Analytics.d.ts +91 -0
- package/dist/modules/Analytics.js +182 -0
- package/dist/modules/Survey.d.ts +65 -0
- package/dist/modules/Survey.js +82 -0
- package/dist/modules/VirtualCurrency.d.ts +63 -0
- package/dist/modules/VirtualCurrency.js +147 -0
- package/dist/murmur.d.ts +23 -0
- package/dist/murmur.js +104 -0
- package/dist/notifications/LiveActivitiesManager.d.ts +47 -0
- package/dist/notifications/LiveActivitiesManager.js +106 -0
- package/dist/notifications/NotificationScheduler.d.ts +55 -0
- package/dist/notifications/NotificationScheduler.js +199 -0
- package/dist/notifications/PurchaseNotificationHandler.d.ts +24 -0
- package/dist/notifications/PurchaseNotificationHandler.js +55 -0
- package/dist/notifications/useDeviceToken.d.ts +36 -0
- package/dist/notifications/useDeviceToken.js +97 -0
- package/dist/notifications/useLiveActivity.d.ts +39 -0
- package/dist/notifications/useLiveActivity.js +104 -0
- package/dist/review/ReviewAnalytics.d.ts +34 -0
- package/dist/review/ReviewAnalytics.js +73 -0
- package/dist/review/ReviewManager.d.ts +83 -0
- package/dist/review/ReviewManager.js +339 -0
- package/dist/review/ReviewTimingManager.d.ts +82 -0
- package/dist/review/ReviewTimingManager.js +224 -0
- package/dist/survey-core/types.d.ts +215 -0
- package/dist/survey-core/types.js +8 -0
- package/dist/types.d.ts +438 -0
- package/dist/types.js +158 -0
- package/dist/utils/ApiClient.d.ts +65 -0
- package/dist/utils/ApiClient.js +132 -0
- package/dist/utils/CSSConverter.d.ts +49 -0
- package/dist/utils/CSSConverter.js +470 -0
- package/dist/utils/animations.d.ts +41 -0
- package/dist/utils/animations.js +194 -0
- package/dist/utils/calendarIntegration.d.ts +36 -0
- package/dist/utils/calendarIntegration.js +89 -0
- package/dist/utils/configurationResolver.d.ts +23 -0
- package/dist/utils/configurationResolver.js +78 -0
- package/dist/utils/deviceDetection.d.ts +38 -0
- package/dist/utils/deviceDetection.js +59 -0
- package/dist/utils/hapticFeedback.d.ts +17 -0
- package/dist/utils/hapticFeedback.js +53 -0
- package/dist/utils/iconMapper.d.ts +26 -0
- package/dist/utils/iconMapper.js +113 -0
- package/dist/utils/notificationPermissions.d.ts +13 -0
- package/dist/utils/notificationPermissions.js +51 -0
- package/dist/utils/priceFormatting.d.ts +19 -0
- package/dist/utils/priceFormatting.js +99 -0
- package/dist/utils/shareSheet.d.ts +17 -0
- package/dist/utils/shareSheet.js +42 -0
- package/dist/utils/subscriptionUpgrade.d.ts +84 -0
- package/dist/utils/subscriptionUpgrade.js +100 -0
- package/dist/valueUtils.d.ts +48 -0
- package/dist/valueUtils.js +129 -0
- package/dist/xFlowTags.d.ts +18 -0
- package/dist/xFlowTags.js +385 -0
- package/ios/BillDogABTest.m +50 -0
- package/ios/BillDogABTest.swift +188 -0
- package/ios/BillDogAnalytics.m +105 -0
- package/ios/BillDogAnalytics.swift +324 -0
- package/ios/BillDogBridge.m +222 -0
- package/ios/BillDogBridge.swift +715 -0
- package/ios/BillDogLiveActivities.m +41 -0
- package/ios/BillDogLiveActivities.swift +104 -0
- package/ios/BillDogPaywallViewManager.m +39 -0
- package/ios/BillDogPaywallViewManager.swift +361 -0
- package/ios/BillDogSurvey.m +34 -0
- package/ios/BillDogSurvey.swift +257 -0
- package/ios/BillDogVirtualCurrencyBridge.m +63 -0
- package/ios/BillDogVirtualCurrencyBridge.swift +511 -0
- package/junit.xml +25 -0
- package/package.json +61 -0
- package/react-native.config.js +13 -0
- package/src/AdvancedPaywallCache.ts +433 -0
- package/src/BillDog.ts +983 -0
- package/src/BillDogConfiguration.ts +318 -0
- package/src/ComponentRenderer.tsx +1325 -0
- package/src/LayoutEngine.ts +459 -0
- package/src/LocalizationResolver.ts +311 -0
- package/src/NativePaywallView.tsx +132 -0
- package/src/PaywallCache.ts +358 -0
- package/src/PaywallStateManager.ts +154 -0
- package/src/PaywallView.tsx +328 -0
- package/src/ProductVariableProvider.ts +282 -0
- package/src/VariableResolver.ts +516 -0
- package/src/WebViewPaywall.tsx +454 -0
- package/src/animations/presets.ts +421 -0
- package/src/components/AccoladeCpsComponent.tsx +259 -0
- package/src/components/AccordionComponent.tsx +213 -0
- package/src/components/AnimatedWrapper.tsx +316 -0
- package/src/components/CarouselComponent.tsx +615 -0
- package/src/components/ChartCurveComponent.tsx +452 -0
- package/src/components/CheckboxComponent.tsx +185 -0
- package/src/components/ConfettiComponent.tsx +178 -0
- package/src/components/CountdownComponent.tsx +454 -0
- package/src/components/DelayedXComponent.tsx +266 -0
- package/src/components/DrawerModalComponent.tsx +324 -0
- package/src/components/GradientView.tsx +129 -0
- package/src/components/HapticFeedbackComponent.tsx +39 -0
- package/src/components/HorizontalProductsComponent.tsx +329 -0
- package/src/components/MultiChoiceGridComponent.tsx +265 -0
- package/src/components/NPSScaleComponent.tsx +294 -0
- package/src/components/NavigationComponent.tsx +329 -0
- package/src/components/OAuthButtonComponent.tsx +266 -0
- package/src/components/ProgressLoaderComponent.tsx +366 -0
- package/src/components/ProgressStepperComponent.tsx +250 -0
- package/src/components/SegmentedPickerComponent.tsx +245 -0
- package/src/components/ShareButtonsComponent.tsx +299 -0
- package/src/components/ShareCodeComponent.tsx +303 -0
- package/src/components/SingleChoiceGridComponent.tsx +229 -0
- package/src/components/SliderComponent.tsx +280 -0
- package/src/components/StarRatingInputComponent.tsx +223 -0
- package/src/components/StickerCompositeComponent.tsx +168 -0
- package/src/components/TabsComponent.tsx +496 -0
- package/src/components/TextInputComponent.tsx +258 -0
- package/src/components/TimelineComponent.tsx +129 -0
- package/src/components/ToggleComponent.tsx +142 -0
- package/src/components/VerticalProductsComponent.tsx +357 -0
- package/src/dynamicValues.ts +180 -0
- package/src/filters/dateFilters.ts +411 -0
- package/src/filters/index.ts +281 -0
- package/src/hooks/useOfferings.ts +192 -0
- package/src/hooks/usePaywall.ts +154 -0
- package/src/hooks/usePlacement.ts +143 -0
- package/src/hooks/useSubscription.ts +154 -0
- package/src/index.ts +245 -0
- package/src/models/BillDogError.ts +24 -0
- package/src/models/CacheFetchPolicy.ts +13 -0
- package/src/models/CustomProduct.ts +33 -0
- package/src/models/CustomerInfo.ts +26 -0
- package/src/models/Delegate.ts +22 -0
- package/src/models/EntitlementInfo.ts +27 -0
- package/src/models/Placement.ts +50 -0
- package/src/models/PurchaseParams.ts +81 -0
- package/src/models/PurchaseResult.ts +32 -0
- package/src/models/RestoreResult.ts +27 -0
- package/src/models/RestoredItem.ts +25 -0
- package/src/modules/ABTest.ts +131 -0
- package/src/modules/Analytics.ts +227 -0
- package/src/modules/Survey.ts +126 -0
- package/src/modules/VirtualCurrency.ts +223 -0
- package/src/murmur.ts +102 -0
- package/src/notifications/LiveActivitiesManager.ts +140 -0
- package/src/notifications/NotificationScheduler.ts +292 -0
- package/src/notifications/PurchaseNotificationHandler.ts +67 -0
- package/src/notifications/useDeviceToken.ts +116 -0
- package/src/notifications/useLiveActivity.ts +125 -0
- package/src/review/ReviewAnalytics.ts +89 -0
- package/src/review/ReviewManager.ts +355 -0
- package/src/review/ReviewTimingManager.ts +265 -0
- package/src/survey-core/types.ts +244 -0
- package/src/types/ambient.d.ts +9 -0
- package/src/types.ts +680 -0
- package/src/utils/ApiClient.ts +163 -0
- package/src/utils/CSSConverter.ts +552 -0
- package/src/utils/animations.ts +232 -0
- package/src/utils/calendarIntegration.ts +113 -0
- package/src/utils/configurationResolver.ts +106 -0
- package/src/utils/deviceDetection.ts +83 -0
- package/src/utils/hapticFeedback.ts +55 -0
- package/src/utils/iconMapper.ts +102 -0
- package/src/utils/notificationPermissions.ts +48 -0
- package/src/utils/priceFormatting.ts +119 -0
- package/src/utils/shareSheet.ts +55 -0
- package/src/utils/subscriptionUpgrade.ts +183 -0
- package/src/valueUtils.ts +108 -0
- package/src/xFlowTags.ts +463 -0
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NPSScaleComponent - React Native
|
|
3
|
+
*
|
|
4
|
+
* Net Promoter Score scale with child-based architecture.
|
|
5
|
+
* Renders children with nps-button-* roles, applies selectedAppearance for selected state.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React, { useState, useCallback, useEffect, useMemo } from 'react';
|
|
9
|
+
import {
|
|
10
|
+
View,
|
|
11
|
+
Text,
|
|
12
|
+
TouchableOpacity,
|
|
13
|
+
StyleSheet,
|
|
14
|
+
Animated,
|
|
15
|
+
} from 'react-native';
|
|
16
|
+
import { PaywallComponent } from '../types';
|
|
17
|
+
|
|
18
|
+
interface NPSScaleComponentProps {
|
|
19
|
+
component: PaywallComponent;
|
|
20
|
+
stateManager?: any;
|
|
21
|
+
isPreviewMode?: boolean;
|
|
22
|
+
renderChild?: (child: PaywallComponent) => React.ReactNode;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// NPS Categories
|
|
26
|
+
const getNPSCategory = (value: number, min: number, max: number): string => {
|
|
27
|
+
const normalizedMax = max - min;
|
|
28
|
+
const normalizedValue = value - min;
|
|
29
|
+
const percentage = normalizedValue / normalizedMax;
|
|
30
|
+
|
|
31
|
+
if (percentage <= 0.6) return 'detractor';
|
|
32
|
+
if (percentage <= 0.8) return 'passive';
|
|
33
|
+
return 'promoter';
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// Extract value from role like "nps-button-5"
|
|
37
|
+
const extractValueFromRole = (role?: string): number => {
|
|
38
|
+
if (!role || !role.startsWith('nps-button-')) return 0;
|
|
39
|
+
const valueStr = role.replace('nps-button-', '');
|
|
40
|
+
return parseInt(valueStr, 10) || 0;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const NPSScaleComponent: React.FC<NPSScaleComponentProps> = ({
|
|
44
|
+
component,
|
|
45
|
+
stateManager,
|
|
46
|
+
isPreviewMode = false,
|
|
47
|
+
renderChild,
|
|
48
|
+
}) => {
|
|
49
|
+
const props = component.properties as any;
|
|
50
|
+
const componentId = component.id;
|
|
51
|
+
|
|
52
|
+
// Configuration
|
|
53
|
+
const minValue = props.minValue ?? 0;
|
|
54
|
+
const maxValue = props.maxValue ?? 10;
|
|
55
|
+
const defaultValue = props.defaultValue ?? -1;
|
|
56
|
+
const showLabels = props.showLabels !== false;
|
|
57
|
+
const minLabel = props.minLabel || 'Not at all likely';
|
|
58
|
+
const maxLabel = props.maxLabel || 'Extremely likely';
|
|
59
|
+
const buttonGap = props.buttonGap || 4;
|
|
60
|
+
|
|
61
|
+
const [selectedValue, setSelectedValue] = useState<number>(defaultValue);
|
|
62
|
+
const [animatedValues] = useState(() =>
|
|
63
|
+
Array.from({ length: maxValue - minValue + 1 }, () => new Animated.Value(1))
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
// Get NPS button children sorted by value
|
|
67
|
+
const npsButtonChildren = useMemo(() => {
|
|
68
|
+
const children = component.children || [];
|
|
69
|
+
return children
|
|
70
|
+
.filter((child: PaywallComponent) => child.role?.startsWith('nps-button-'))
|
|
71
|
+
.sort((a: PaywallComponent, b: PaywallComponent) => {
|
|
72
|
+
const valueA = extractValueFromRole(a.role);
|
|
73
|
+
const valueB = extractValueFromRole(b.role);
|
|
74
|
+
return valueA - valueB;
|
|
75
|
+
});
|
|
76
|
+
}, [component.children]);
|
|
77
|
+
|
|
78
|
+
const hasChildren = npsButtonChildren.length > 0 && renderChild;
|
|
79
|
+
|
|
80
|
+
// Update state manager when value changes
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
if (stateManager && selectedValue >= minValue) {
|
|
83
|
+
const category = getNPSCategory(selectedValue, minValue, maxValue);
|
|
84
|
+
|
|
85
|
+
stateManager.updateState(`element:${componentId}.score`, selectedValue);
|
|
86
|
+
stateManager.updateState(`element:${componentId}.category`, category);
|
|
87
|
+
stateManager.updateState(`element:${componentId}.isValid`, true);
|
|
88
|
+
}
|
|
89
|
+
}, [selectedValue, componentId, minValue, maxValue, stateManager]);
|
|
90
|
+
|
|
91
|
+
// Subscribe to external state changes
|
|
92
|
+
useEffect(() => {
|
|
93
|
+
if (stateManager) {
|
|
94
|
+
return stateManager.subscribeToState(
|
|
95
|
+
`element:${componentId}.score`,
|
|
96
|
+
(value: number) => {
|
|
97
|
+
if (value !== selectedValue) {
|
|
98
|
+
setSelectedValue(value);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
}, [componentId, stateManager, selectedValue]);
|
|
104
|
+
|
|
105
|
+
// Handle value selection
|
|
106
|
+
const handleSelect = useCallback((value: number, index: number) => {
|
|
107
|
+
// Animate button
|
|
108
|
+
Animated.sequence([
|
|
109
|
+
Animated.timing(animatedValues[index], {
|
|
110
|
+
toValue: 0.9,
|
|
111
|
+
duration: 50,
|
|
112
|
+
useNativeDriver: true,
|
|
113
|
+
}),
|
|
114
|
+
Animated.timing(animatedValues[index], {
|
|
115
|
+
toValue: 1.1,
|
|
116
|
+
duration: 100,
|
|
117
|
+
useNativeDriver: true,
|
|
118
|
+
}),
|
|
119
|
+
Animated.timing(animatedValues[index], {
|
|
120
|
+
toValue: 1,
|
|
121
|
+
duration: 100,
|
|
122
|
+
useNativeDriver: true,
|
|
123
|
+
}),
|
|
124
|
+
]).start();
|
|
125
|
+
|
|
126
|
+
setSelectedValue(value);
|
|
127
|
+
}, [animatedValues]);
|
|
128
|
+
|
|
129
|
+
// Apply selectedAppearance to child when selected
|
|
130
|
+
const applySelectedAppearance = useCallback((child: PaywallComponent, isSelected: boolean): PaywallComponent => {
|
|
131
|
+
if (!isSelected || !child.selectedAppearance) {
|
|
132
|
+
return child;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Create a copy with selectedAppearance merged into appearance
|
|
136
|
+
return {
|
|
137
|
+
...child,
|
|
138
|
+
appearance: child.selectedAppearance,
|
|
139
|
+
};
|
|
140
|
+
}, []);
|
|
141
|
+
|
|
142
|
+
// Get styling from component.properties for fallback rendering (canonical direct properties)
|
|
143
|
+
const getSelectionStyles = useCallback(() => {
|
|
144
|
+
const componentAppearance = (component as any).appearance || {};
|
|
145
|
+
|
|
146
|
+
// Read from component.properties (canonical names per Property Parity Contract)
|
|
147
|
+
const buttonFill = props.buttonFill;
|
|
148
|
+
const selectedFill = props.selectedFill;
|
|
149
|
+
|
|
150
|
+
const buttonSize = props.buttonSize ?? 40;
|
|
151
|
+
const borderRadius = componentAppearance.cornerRadius ?? props.borderRadius ?? 8;
|
|
152
|
+
const fontSize = props.labelFontSize ?? 14;
|
|
153
|
+
|
|
154
|
+
// Extract colors from FillValue objects
|
|
155
|
+
const activeBackground = selectedFill?.resolvedColor ?? selectedFill?.color ?? '#3B82F6';
|
|
156
|
+
const inactiveBackground = buttonFill?.resolvedColor ?? buttonFill?.color ??
|
|
157
|
+
componentAppearance.fill?.resolvedColor ?? componentAppearance.fill?.color ?? '#E5E7EB';
|
|
158
|
+
const activeTextColor = props.selectedTextColor ?? '#FFFFFF';
|
|
159
|
+
const inactiveTextColor = props.textColor ?? '#6B7280';
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
buttonSize,
|
|
163
|
+
borderRadius,
|
|
164
|
+
fontSize,
|
|
165
|
+
activeBackground,
|
|
166
|
+
inactiveBackground,
|
|
167
|
+
activeTextColor,
|
|
168
|
+
inactiveTextColor,
|
|
169
|
+
};
|
|
170
|
+
}, [component, props]);
|
|
171
|
+
|
|
172
|
+
const selectionStyles = getSelectionStyles();
|
|
173
|
+
|
|
174
|
+
// Render child-based buttons
|
|
175
|
+
const renderChildBasedButtons = () => {
|
|
176
|
+
return npsButtonChildren.map((child: PaywallComponent, index: number) => {
|
|
177
|
+
const value = extractValueFromRole(child.role);
|
|
178
|
+
const isSelected = selectedValue === value;
|
|
179
|
+
|
|
180
|
+
return (
|
|
181
|
+
<TouchableOpacity
|
|
182
|
+
key={child.id}
|
|
183
|
+
onPress={() => handleSelect(value, index)}
|
|
184
|
+
activeOpacity={0.7}
|
|
185
|
+
style={{ marginHorizontal: buttonGap / 2 }}
|
|
186
|
+
>
|
|
187
|
+
<Animated.View
|
|
188
|
+
style={{
|
|
189
|
+
transform: [{ scale: animatedValues[index] }],
|
|
190
|
+
}}
|
|
191
|
+
>
|
|
192
|
+
{renderChild!(applySelectedAppearance(child, isSelected))}
|
|
193
|
+
</Animated.View>
|
|
194
|
+
</TouchableOpacity>
|
|
195
|
+
);
|
|
196
|
+
});
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
// Render fallback buttons when no children available
|
|
200
|
+
const renderFallbackButtons = () => {
|
|
201
|
+
const values: number[] = [];
|
|
202
|
+
for (let i = minValue; i <= maxValue; i++) {
|
|
203
|
+
values.push(i);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return values.map((value, index) => {
|
|
207
|
+
const isSelected = selectedValue === value;
|
|
208
|
+
const backgroundColor = isSelected
|
|
209
|
+
? selectionStyles.activeBackground
|
|
210
|
+
: selectionStyles.inactiveBackground;
|
|
211
|
+
const textColor = isSelected
|
|
212
|
+
? selectionStyles.activeTextColor
|
|
213
|
+
: selectionStyles.inactiveTextColor;
|
|
214
|
+
|
|
215
|
+
return (
|
|
216
|
+
<TouchableOpacity
|
|
217
|
+
key={value}
|
|
218
|
+
onPress={() => handleSelect(value, index)}
|
|
219
|
+
activeOpacity={0.7}
|
|
220
|
+
style={{ marginHorizontal: buttonGap / 2 }}
|
|
221
|
+
>
|
|
222
|
+
<Animated.View
|
|
223
|
+
style={[
|
|
224
|
+
styles.button,
|
|
225
|
+
{
|
|
226
|
+
width: selectionStyles.buttonSize,
|
|
227
|
+
height: selectionStyles.buttonSize,
|
|
228
|
+
borderRadius: selectionStyles.borderRadius,
|
|
229
|
+
backgroundColor,
|
|
230
|
+
transform: [{ scale: animatedValues[index] }],
|
|
231
|
+
},
|
|
232
|
+
]}
|
|
233
|
+
>
|
|
234
|
+
<Text style={[
|
|
235
|
+
styles.buttonText,
|
|
236
|
+
{
|
|
237
|
+
fontSize: selectionStyles.fontSize,
|
|
238
|
+
color: textColor,
|
|
239
|
+
fontWeight: isSelected ? '600' : '500',
|
|
240
|
+
}
|
|
241
|
+
]}>
|
|
242
|
+
{value}
|
|
243
|
+
</Text>
|
|
244
|
+
</Animated.View>
|
|
245
|
+
</TouchableOpacity>
|
|
246
|
+
);
|
|
247
|
+
});
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
return (
|
|
251
|
+
<View style={styles.container}>
|
|
252
|
+
{/* Scale Buttons */}
|
|
253
|
+
<View style={[styles.scaleContainer, { gap: buttonGap }]}>
|
|
254
|
+
{hasChildren ? renderChildBasedButtons() : renderFallbackButtons()}
|
|
255
|
+
</View>
|
|
256
|
+
|
|
257
|
+
{/* Labels */}
|
|
258
|
+
{showLabels && (
|
|
259
|
+
<View style={styles.labelsContainer}>
|
|
260
|
+
<Text style={styles.labelText}>{minLabel}</Text>
|
|
261
|
+
<Text style={styles.labelText}>{maxLabel}</Text>
|
|
262
|
+
</View>
|
|
263
|
+
)}
|
|
264
|
+
</View>
|
|
265
|
+
);
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
const styles = StyleSheet.create({
|
|
269
|
+
container: {
|
|
270
|
+
width: '100%',
|
|
271
|
+
gap: 16,
|
|
272
|
+
},
|
|
273
|
+
scaleContainer: {
|
|
274
|
+
flexDirection: 'row',
|
|
275
|
+
flexWrap: 'wrap',
|
|
276
|
+
justifyContent: 'center',
|
|
277
|
+
},
|
|
278
|
+
button: {
|
|
279
|
+
alignItems: 'center',
|
|
280
|
+
justifyContent: 'center',
|
|
281
|
+
},
|
|
282
|
+
buttonText: {
|
|
283
|
+
fontWeight: '500',
|
|
284
|
+
},
|
|
285
|
+
labelsContainer: {
|
|
286
|
+
flexDirection: 'row',
|
|
287
|
+
justifyContent: 'space-between',
|
|
288
|
+
paddingHorizontal: 4,
|
|
289
|
+
},
|
|
290
|
+
labelText: {
|
|
291
|
+
fontSize: 12,
|
|
292
|
+
color: '#6B7280',
|
|
293
|
+
},
|
|
294
|
+
});
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NavigationComponent - React Native
|
|
3
|
+
*
|
|
4
|
+
* Multi-screen paywall navigation with state management.
|
|
5
|
+
* Features:
|
|
6
|
+
* - Page stack navigation
|
|
7
|
+
* - State integration for current page tracking
|
|
8
|
+
* - Transition animations (slide, fade, push, none)
|
|
9
|
+
* - Swipe gesture navigation
|
|
10
|
+
* - Forward/back navigation
|
|
11
|
+
* - Entry/exit condition support
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import React, { useState, useEffect, useRef, useCallback } from 'react';
|
|
15
|
+
import {
|
|
16
|
+
View,
|
|
17
|
+
Animated,
|
|
18
|
+
PanResponder,
|
|
19
|
+
Dimensions,
|
|
20
|
+
StyleSheet,
|
|
21
|
+
} from 'react-native';
|
|
22
|
+
import { PaywallComponent } from '../types';
|
|
23
|
+
|
|
24
|
+
interface NavigationComponentProps {
|
|
25
|
+
component: PaywallComponent;
|
|
26
|
+
/** Child components to render within the navigation (defaults to component.children) */
|
|
27
|
+
children?: PaywallComponent[];
|
|
28
|
+
stateManager?: any;
|
|
29
|
+
isPreviewMode?: boolean;
|
|
30
|
+
renderComponent?: (component: PaywallComponent) => React.ReactNode;
|
|
31
|
+
/** Alias for renderComponent used by ComponentRenderer */
|
|
32
|
+
renderChild?: (child: PaywallComponent, index?: number) => React.ReactNode;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
type TransitionType = 'slide' | 'fade' | 'push' | 'none';
|
|
36
|
+
type TransitionDirection = 'horizontal' | 'vertical';
|
|
37
|
+
|
|
38
|
+
const { width: SCREEN_WIDTH, height: SCREEN_HEIGHT } = Dimensions.get('window');
|
|
39
|
+
|
|
40
|
+
export const NavigationComponent: React.FC<NavigationComponentProps> = ({
|
|
41
|
+
component,
|
|
42
|
+
stateManager,
|
|
43
|
+
isPreviewMode = false,
|
|
44
|
+
renderComponent,
|
|
45
|
+
}) => {
|
|
46
|
+
const props = component.properties as any;
|
|
47
|
+
const componentId = component.id;
|
|
48
|
+
|
|
49
|
+
// Configuration
|
|
50
|
+
const transition: TransitionType = props.transition || 'slide';
|
|
51
|
+
const transitionDirection: TransitionDirection = props.transitionDirection || 'horizontal';
|
|
52
|
+
const transitionDuration = props.transitionDuration || 300;
|
|
53
|
+
const swipeEnabled = props.swipeEnabled !== false;
|
|
54
|
+
const swipeThreshold = props.swipeThreshold || 0.3;
|
|
55
|
+
const initialPage = props.initialPage || 0;
|
|
56
|
+
|
|
57
|
+
// Get pages from children
|
|
58
|
+
const pages = component.children || [];
|
|
59
|
+
const totalPages = pages.length;
|
|
60
|
+
|
|
61
|
+
// State
|
|
62
|
+
const [currentIndex, setCurrentIndex] = useState(initialPage);
|
|
63
|
+
const [history, setHistory] = useState<number[]>([initialPage]);
|
|
64
|
+
const animatedValue = useRef(new Animated.Value(0)).current;
|
|
65
|
+
const panValue = useRef(new Animated.Value(0)).current;
|
|
66
|
+
|
|
67
|
+
// State manager integration
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
if (stateManager) {
|
|
70
|
+
// Initialize state
|
|
71
|
+
stateManager.updateState(`element:${componentId}.currentIndex`, currentIndex);
|
|
72
|
+
stateManager.updateState(`element:${componentId}.totalPages`, totalPages);
|
|
73
|
+
stateManager.updateState(`element:${componentId}.canGoBack`, history.length > 1);
|
|
74
|
+
stateManager.updateState(`element:${componentId}.history`, history);
|
|
75
|
+
|
|
76
|
+
// Subscribe to external navigation commands
|
|
77
|
+
const unsubscribe = stateManager.subscribeToState(
|
|
78
|
+
`element:${componentId}.navigateTo`,
|
|
79
|
+
(value: number | null) => {
|
|
80
|
+
if (value !== null && value >= 0 && value < totalPages && value !== currentIndex) {
|
|
81
|
+
navigateToPage(value);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
return unsubscribe;
|
|
87
|
+
}
|
|
88
|
+
}, [componentId, stateManager, currentIndex, totalPages, history]);
|
|
89
|
+
|
|
90
|
+
// Update state manager when current index changes
|
|
91
|
+
useEffect(() => {
|
|
92
|
+
if (stateManager) {
|
|
93
|
+
stateManager.updateState(`element:${componentId}.currentIndex`, currentIndex);
|
|
94
|
+
stateManager.updateState(`element:${componentId}.canGoBack`, history.length > 1);
|
|
95
|
+
stateManager.updateState(`element:${componentId}.history`, history);
|
|
96
|
+
}
|
|
97
|
+
}, [currentIndex, history, componentId, stateManager]);
|
|
98
|
+
|
|
99
|
+
// Navigate to a specific page
|
|
100
|
+
const navigateToPage = useCallback((pageIndex: number, options?: { replace?: boolean }) => {
|
|
101
|
+
if (pageIndex < 0 || pageIndex >= totalPages || pageIndex === currentIndex) return;
|
|
102
|
+
|
|
103
|
+
const direction = pageIndex > currentIndex ? 1 : -1;
|
|
104
|
+
|
|
105
|
+
// Animate transition
|
|
106
|
+
Animated.timing(animatedValue, {
|
|
107
|
+
toValue: direction,
|
|
108
|
+
duration: transitionDuration,
|
|
109
|
+
useNativeDriver: true,
|
|
110
|
+
}).start(() => {
|
|
111
|
+
setCurrentIndex(pageIndex);
|
|
112
|
+
|
|
113
|
+
// Update history
|
|
114
|
+
if (options?.replace) {
|
|
115
|
+
setHistory(prev => [...prev.slice(0, -1), pageIndex]);
|
|
116
|
+
} else {
|
|
117
|
+
setHistory(prev => [...prev, pageIndex]);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
animatedValue.setValue(0);
|
|
121
|
+
});
|
|
122
|
+
}, [currentIndex, totalPages, transitionDuration, animatedValue]);
|
|
123
|
+
|
|
124
|
+
// Go back in history
|
|
125
|
+
const goBack = useCallback(() => {
|
|
126
|
+
if (history.length <= 1) return;
|
|
127
|
+
|
|
128
|
+
const newHistory = [...history];
|
|
129
|
+
newHistory.pop();
|
|
130
|
+
const previousPage = newHistory[newHistory.length - 1];
|
|
131
|
+
|
|
132
|
+
Animated.timing(animatedValue, {
|
|
133
|
+
toValue: -1,
|
|
134
|
+
duration: transitionDuration,
|
|
135
|
+
useNativeDriver: true,
|
|
136
|
+
}).start(() => {
|
|
137
|
+
setCurrentIndex(previousPage);
|
|
138
|
+
setHistory(newHistory);
|
|
139
|
+
animatedValue.setValue(0);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
if (stateManager) {
|
|
143
|
+
stateManager.updateState(`element:${componentId}.currentIndex`, previousPage);
|
|
144
|
+
}
|
|
145
|
+
}, [history, transitionDuration, animatedValue, componentId, stateManager]);
|
|
146
|
+
|
|
147
|
+
// Go to next page
|
|
148
|
+
const goNext = useCallback(() => {
|
|
149
|
+
if (currentIndex < totalPages - 1) {
|
|
150
|
+
navigateToPage(currentIndex + 1);
|
|
151
|
+
}
|
|
152
|
+
}, [currentIndex, totalPages, navigateToPage]);
|
|
153
|
+
|
|
154
|
+
// Go to previous page
|
|
155
|
+
const goPrevious = useCallback(() => {
|
|
156
|
+
if (currentIndex > 0) {
|
|
157
|
+
navigateToPage(currentIndex - 1);
|
|
158
|
+
}
|
|
159
|
+
}, [currentIndex, navigateToPage]);
|
|
160
|
+
|
|
161
|
+
// Pan responder for swipe navigation
|
|
162
|
+
const panResponder = useRef(
|
|
163
|
+
PanResponder.create({
|
|
164
|
+
onStartShouldSetPanResponder: () => false,
|
|
165
|
+
onMoveShouldSetPanResponder: (_, gestureState) => {
|
|
166
|
+
if (!swipeEnabled) return false;
|
|
167
|
+
|
|
168
|
+
const { dx, dy } = gestureState;
|
|
169
|
+
const isHorizontal = transitionDirection === 'horizontal';
|
|
170
|
+
|
|
171
|
+
if (isHorizontal) {
|
|
172
|
+
return Math.abs(dx) > Math.abs(dy) && Math.abs(dx) > 10;
|
|
173
|
+
} else {
|
|
174
|
+
return Math.abs(dy) > Math.abs(dx) && Math.abs(dy) > 10;
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
onPanResponderMove: (_, gestureState) => {
|
|
178
|
+
const { dx, dy } = gestureState;
|
|
179
|
+
const isHorizontal = transitionDirection === 'horizontal';
|
|
180
|
+
const delta = isHorizontal ? dx : dy;
|
|
181
|
+
const size = isHorizontal ? SCREEN_WIDTH : SCREEN_HEIGHT;
|
|
182
|
+
|
|
183
|
+
// Normalize pan value between -1 and 1
|
|
184
|
+
const normalizedDelta = delta / size;
|
|
185
|
+
|
|
186
|
+
// Check boundaries
|
|
187
|
+
if (normalizedDelta > 0 && currentIndex === 0) {
|
|
188
|
+
panValue.setValue(normalizedDelta * 0.2); // Resistance at start
|
|
189
|
+
} else if (normalizedDelta < 0 && currentIndex === totalPages - 1) {
|
|
190
|
+
panValue.setValue(normalizedDelta * 0.2); // Resistance at end
|
|
191
|
+
} else {
|
|
192
|
+
panValue.setValue(normalizedDelta);
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
onPanResponderRelease: (_, gestureState) => {
|
|
196
|
+
const { dx, dy, vx, vy } = gestureState;
|
|
197
|
+
const isHorizontal = transitionDirection === 'horizontal';
|
|
198
|
+
const delta = isHorizontal ? dx : dy;
|
|
199
|
+
const velocity = isHorizontal ? vx : vy;
|
|
200
|
+
const size = isHorizontal ? SCREEN_WIDTH : SCREEN_HEIGHT;
|
|
201
|
+
|
|
202
|
+
const normalizedDelta = delta / size;
|
|
203
|
+
|
|
204
|
+
// Determine if we should navigate
|
|
205
|
+
if (Math.abs(normalizedDelta) > swipeThreshold || Math.abs(velocity) > 0.5) {
|
|
206
|
+
if (normalizedDelta > 0 && currentIndex > 0) {
|
|
207
|
+
// Swipe right/down - go back
|
|
208
|
+
Animated.timing(panValue, {
|
|
209
|
+
toValue: 1,
|
|
210
|
+
duration: 200,
|
|
211
|
+
useNativeDriver: true,
|
|
212
|
+
}).start(() => {
|
|
213
|
+
panValue.setValue(0);
|
|
214
|
+
goPrevious();
|
|
215
|
+
});
|
|
216
|
+
} else if (normalizedDelta < 0 && currentIndex < totalPages - 1) {
|
|
217
|
+
// Swipe left/up - go forward
|
|
218
|
+
Animated.timing(panValue, {
|
|
219
|
+
toValue: -1,
|
|
220
|
+
duration: 200,
|
|
221
|
+
useNativeDriver: true,
|
|
222
|
+
}).start(() => {
|
|
223
|
+
panValue.setValue(0);
|
|
224
|
+
goNext();
|
|
225
|
+
});
|
|
226
|
+
} else {
|
|
227
|
+
// Snap back
|
|
228
|
+
Animated.spring(panValue, {
|
|
229
|
+
toValue: 0,
|
|
230
|
+
useNativeDriver: true,
|
|
231
|
+
}).start();
|
|
232
|
+
}
|
|
233
|
+
} else {
|
|
234
|
+
// Snap back
|
|
235
|
+
Animated.spring(panValue, {
|
|
236
|
+
toValue: 0,
|
|
237
|
+
useNativeDriver: true,
|
|
238
|
+
}).start();
|
|
239
|
+
}
|
|
240
|
+
},
|
|
241
|
+
})
|
|
242
|
+
).current;
|
|
243
|
+
|
|
244
|
+
// Calculate animated transform based on transition type
|
|
245
|
+
const getTransformStyle = (pageOffset: number) => {
|
|
246
|
+
const isHorizontal = transitionDirection === 'horizontal';
|
|
247
|
+
const size = isHorizontal ? SCREEN_WIDTH : SCREEN_HEIGHT;
|
|
248
|
+
|
|
249
|
+
const combinedOffset = Animated.add(
|
|
250
|
+
animatedValue,
|
|
251
|
+
panValue
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
switch (transition) {
|
|
255
|
+
case 'slide':
|
|
256
|
+
const translateX = isHorizontal
|
|
257
|
+
? Animated.multiply(combinedOffset, new Animated.Value(-size)).interpolate({
|
|
258
|
+
inputRange: [-1, 0, 1],
|
|
259
|
+
outputRange: [size + pageOffset * size, pageOffset * size, -size + pageOffset * size],
|
|
260
|
+
})
|
|
261
|
+
: 0;
|
|
262
|
+
const translateY = !isHorizontal
|
|
263
|
+
? Animated.multiply(combinedOffset, new Animated.Value(-size)).interpolate({
|
|
264
|
+
inputRange: [-1, 0, 1],
|
|
265
|
+
outputRange: [size + pageOffset * size, pageOffset * size, -size + pageOffset * size],
|
|
266
|
+
})
|
|
267
|
+
: 0;
|
|
268
|
+
return { transform: [{ translateX }, { translateY }] };
|
|
269
|
+
|
|
270
|
+
case 'fade':
|
|
271
|
+
return {
|
|
272
|
+
opacity: combinedOffset.interpolate({
|
|
273
|
+
inputRange: [-1, 0, 1],
|
|
274
|
+
outputRange: pageOffset === 0 ? [0, 1, 0] : [1, 0, 1],
|
|
275
|
+
}),
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
case 'push':
|
|
279
|
+
const pushTranslate = isHorizontal
|
|
280
|
+
? combinedOffset.interpolate({
|
|
281
|
+
inputRange: [-1, 0, 1],
|
|
282
|
+
outputRange: [size * 0.3, pageOffset * size, -size * 0.3],
|
|
283
|
+
})
|
|
284
|
+
: combinedOffset.interpolate({
|
|
285
|
+
inputRange: [-1, 0, 1],
|
|
286
|
+
outputRange: [size * 0.3, pageOffset * size, -size * 0.3],
|
|
287
|
+
});
|
|
288
|
+
const scale = combinedOffset.interpolate({
|
|
289
|
+
inputRange: [-1, 0, 1],
|
|
290
|
+
outputRange: pageOffset === 0 ? [0.9, 1, 0.9] : [1, 0.9, 1],
|
|
291
|
+
});
|
|
292
|
+
return {
|
|
293
|
+
transform: [
|
|
294
|
+
isHorizontal ? { translateX: pushTranslate } : { translateY: pushTranslate },
|
|
295
|
+
{ scale },
|
|
296
|
+
],
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
case 'none':
|
|
300
|
+
default:
|
|
301
|
+
return {};
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
// Render current page
|
|
306
|
+
const currentPage = pages[currentIndex];
|
|
307
|
+
|
|
308
|
+
if (!currentPage) {
|
|
309
|
+
return null;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return (
|
|
313
|
+
<View style={styles.container} {...panResponder.panHandlers}>
|
|
314
|
+
<Animated.View style={[styles.page, getTransformStyle(0)]}>
|
|
315
|
+
{renderComponent?.(currentPage)}
|
|
316
|
+
</Animated.View>
|
|
317
|
+
</View>
|
|
318
|
+
);
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
const styles = StyleSheet.create({
|
|
322
|
+
container: {
|
|
323
|
+
flex: 1,
|
|
324
|
+
overflow: 'hidden',
|
|
325
|
+
},
|
|
326
|
+
page: {
|
|
327
|
+
...StyleSheet.absoluteFillObject,
|
|
328
|
+
},
|
|
329
|
+
});
|