@umituz/react-native-subscription 2.12.1 → 2.12.3

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 (45) hide show
  1. package/package.json +3 -3
  2. package/src/domains/index.ts +5 -0
  3. package/src/domains/paywall/components/CreditCard.tsx +117 -0
  4. package/src/domains/paywall/components/FeatureItem.tsx +50 -0
  5. package/src/domains/paywall/components/FeatureList.tsx +34 -0
  6. package/src/domains/paywall/components/PaywallFooter.tsx +98 -0
  7. package/src/domains/paywall/components/PaywallHeader.tsx +115 -0
  8. package/src/domains/paywall/components/PaywallModal.tsx +187 -0
  9. package/src/domains/paywall/components/PaywallTabBar.tsx +102 -0
  10. package/src/domains/paywall/components/PlanCard.tsx +124 -0
  11. package/src/domains/paywall/components/index.ts +14 -0
  12. package/src/domains/paywall/entities/index.ts +5 -0
  13. package/src/domains/paywall/entities/types.ts +48 -0
  14. package/src/domains/paywall/hooks/index.ts +6 -0
  15. package/src/{presentation → domains/paywall}/hooks/usePaywall.ts +1 -1
  16. package/src/domains/paywall/index.ts +13 -0
  17. package/src/index.ts +17 -21
  18. package/src/domain/entities/paywall/CreditsPackage.ts +0 -16
  19. package/src/domain/entities/paywall/PaywallTab.ts +0 -11
  20. package/src/domain/entities/paywall/SubscriptionPlan.ts +0 -27
  21. package/src/presentation/components/paywall/BestValueBadge.tsx +0 -59
  22. package/src/presentation/components/paywall/CreditsPackageCard.tsx +0 -129
  23. package/src/presentation/components/paywall/CreditsTabContent.tsx +0 -123
  24. package/src/presentation/components/paywall/PaywallFeatureItem.tsx +0 -65
  25. package/src/presentation/components/paywall/PaywallFeaturesList.tsx +0 -47
  26. package/src/presentation/components/paywall/PaywallHeader.tsx +0 -82
  27. package/src/presentation/components/paywall/PaywallLegalFooter.tsx +0 -145
  28. package/src/presentation/components/paywall/PaywallLegalFooterStyles.ts +0 -53
  29. package/src/presentation/components/paywall/PaywallLegalFooterTypes.ts +0 -19
  30. package/src/presentation/components/paywall/PaywallModal.tsx +0 -162
  31. package/src/presentation/components/paywall/PaywallTabBar.tsx +0 -96
  32. package/src/presentation/components/paywall/SubscriptionFooter.tsx +0 -116
  33. package/src/presentation/components/paywall/SubscriptionModal.tsx +0 -168
  34. package/src/presentation/components/paywall/SubscriptionModalHeader.tsx +0 -78
  35. package/src/presentation/components/paywall/SubscriptionPackageList.tsx +0 -171
  36. package/src/presentation/components/paywall/SubscriptionPlanCard.tsx +0 -213
  37. package/src/presentation/components/paywall/SubscriptionPlanCardStyles.ts +0 -61
  38. package/src/presentation/components/paywall/SubscriptionPlanCardTypes.ts +0 -15
  39. package/src/presentation/components/paywall/SubscriptionTabContent.tsx +0 -139
  40. package/src/presentation/components/paywall/accordion/AccordionPlanCard.tsx +0 -100
  41. package/src/presentation/components/paywall/accordion/AccordionPlanCardTypes.ts +0 -39
  42. package/src/presentation/components/paywall/accordion/PlanCardDetails.tsx +0 -107
  43. package/src/presentation/components/paywall/accordion/PlanCardHeader.tsx +0 -165
  44. package/src/presentation/components/paywall/accordion/index.ts +0 -12
  45. /package/src/{presentation → domains/paywall}/hooks/useSubscriptionModal.ts +0 -0
@@ -1,213 +0,0 @@
1
- /**
2
- * Subscription Plan Card Component
3
- * Single Responsibility: Display a subscription plan option
4
- */
5
-
6
- import React, { useMemo } from "react";
7
- import { View, TouchableOpacity, StyleSheet } from "react-native";
8
- import { AtomicText } from "@umituz/react-native-design-system";
9
- import { useAppDesignTokens, withAlpha, useResponsive } from "@umituz/react-native-design-system";
10
- import { formatPrice } from "../../../utils/priceUtils";
11
- import { useLocalization } from "@umituz/react-native-localization";
12
- import { BestValueBadge } from "./BestValueBadge";
13
- import { getPeriodLabel, isYearlyPackage } from "../../../utils/packagePeriodUtils";
14
- import { LinearGradient } from "expo-linear-gradient";
15
- import type { SubscriptionPlanCardProps } from "./SubscriptionPlanCardTypes";
16
-
17
- export type { SubscriptionPlanCardProps } from "./SubscriptionPlanCardTypes";
18
-
19
- /**
20
- * Create responsive styles for subscription plan card
21
- */
22
- const createStyles = (spacingMult: number, touchTarget: number) => {
23
- const basePadding = 18;
24
- const baseRadius = 16;
25
- const baseCreditRadius = 12;
26
-
27
- const radioSize = Math.max(touchTarget * 0.5, 24);
28
- const radioInnerSize = radioSize * 0.5;
29
-
30
- return StyleSheet.create({
31
- container: {
32
- borderRadius: baseRadius * spacingMult,
33
- position: "relative",
34
- overflow: "hidden",
35
- },
36
- gradientWrapper: {
37
- flex: 1,
38
- padding: basePadding * spacingMult,
39
- },
40
- content: {
41
- flexDirection: "row",
42
- justifyContent: "space-between",
43
- alignItems: "center",
44
- },
45
- leftSection: {
46
- flexDirection: "row",
47
- alignItems: "center",
48
- flex: 1,
49
- },
50
- radio: {
51
- width: radioSize,
52
- height: radioSize,
53
- borderRadius: radioSize / 2,
54
- borderWidth: 2,
55
- alignItems: "center",
56
- justifyContent: "center",
57
- marginRight: 16 * spacingMult,
58
- },
59
- radioInner: {
60
- width: radioInnerSize,
61
- height: radioInnerSize,
62
- borderRadius: radioInnerSize / 2,
63
- },
64
- textContainer: {
65
- flex: 1,
66
- },
67
- title: {
68
- fontWeight: "600",
69
- marginBottom: 2 * spacingMult,
70
- },
71
- creditBadge: {
72
- paddingHorizontal: 10 * spacingMult,
73
- paddingVertical: 4 * spacingMult,
74
- borderRadius: baseCreditRadius * spacingMult,
75
- marginBottom: 4 * spacingMult,
76
- },
77
- rightSection: {
78
- alignItems: "flex-end",
79
- },
80
- price: {
81
- fontWeight: "700",
82
- },
83
- });
84
- };
85
-
86
- export const SubscriptionPlanCard: React.FC<SubscriptionPlanCardProps> =
87
- React.memo(({ package: pkg, isSelected, onSelect, isBestValue = false, creditAmount }) => {
88
- const tokens = useAppDesignTokens();
89
- const { t } = useLocalization();
90
- const { spacingMultiplier, getFontSize, minTouchTarget } = useResponsive();
91
-
92
- const period = pkg.product.subscriptionPeriod;
93
- const isYearly = isYearlyPackage(pkg);
94
- const periodLabel = getPeriodLabel(period);
95
- const price = formatPrice(pkg.product.price, pkg.product.currencyCode);
96
- const monthlyEquivalent = isYearly
97
- ? formatPrice(pkg.product.price / 12, pkg.product.currencyCode)
98
- : null;
99
-
100
- const title = pkg.product.title || t(`paywall.period.${periodLabel}`);
101
-
102
- const CardComponent = isSelected ? LinearGradient : View;
103
- const cardProps = isSelected
104
- ? {
105
- colors: [withAlpha(tokens.colors.primary, 0.2), tokens.colors.surface],
106
- start: { x: 0, y: 0 },
107
- end: { x: 1, y: 1 },
108
- }
109
- : {};
110
-
111
- // Responsive styles
112
- const styles = useMemo(() => createStyles(spacingMultiplier, minTouchTarget), [spacingMultiplier, minTouchTarget]);
113
- const secondaryFontSize = getFontSize(11);
114
- const creditFontSize = getFontSize(11);
115
-
116
- return (
117
- <TouchableOpacity
118
- onPress={onSelect}
119
- activeOpacity={0.8}
120
- style={[
121
- styles.container,
122
- {
123
- borderColor: isSelected
124
- ? tokens.colors.primary
125
- : tokens.colors.borderLight,
126
- borderWidth: isSelected ? 2 : 1,
127
- backgroundColor: isSelected ? undefined : tokens.colors.surface,
128
- },
129
- ]}
130
- >
131
- <CardComponent {...(cardProps as any)} style={styles.gradientWrapper}>
132
- <BestValueBadge text={t("paywall.bestValue")} visible={isBestValue} />
133
-
134
- <View style={styles.content}>
135
- <View style={styles.leftSection}>
136
- <View
137
- style={[
138
- styles.radio,
139
- {
140
- borderColor: isSelected
141
- ? tokens.colors.primary
142
- : tokens.colors.border,
143
- },
144
- ]}
145
- >
146
- {isSelected && (
147
- <View
148
- style={[
149
- styles.radioInner,
150
- { backgroundColor: tokens.colors.primary },
151
- ]}
152
- />
153
- )}
154
- </View>
155
- <View style={styles.textContainer}>
156
- <AtomicText
157
- type="titleSmall"
158
- style={[styles.title, { color: tokens.colors.textPrimary }]}
159
- >
160
- {title}
161
- </AtomicText>
162
- {isYearly && monthlyEquivalent && (
163
- <AtomicText
164
- type="bodySmall"
165
- style={{ color: tokens.colors.textSecondary, fontSize: secondaryFontSize }}
166
- >
167
- {monthlyEquivalent}/mo
168
- </AtomicText>
169
- )}
170
- </View>
171
- </View>
172
-
173
- <View style={styles.rightSection}>
174
- {creditAmount && (
175
- <View
176
- style={[
177
- styles.creditBadge,
178
- {
179
- backgroundColor: withAlpha(tokens.colors.primary, 0.25), // Increased alpha
180
- borderColor: withAlpha(tokens.colors.primary, 0.4),
181
- borderWidth: 1,
182
- flexDirection: "row",
183
- alignItems: "center"
184
- },
185
- ]}
186
- >
187
- <AtomicText
188
- type="labelSmall"
189
- style={{
190
- color: tokens.colors.primary,
191
- fontWeight: "800",
192
- fontSize: creditFontSize,
193
- }}
194
- >
195
- {creditAmount} {t("paywall.credits") || "Credits"}
196
- </AtomicText>
197
- </View>
198
- )}
199
- <AtomicText
200
- type="titleMedium"
201
- style={[styles.price, { color: tokens.colors.textPrimary }]}
202
- >
203
- {price}
204
- </AtomicText>
205
- </View>
206
- </View>
207
- </CardComponent>
208
- </TouchableOpacity>
209
- );
210
- });
211
-
212
-
213
- SubscriptionPlanCard.displayName = "SubscriptionPlanCard";
@@ -1,61 +0,0 @@
1
- /**
2
- * Subscription Plan Card Styles
3
- * StyleSheet definitions for subscription plan card
4
- */
5
-
6
- import { StyleSheet } from "react-native";
7
-
8
- export const styles = StyleSheet.create({
9
- container: {
10
- borderRadius: 16,
11
- position: "relative",
12
- overflow: "hidden",
13
- },
14
- gradientWrapper: {
15
- flex: 1,
16
- padding: 18,
17
- },
18
- content: {
19
- flexDirection: "row",
20
- justifyContent: "space-between",
21
- alignItems: "center",
22
- },
23
- leftSection: {
24
- flexDirection: "row",
25
- alignItems: "center",
26
- flex: 1,
27
- },
28
- radio: {
29
- width: 24,
30
- height: 24,
31
- borderRadius: 12,
32
- borderWidth: 2,
33
- alignItems: "center",
34
- justifyContent: "center",
35
- marginRight: 16,
36
- },
37
- radioInner: {
38
- width: 12,
39
- height: 12,
40
- borderRadius: 6,
41
- },
42
- textContainer: {
43
- flex: 1,
44
- },
45
- title: {
46
- fontWeight: "600",
47
- marginBottom: 2,
48
- },
49
- creditBadge: {
50
- paddingHorizontal: 10,
51
- paddingVertical: 4,
52
- borderRadius: 12,
53
- marginBottom: 4,
54
- },
55
- rightSection: {
56
- alignItems: "flex-end",
57
- },
58
- price: {
59
- fontWeight: "700",
60
- },
61
- });
@@ -1,15 +0,0 @@
1
- /**
2
- * Subscription Plan Card Types
3
- * Type definitions for subscription plan display
4
- */
5
-
6
- import type { PurchasesPackage } from "react-native-purchases";
7
-
8
- export interface SubscriptionPlanCardProps {
9
- package: PurchasesPackage;
10
- isSelected: boolean;
11
- onSelect: () => void;
12
- isBestValue?: boolean;
13
- /** Optional: Number of credits/generations included with this package */
14
- creditAmount?: number;
15
- }
@@ -1,139 +0,0 @@
1
- /**
2
- * Subscription Tab Content Component
3
- * Single Responsibility: Display subscription plans list
4
- */
5
-
6
- import React, { useMemo } from "react";
7
- import { View, StyleSheet, ScrollView } from "react-native";
8
- import { useAppDesignTokens } from "@umituz/react-native-design-system";
9
- import type { PurchasesPackage } from "react-native-purchases";
10
- import { PaywallFeaturesList } from "./PaywallFeaturesList";
11
- import { SubscriptionPackageList } from "./SubscriptionPackageList";
12
- import { SubscriptionFooter } from "./SubscriptionFooter";
13
-
14
- interface SubscriptionTabContentProps {
15
- packages: PurchasesPackage[];
16
- selectedPackage: PurchasesPackage | null;
17
- onSelectPackage: (pkg: PurchasesPackage) => void;
18
- onPurchase: () => void;
19
- onRestore?: () => void;
20
- features?: Array<{ icon: string; text: string }>;
21
- isLoading?: boolean;
22
- purchaseButtonText: string;
23
- processingText: string;
24
- restoreButtonText: string;
25
- loadingText: string;
26
- emptyText: string;
27
- privacyUrl?: string;
28
- termsUrl?: string;
29
- privacyText?: string;
30
- termsOfServiceText?: string;
31
- }
32
-
33
- const isYearlyPackage = (pkg: PurchasesPackage): boolean => {
34
- const period = pkg.product.subscriptionPeriod;
35
- return period?.includes("Y") || period?.includes("year") || false;
36
- };
37
-
38
- const sortPackages = (packages: PurchasesPackage[]): PurchasesPackage[] => {
39
- return [...packages].sort((a, b) => {
40
- const aIsYearly = isYearlyPackage(a);
41
- const bIsYearly = isYearlyPackage(b);
42
- if (aIsYearly && !bIsYearly) return -1;
43
- if (!aIsYearly && bIsYearly) return 1;
44
- return b.product.price - a.product.price;
45
- });
46
- };
47
-
48
- export const SubscriptionTabContent: React.FC<SubscriptionTabContentProps> =
49
- React.memo(
50
- ({
51
- packages,
52
- selectedPackage,
53
- onSelectPackage,
54
- onPurchase,
55
- onRestore,
56
- features = [],
57
- isLoading = false,
58
- purchaseButtonText,
59
- processingText,
60
- restoreButtonText,
61
- loadingText,
62
- emptyText,
63
- privacyUrl,
64
- termsUrl,
65
- privacyText,
66
- termsOfServiceText,
67
- }) => {
68
- const tokens = useAppDesignTokens();
69
-
70
- const sortedPackages = useMemo(() => sortPackages(packages), [packages]);
71
-
72
- return (
73
- <View style={styles.container}>
74
- <ScrollView
75
- style={styles.scrollView}
76
- contentContainerStyle={styles.scrollContent}
77
- showsVerticalScrollIndicator={false}
78
- >
79
- <SubscriptionPackageList
80
- packages={sortedPackages}
81
- isLoading={isLoading}
82
- selectedPkg={selectedPackage}
83
- onSelect={onSelectPackage}
84
- loadingText={loadingText}
85
- emptyText={emptyText}
86
- />
87
-
88
- {features.length > 0 && (
89
- <View
90
- style={[
91
- styles.featuresSection,
92
- { backgroundColor: tokens.colors.surfaceSecondary },
93
- ]}
94
- >
95
- <PaywallFeaturesList features={features} gap={12} />
96
- </View>
97
- )}
98
- </ScrollView>
99
-
100
- <SubscriptionFooter
101
- isProcessing={false}
102
- isLoading={isLoading}
103
- processingText={processingText}
104
- purchaseButtonText={purchaseButtonText}
105
- hasPackages={packages.length > 0}
106
- selectedPkg={selectedPackage}
107
- restoreButtonText={restoreButtonText}
108
- showRestoreButton={!!onRestore}
109
- onPurchase={onPurchase}
110
- onRestore={onRestore || (() => { })}
111
- privacyUrl={privacyUrl}
112
- termsUrl={termsUrl}
113
- privacyText={privacyText}
114
- termsOfServiceText={termsOfServiceText}
115
- />
116
- </View>
117
- );
118
- }
119
- );
120
-
121
- SubscriptionTabContent.displayName = "SubscriptionTabContent";
122
-
123
- const styles = StyleSheet.create({
124
- container: {
125
- flex: 1,
126
- },
127
- scrollView: {
128
- flex: 1,
129
- },
130
- scrollContent: {
131
- paddingHorizontal: 24,
132
- paddingBottom: 16,
133
- },
134
- featuresSection: {
135
- borderRadius: 16,
136
- padding: 16,
137
- marginTop: 20,
138
- },
139
- });
@@ -1,100 +0,0 @@
1
- /**
2
- * Accordion Plan Card
3
- * Expandable subscription plan card with credit display
4
- */
5
-
6
- import React, { useCallback, useMemo } from "react";
7
- import { View, StyleSheet, type StyleProp, type ViewStyle } from "react-native";
8
- import { useAppDesignTokens, useResponsive } from "@umituz/react-native-design-system";
9
- import { formatPrice } from "../../../../utils/priceUtils";
10
- import { getPeriodLabel, isYearlyPackage } from "../../../../utils/packagePeriodUtils";
11
- import { useLocalization } from "@umituz/react-native-localization";
12
- import { PlanCardHeader } from "./PlanCardHeader";
13
- import { PlanCardDetails } from "./PlanCardDetails";
14
- import type { AccordionPlanCardProps } from "./AccordionPlanCardTypes";
15
-
16
- export const AccordionPlanCard: React.FC<AccordionPlanCardProps> = React.memo(
17
- ({
18
- package: pkg,
19
- isSelected,
20
- isExpanded,
21
- onSelect,
22
- onToggleExpand,
23
- isBestValue = false,
24
- creditAmount,
25
- billingPeriodLabel,
26
- totalPriceLabel,
27
- perMonthLabel,
28
- }) => {
29
- const tokens = useAppDesignTokens();
30
- const { t } = useLocalization();
31
- const { spacingMultiplier } = useResponsive();
32
-
33
- const styles = useMemo(() => createStyles(spacingMultiplier), [spacingMultiplier]);
34
-
35
- const period = pkg.product.subscriptionPeriod;
36
- const isYearly = isYearlyPackage(pkg);
37
- const periodLabel = getPeriodLabel(period);
38
- const price = formatPrice(pkg.product.price, pkg.product.currencyCode);
39
- const monthlyEquivalent = isYearly
40
- ? formatPrice(pkg.product.price / 12, pkg.product.currencyCode)
41
- : null;
42
-
43
- const title = pkg.product.title || t(`paywall.period.${periodLabel}`);
44
- const displayPrice = price;
45
-
46
- const handleHeaderPress = useCallback(() => {
47
- onSelect();
48
- if (!isExpanded) {
49
- onToggleExpand();
50
- }
51
- }, [onSelect, onToggleExpand, isExpanded]);
52
-
53
- const containerStyle: StyleProp<ViewStyle> = [
54
- styles.container,
55
- {
56
- borderColor: isSelected
57
- ? tokens.colors.primary
58
- : tokens.colors.borderLight,
59
- borderWidth: isSelected ? 2 : 1,
60
- backgroundColor: tokens.colors.surface,
61
- },
62
- ];
63
-
64
- return (
65
- <View style={containerStyle}>
66
- <PlanCardHeader
67
- title={title}
68
- price={displayPrice}
69
- creditAmount={creditAmount}
70
- isSelected={isSelected}
71
- isExpanded={isExpanded}
72
- isBestValue={isBestValue}
73
- onToggle={handleHeaderPress}
74
- />
75
-
76
- {isExpanded && (
77
- <PlanCardDetails
78
- fullPrice={price}
79
- monthlyEquivalent={monthlyEquivalent}
80
- periodLabel={periodLabel}
81
- isYearly={isYearly}
82
- billingPeriodLabel={billingPeriodLabel}
83
- totalPriceLabel={totalPriceLabel}
84
- perMonthLabel={perMonthLabel}
85
- />
86
- )}
87
- </View>
88
- );
89
- }
90
- );
91
-
92
- AccordionPlanCard.displayName = "AccordionPlanCard";
93
-
94
- const createStyles = (spacingMult: number) =>
95
- StyleSheet.create({
96
- container: {
97
- borderRadius: 16 * spacingMult,
98
- marginBottom: 12 * spacingMult,
99
- },
100
- });
@@ -1,39 +0,0 @@
1
- /**
2
- * Accordion Plan Card Types
3
- * Type definitions for accordion-style subscription cards
4
- */
5
-
6
- import type { PurchasesPackage } from "react-native-purchases";
7
-
8
- export interface AccordionPlanCardProps {
9
- package: PurchasesPackage;
10
- isSelected: boolean;
11
- isExpanded: boolean;
12
- onSelect: () => void;
13
- onToggleExpand: () => void;
14
- isBestValue?: boolean;
15
- creditAmount?: number;
16
- billingPeriodLabel?: string;
17
- totalPriceLabel?: string;
18
- perMonthLabel?: string;
19
- }
20
-
21
- export interface PlanCardHeaderProps {
22
- title: string;
23
- price: string;
24
- creditAmount?: number;
25
- isSelected: boolean;
26
- isExpanded: boolean;
27
- isBestValue?: boolean;
28
- onToggle: () => void;
29
- }
30
-
31
- export interface PlanCardDetailsProps {
32
- fullPrice: string;
33
- monthlyEquivalent: string | null;
34
- periodLabel: string;
35
- isYearly: boolean;
36
- billingPeriodLabel?: string;
37
- totalPriceLabel?: string;
38
- perMonthLabel?: string;
39
- }
@@ -1,107 +0,0 @@
1
- /**
2
- * Plan Card Details
3
- * Expanded state of accordion subscription card
4
- */
5
-
6
- import React, { useMemo } from "react";
7
- import { View, StyleSheet } from "react-native";
8
- import {
9
- AtomicText,
10
- useAppDesignTokens,
11
- useResponsive,
12
- } from "@umituz/react-native-design-system";
13
- import type { PlanCardDetailsProps } from "./AccordionPlanCardTypes";
14
-
15
- export const PlanCardDetails: React.FC<PlanCardDetailsProps> = ({
16
- fullPrice,
17
- monthlyEquivalent,
18
- periodLabel,
19
- isYearly,
20
- billingPeriodLabel = "Billing Period",
21
- totalPriceLabel = "Total Price",
22
- perMonthLabel = "Per Month",
23
- }) => {
24
- const tokens = useAppDesignTokens();
25
- const { spacingMultiplier } = useResponsive();
26
-
27
- const styles = useMemo(() => createStyles(spacingMultiplier), [spacingMultiplier]);
28
-
29
- return (
30
- <View
31
- style={[
32
- styles.container,
33
- {
34
- backgroundColor: tokens.colors.surfaceSecondary,
35
- borderTopColor: tokens.colors.border,
36
- },
37
- ]}
38
- >
39
- <View style={styles.row}>
40
- <AtomicText
41
- type="bodyMedium"
42
- style={{ color: tokens.colors.textSecondary }}
43
- >
44
- {billingPeriodLabel}
45
- </AtomicText>
46
- <AtomicText
47
- type="bodyMedium"
48
- style={{ color: tokens.colors.textPrimary, fontWeight: "600" }}
49
- >
50
- {periodLabel}
51
- </AtomicText>
52
- </View>
53
-
54
- {isYearly && (
55
- <View style={styles.row}>
56
- <AtomicText
57
- type="bodyMedium"
58
- style={{ color: tokens.colors.textSecondary }}
59
- >
60
- {totalPriceLabel}
61
- </AtomicText>
62
- <AtomicText
63
- type="bodyMedium"
64
- style={{
65
- color: tokens.colors.primary,
66
- fontWeight: "700",
67
- }}
68
- >
69
- {fullPrice}
70
- </AtomicText>
71
- </View>
72
- )}
73
-
74
- {monthlyEquivalent && (
75
- <View style={styles.row}>
76
- <AtomicText
77
- type="bodyMedium"
78
- style={{ color: tokens.colors.textSecondary }}
79
- >
80
- {perMonthLabel}
81
- </AtomicText>
82
- <AtomicText
83
- type="bodyMedium"
84
- style={{ color: tokens.colors.textPrimary, fontWeight: "600" }}
85
- >
86
- {monthlyEquivalent}
87
- </AtomicText>
88
- </View>
89
- )}
90
- </View>
91
- );
92
- };
93
-
94
- const createStyles = (spacingMult: number) =>
95
- StyleSheet.create({
96
- container: {
97
- paddingHorizontal: 16 * spacingMult,
98
- paddingVertical: 12 * spacingMult,
99
- borderTopWidth: 1,
100
- gap: 10 * spacingMult,
101
- },
102
- row: {
103
- flexDirection: "row",
104
- justifyContent: "space-between",
105
- alignItems: "center",
106
- },
107
- });