@developer_tribe/react-builder 1.2.27 → 1.2.29

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 (156) hide show
  1. package/dist/assets/samples/getSamples.d.ts +0 -3
  2. package/dist/build-components/BIcon/BIconProps.generated.d.ts +1 -2
  3. package/dist/build-components/CountDown/CountDownProps.generated.d.ts +2 -1
  4. package/dist/build-components/OnboardFooter/OnboardFooterProps.generated.d.ts +1 -2
  5. package/dist/build-components/OnboardSubtitle/OnboardSubtitleProps.generated.d.ts +1 -2
  6. package/dist/build-components/OnboardTitle/OnboardTitleProps.generated.d.ts +1 -2
  7. package/dist/build-components/PaywallCloseButton/PaywallCloseButtonProps.generated.d.ts +1 -2
  8. package/dist/build-components/PaywallOptions/usePaywallOptionParamsFactory.d.ts +1 -1
  9. package/dist/build-components/PriceTag/PriceTag.d.ts +5 -0
  10. package/dist/build-components/PriceTag/PriceTagProps.generated.d.ts +63 -0
  11. package/dist/build-components/Pricing/Pricing.d.ts +5 -0
  12. package/dist/build-components/Pricing/PricingProps.generated.d.ts +59 -0
  13. package/dist/build-components/Promo/Promo.d.ts +5 -0
  14. package/dist/build-components/Promo/PromoProps.generated.d.ts +59 -0
  15. package/dist/build-components/Text/TextProps.generated.d.ts +1 -2
  16. package/dist/build-components/index.d.ts +4 -1
  17. package/dist/build-components/patterns.generated.d.ts +1405 -202
  18. package/dist/components/BuilderProvider.d.ts +5 -3
  19. package/dist/components/ParamsProvider.d.ts +16 -8
  20. package/dist/hooks/useSyncHtmlThemeClass.d.ts +1 -1
  21. package/dist/index.cjs.js +4 -4
  22. package/dist/index.cjs.js.map +1 -1
  23. package/dist/index.d.ts +16 -3
  24. package/dist/index.esm.js +4 -4
  25. package/dist/index.esm.js.map +1 -1
  26. package/dist/index.web.cjs.js +4 -4
  27. package/dist/index.web.cjs.js.map +1 -1
  28. package/dist/index.web.esm.js +4 -4
  29. package/dist/index.web.esm.js.map +1 -1
  30. package/dist/logger.d.ts +18 -0
  31. package/dist/modals/InspectModal.d.ts +5 -0
  32. package/dist/modals/index.d.ts +1 -1
  33. package/dist/pages/ProjectPage.d.ts +3 -3
  34. package/dist/paywall/hooks/useCalculateLocalizedPrice.d.ts +4 -2
  35. package/dist/paywall/hooks/useDiscountRate.d.ts +3 -2
  36. package/dist/paywall/types/paywall-types.d.ts +7 -32
  37. package/dist/product-base/buildPaywallLocalizationParams.d.ts +16 -0
  38. package/dist/product-base/calculations.d.ts +29 -0
  39. package/dist/product-base/extractAndroidParams.d.ts +24 -0
  40. package/dist/product-base/extractIOSParams.d.ts +24 -0
  41. package/dist/product-base/index.d.ts +51 -0
  42. package/dist/product-base/periodLocalizationKeys.d.ts +44 -0
  43. package/dist/product-base/types.d.ts +155 -0
  44. package/dist/product-base/usePaywallLocalizationParams.d.ts +29 -0
  45. package/dist/store.d.ts +7 -1
  46. package/dist/styles.css +1 -1
  47. package/dist/types/PreviewConfig.d.ts +10 -16
  48. package/dist/utils/extractTextStyle/extractTextStyle.d.ts +2 -2
  49. package/dist/utils/extractTextStyle/extractTextStyleNative.d.ts +2 -2
  50. package/dist/utils/replaceLocalizationParams.d.ts +1 -1
  51. package/package.json +2 -2
  52. package/scripts/migrate-samples-to-current.ts +3 -3
  53. package/scripts/prebuild/utils/validateAllComponentsOrThrow.js +28 -12
  54. package/src/DeviceMockFrame.tsx +15 -10
  55. package/src/assets/meta.json +1 -1
  56. package/src/assets/samples/carousel-sample.json +6 -5
  57. package/src/assets/samples/getSamples.ts +16 -49
  58. package/src/assets/samples/paywall-1.json +64 -22
  59. package/src/assets/samples/paywall-2.json +0 -15
  60. package/src/assets/samples/paywall-app-delete-offer.json +0 -15
  61. package/src/assets/samples/paywall-app-open-offer.json +0 -15
  62. package/src/assets/samples/paywall-back-offer.json +0 -15
  63. package/src/assets/samples/paywall-notification-offer.json +0 -15
  64. package/src/assets/samples/simple-1.json +1 -16
  65. package/src/assets/samples/simple-2.json +0 -15
  66. package/src/assets/samples/unmigrated-builder-1.1.1.json +0 -3
  67. package/src/assets/samples/unmigrated-builder1.json +0 -3
  68. package/src/assets/samples/unvalidated-builder1.json +0 -3
  69. package/src/assets/samples/unvalidated-crash1.json +0 -3
  70. package/src/assets/samples/unvalidated-crashcomponent1.json +0 -3
  71. package/src/assets/samples/vpn-onboard-1.json +1 -34
  72. package/src/assets/samples/vpn-onboard-2.json +1 -34
  73. package/src/assets/samples/vpn-onboard-3.json +1 -42
  74. package/src/assets/samples/vpn-onboard-4.json +0 -73
  75. package/src/assets/samples/vpn-onboard-5.json +0 -73
  76. package/src/assets/samples/vpn-onboard-6.json +0 -73
  77. package/src/assets/samples/vpn-onboard-7.json +529 -0
  78. package/src/attribute-analyser/style/native/useExtractImageStyle.ts +1 -4
  79. package/src/attribute-analyser/style/native/useExtractTextStyle.ts +3 -12
  80. package/src/attribute-analyser/style/native/useExtractViewStyle.ts +1 -4
  81. package/src/attribute-analyser/style/web/useExtractImageStyle.ts +1 -4
  82. package/src/attribute-analyser/style/web/useExtractTextStyle.ts +3 -12
  83. package/src/attribute-analyser/style/web/useExtractViewStyle.ts +1 -4
  84. package/src/attributes-editor/useAttributesEditorModel.ts +5 -52
  85. package/src/build-components/BIcon/BIconProps.generated.ts +1 -2
  86. package/src/build-components/CarouselDots/CarouselDots.tsx +6 -13
  87. package/src/build-components/CountDown/CountDownProps.generated.ts +2 -1
  88. package/src/build-components/NavigationBarColor/NavigationBarColor.tsx +2 -2
  89. package/src/build-components/OnboardButton/OnboardButton.tsx +1 -2
  90. package/src/build-components/OnboardDot/OnboardDot.tsx +6 -18
  91. package/src/build-components/OnboardFooter/OnboardFooter.tsx +5 -3
  92. package/src/build-components/OnboardFooter/OnboardFooterProps.generated.ts +1 -2
  93. package/src/build-components/OnboardFooter/pattern.json +1 -1
  94. package/src/build-components/OnboardSubtitle/OnboardSubtitleProps.generated.ts +1 -2
  95. package/src/build-components/OnboardTitle/OnboardTitleProps.generated.ts +1 -2
  96. package/src/build-components/PaywallCloseButton/PaywallCloseButtonProps.generated.ts +1 -2
  97. package/src/build-components/PaywallOptions/PaywallOptions.tsx +3 -3
  98. package/src/build-components/PaywallOptions/usePaywallOptionParamsFactory.ts +26 -13
  99. package/src/build-components/PaywallProvider/PaywallProvider.tsx +51 -12
  100. package/src/build-components/PriceTag/PriceTag.tsx +25 -0
  101. package/src/build-components/PriceTag/PriceTagProps.generated.ts +83 -0
  102. package/src/build-components/PriceTag/pattern.json +53 -0
  103. package/src/build-components/Pricing/Pricing.tsx +13 -0
  104. package/src/build-components/Pricing/PricingProps.generated.ts +76 -0
  105. package/src/build-components/Pricing/pattern.json +25 -0
  106. package/src/build-components/Promo/Promo.tsx +13 -0
  107. package/src/build-components/Promo/PromoProps.generated.ts +76 -0
  108. package/src/build-components/Promo/pattern.json +25 -0
  109. package/src/build-components/RadioButton/RadioButton.tsx +3 -5
  110. package/src/build-components/RenderNode.generated.tsx +15 -0
  111. package/src/build-components/StatusBarColor/StatusBarColor.tsx +2 -2
  112. package/src/build-components/Text/Text.tsx +12 -5
  113. package/src/build-components/Text/TextProps.generated.ts +1 -2
  114. package/src/build-components/Text/pattern.json +3 -2
  115. package/src/build-components/index.ts +15 -0
  116. package/src/build-components/patterns.generated.ts +1454 -181
  117. package/src/components/BottomBar.tsx +42 -39
  118. package/src/components/BuilderProvider.tsx +41 -14
  119. package/src/components/LocalizationParamsProvider.tsx +1 -1
  120. package/src/components/ParamsProvider.tsx +36 -11
  121. package/src/hooks/useLocalize.ts +7 -4
  122. package/src/hooks/useParams.ts +1 -1
  123. package/src/hooks/useSyncHtmlThemeClass.ts +2 -2
  124. package/src/index.ts +54 -8
  125. package/src/logger.ts +39 -0
  126. package/src/modals/InspectModal.tsx +331 -0
  127. package/src/modals/ProductPresetsModal.tsx +7 -14
  128. package/src/modals/index.ts +1 -1
  129. package/src/pages/DebugJsonPage.tsx +9 -22
  130. package/src/pages/ProjectDebug.tsx +1 -1
  131. package/src/pages/ProjectPage.tsx +29 -11
  132. package/src/pages/tabs/SideTool.tsx +28 -104
  133. package/src/paywall/hooks/useCalculateLocalizedPrice.ts +8 -3
  134. package/src/paywall/hooks/useDiscountRate.ts +11 -3
  135. package/src/paywall/types/paywall-types.ts +7 -38
  136. package/src/product-base/buildPaywallLocalizationParams.ts +100 -0
  137. package/src/product-base/calculations.ts +93 -0
  138. package/src/product-base/extractAndroidParams.ts +207 -0
  139. package/src/product-base/extractIOSParams.ts +199 -0
  140. package/src/product-base/index.ts +64 -0
  141. package/src/product-base/mockProducts.json +489 -0
  142. package/src/product-base/periodLocalizationKeys.ts +114 -0
  143. package/src/product-base/types.ts +183 -0
  144. package/src/product-base/usePaywallLocalizationParams.ts +61 -0
  145. package/src/store.ts +18 -1
  146. package/src/styles/index.scss +1 -0
  147. package/src/styles/modals/_inspect-modal.scss +155 -0
  148. package/src/types/PreviewConfig.ts +157 -16
  149. package/src/utils/extractTextStyle/extractTextStyle.ts +14 -6
  150. package/src/utils/extractTextStyle/extractTextStyleNative.ts +8 -6
  151. package/src/utils/logRenderStore.ts +6 -10
  152. package/src/utils/parseColor.ts +0 -1
  153. package/src/utils/replaceLocalizationParams.ts +8 -4
  154. package/dist/modals/ScreenColorsModal.d.ts +0 -8
  155. package/src/assets/products.json +0 -98
  156. package/src/modals/ScreenColorsModal.tsx +0 -121
@@ -0,0 +1,183 @@
1
+ /**
2
+ * Product types for IAP.
3
+ *
4
+ * All platform-specific fields (iOS/Android) are included.
5
+ *
6
+ * See: `https://www.npmjs.com/package/react-native-iap`
7
+ */
8
+
9
+ /** Purchase type: subscription or one-time in-app purchase. */
10
+ export type PurchaseType = 'in-app' | 'subs';
11
+
12
+ /** Subscription offer type */
13
+ export type SubscriptionOfferType = 'introductory' | 'promotional';
14
+
15
+ /** Payment mode */
16
+ export type PaymentMode = 'pay-as-you-go' | 'pay-up-front' | 'free-trial';
17
+
18
+ /** recurrenceMode values from Google Play Billing */
19
+ export type RecurrenceMode = 1 | 2 | 3;
20
+ // 1 = INFINITE_RECURRING (normal subscription)
21
+ // 2 = FINITE_RECURRING (limited cycles, e.g. promo)
22
+ // 3 = NON_RECURRING (one-time, e.g. trial)
23
+
24
+ /** iOS subscription period unit */
25
+ export type IOSPeriodUnit = 'DAY' | 'WEEK' | 'MONTH' | 'YEAR';
26
+
27
+ /** Normalized period unit */
28
+ export type PeriodUnit = 'day' | 'week' | 'month' | 'year';
29
+
30
+ /** A single billing phase (Google Play Billing) */
31
+ export interface PricingPhase {
32
+ /** ISO 8601 duration (e.g. "P1M", "P1Y", "P7D") */
33
+ billingPeriod: string;
34
+ /** How many times this phase repeats (0 = infinite) */
35
+ billingCycleCount: number;
36
+ /** Human-readable price (e.g. "$4.99") */
37
+ formattedPrice: string;
38
+ /** Price in micros (e.g. "4990000"). "0" = free (trial) */
39
+ priceAmountMicros: string;
40
+ /** Currency code (e.g. "USD") */
41
+ priceCurrencyCode: string;
42
+ /** Recurrence mode: 1=infinite, 2=finite/promo, 3=one-time/trial */
43
+ recurrenceMode: RecurrenceMode;
44
+ }
45
+
46
+ /** Android raw offer details (from Google Play Billing API) */
47
+ export interface AndroidOfferDetails {
48
+ basePlanId: string;
49
+ offerId?: string;
50
+ offerToken: string;
51
+ offerTags: string[];
52
+ pricingPhases: {
53
+ pricingPhaseList: PricingPhase[];
54
+ };
55
+ }
56
+
57
+ /** Normalized subscription offer (cross-platform) */
58
+ export interface SubscriptionOffer {
59
+ id: string;
60
+ displayPrice: string;
61
+ price: number;
62
+ type: SubscriptionOfferType;
63
+ currency: string;
64
+ paymentMode?: PaymentMode;
65
+ periodCount?: number;
66
+ period?: {
67
+ unit: PeriodUnit;
68
+ value: number;
69
+ };
70
+ // Android specific
71
+ basePlanIdAndroid?: string;
72
+ offerTokenAndroid?: string;
73
+ offerTagsAndroid?: string[];
74
+ pricingPhasesAndroid?: {
75
+ pricingPhaseList: PricingPhase[];
76
+ };
77
+ }
78
+
79
+ /** iOS introductory price */
80
+ export interface IOSIntroductoryPrice {
81
+ price: string;
82
+ priceIOS?: string;
83
+ type: 'free_trial' | 'pay_as_you_go' | 'pay_up_front';
84
+ numberOfPeriods: number;
85
+ subscriptionPeriod?: IOSPeriodUnit;
86
+ subscriptionPeriodUnitIOS?: IOSPeriodUnit;
87
+ subscriptionPeriodNumberIOS?: number;
88
+ }
89
+
90
+ /** iOS promotional discount */
91
+ export interface IOSDiscount {
92
+ identifier: string;
93
+ price: string;
94
+ numberOfPeriods: number;
95
+ subscriptionPeriod: IOSPeriodUnit;
96
+ }
97
+
98
+ /** Product input coming from builder for membership initialization. */
99
+ export interface ProductInput {
100
+ store_package_id: string;
101
+ offer_id?: string;
102
+ type?: string;
103
+ is_consumable?: boolean;
104
+ }
105
+
106
+ /** Unified product representation for IAP */
107
+ export interface Product {
108
+ /** iOS: `productId`, Android: `id` (normalized to productId) */
109
+ productId: string;
110
+ /** Android raw id */
111
+ id?: string;
112
+
113
+ title?: string;
114
+ description?: string;
115
+ displayName?: string;
116
+ displayPrice?: string;
117
+ localizedPrice?: string;
118
+ price?: string;
119
+ currency?: string;
120
+ currencyCode?: string;
121
+ platform?: 'android' | 'ios';
122
+ debugDescription?: string | null;
123
+
124
+ /** Offer identifier from builder (Android offer token) */
125
+ offerId?: string;
126
+ type?: PurchaseType;
127
+ isConsumable?: boolean;
128
+
129
+ // Android specific
130
+ nameAndroid?: string;
131
+ productStatusAndroid?: unknown | null;
132
+ oneTimePurchaseOfferDetailsAndroid?: unknown | null;
133
+ discountOffers?: unknown | null;
134
+ /** Raw Google Play offer details */
135
+ subscriptionOfferDetailsAndroid?: AndroidOfferDetails[];
136
+ /** Normalized cross-platform offers */
137
+ subscriptionOffers?: SubscriptionOffer[];
138
+
139
+ // iOS specific
140
+ introductoryPrice?: IOSIntroductoryPrice;
141
+ introductoryPriceIOS?: IOSIntroductoryPrice;
142
+ discounts?: IOSDiscount[];
143
+ subscriptionPeriodUnitIOS?: IOSPeriodUnit;
144
+ subscriptionPeriodNumberIOS?: number;
145
+ }
146
+
147
+ /** Product params exposed as localization variables (e.g. @price, @promoPrice). */
148
+ export interface ProductParams {
149
+ price: string;
150
+ promoPrice: string;
151
+ currency: string;
152
+ localizedPrice: string;
153
+ period: string;
154
+ promoPeriod: string;
155
+ promoPeriodUnit: string;
156
+ hasTrial: string;
157
+ trialPeriod: string;
158
+ trialPeriodUnit: string;
159
+ discountPercentage: string;
160
+ localizedPeriod: string;
161
+ localizedPromoPeriod: string;
162
+ localizedPromoPrice: string;
163
+ localizedCalculatedPrice: string;
164
+ localizedCalculatedPeriod: string;
165
+ baseLocalizedPricingText: string;
166
+ baseLocalizedPromoText: string;
167
+ productTitle: string;
168
+ productDescription: string;
169
+ productCurreny: string;
170
+ productId: string;
171
+ productSelected: string;
172
+ }
173
+
174
+ /** Keys excluded from single (per-option) params — these are global/context-level. */
175
+ type SingleExcludedKeys = 'productId' | 'productSelected';
176
+
177
+ /** Per-option (single product) params — same keys as ProductParams prefixed with `single`. */
178
+ export type SingleProductParams = {
179
+ [K in keyof Omit<
180
+ ProductParams,
181
+ SingleExcludedKeys
182
+ > as `single${Capitalize<K>}`]: ProductParams[K];
183
+ };
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Generic React hook for extracting paywall localization params.
3
+ *
4
+ * All runtime dependencies (platform, localize, builderProducts) are injected
5
+ * via the `config` parameter — no hard-coded imports to any specific project.
6
+ *
7
+ * Usage (React Native / core):
8
+ * usePaywallLocalizationParams(product, productId, {
9
+ * platform: Platform.OS,
10
+ * localize: (key) => localize(key),
11
+ * builderProducts: builder.products,
12
+ * })
13
+ *
14
+ * Usage (Web builder):
15
+ * usePaywallLocalizationParams(product, productId, {
16
+ * platform: device.platform,
17
+ * localize,
18
+ * })
19
+ */
20
+
21
+ import { useMemo } from 'react';
22
+ import { buildPaywallLocalizationParams } from './buildPaywallLocalizationParams';
23
+ import type { Product, ProductInput, ProductParams } from './types';
24
+
25
+ export type PaywallLocalizationConfig = {
26
+ /** 'ios' | 'android' — RN: Platform.OS, Web: device.platform */
27
+ platform: 'ios' | 'android';
28
+ /** Translates a localization key to a display string. Falls back to identity. */
29
+ localize?: (key: string) => string;
30
+ /** Builder product definitions — used to resolve offer_id for a product. */
31
+ builderProducts?: ProductInput[];
32
+ };
33
+
34
+ export function usePaywallLocalizationParams(
35
+ selectedProduct?: Product,
36
+ selectedProductId?: string,
37
+ config?: PaywallLocalizationConfig,
38
+ ): Partial<ProductParams> {
39
+ return useMemo(() => {
40
+ if (!selectedProduct || !config?.platform) {
41
+ return {};
42
+ }
43
+
44
+ try {
45
+ const builderProduct = config.builderProducts?.find(
46
+ (p) => p.store_package_id === selectedProductId,
47
+ );
48
+ const offerId = builderProduct?.offer_id;
49
+
50
+ return buildPaywallLocalizationParams(
51
+ selectedProduct,
52
+ config.platform,
53
+ offerId,
54
+ config.localize,
55
+ );
56
+ } catch (error) {
57
+ console.error('[usePaywallLocalizationParams] Error:', error);
58
+ return {};
59
+ }
60
+ }, [selectedProduct, selectedProductId, config]);
61
+ }
package/src/store.ts CHANGED
@@ -1,7 +1,12 @@
1
1
  import { createWithEqualityFn } from 'zustand/traditional';
2
2
  import { shallow } from 'zustand/shallow';
3
3
  import type { Device } from './types/Device';
4
- import { defaultAppConfig, type AppConfig } from './types/PreviewConfig';
4
+ import {
5
+ defaultAppConfig,
6
+ defaultTheme,
7
+ type AppConfig,
8
+ type Theme,
9
+ } from './types/PreviewConfig';
5
10
  import { getDefaultDevice } from './utils/getDevices';
6
11
  import { createJSONStorage, persist } from 'zustand/middleware';
7
12
  import { Node } from './types/Node';
@@ -28,6 +33,12 @@ type RenderStore = {
28
33
  setAppConfig: (appConfig: AppConfig) => void;
29
34
  projectColors?: ProjectColors;
30
35
  setProjectColors: (projectColors?: ProjectColors) => void;
36
+ theme: Theme;
37
+ setTheme: (theme: Theme) => void;
38
+ defaultLanguage: string;
39
+ setDefaultLanguage: (lang: string) => void;
40
+ isRtl: boolean;
41
+ setIsRtl: (isRtl: boolean) => void;
31
42
  previewMode: boolean;
32
43
  setPreviewMode: (previewMode: boolean) => void;
33
44
  // Mockable: products (persisted)
@@ -99,6 +110,12 @@ export const useRenderStore = createWithEqualityFn<RenderStore>()(
99
110
  setAppConfig: (appConfig) => set({ appConfig }),
100
111
  projectColors: undefined,
101
112
  setProjectColors: (projectColors) => set({ projectColors }),
113
+ theme: defaultTheme,
114
+ setTheme: (theme) => set({ theme }),
115
+ defaultLanguage: 'en',
116
+ setDefaultLanguage: (lang) => set({ defaultLanguage: lang }),
117
+ isRtl: false,
118
+ setIsRtl: (isRtl) => set({ isRtl }),
102
119
  previewMode: false,
103
120
  setPreviewMode: (previewMode) => set({ previewMode }),
104
121
  products: [],
@@ -27,5 +27,6 @@
27
27
  @use './modals/product-presets-modal';
28
28
  @use './modals/benefit-edit-modal';
29
29
  @use './modals/benefit-presets-modal';
30
+ @use './modals/inspect-modal';
30
31
 
31
32
  @use './utilities/carousel';
@@ -0,0 +1,155 @@
1
+ @use '../foundation/colors' as colors;
2
+ @use '../foundation/sizes' as sizes;
3
+
4
+ .inspect-modal__content {
5
+ width: calc(100vw - 32px);
6
+ height: calc(100vh - 64px);
7
+ max-width: 900px;
8
+ max-height: calc(100vh - 64px);
9
+ padding: 0;
10
+ display: flex;
11
+ flex-direction: column;
12
+ }
13
+
14
+ .inspect-modal__header {
15
+ padding: sizes.$spaceComfy sizes.$spaceRoomy;
16
+ border-bottom: 1px solid colors.$borderColor;
17
+ }
18
+
19
+ .inspect-modal__tabs {
20
+ display: flex;
21
+ gap: 0;
22
+ border-bottom: 1px solid colors.$borderColor;
23
+ padding: 0 sizes.$spaceRoomy;
24
+ }
25
+
26
+ .inspect-modal__tab {
27
+ all: unset;
28
+ cursor: pointer;
29
+ padding: sizes.$spaceCompact sizes.$spaceComfy;
30
+ font-size: sizes.$fontSizeSmPlus;
31
+ font-weight: 500;
32
+ color: colors.$mutedTextColor;
33
+ border-bottom: 2px solid transparent;
34
+ transition: color 0.15s, border-color 0.15s;
35
+
36
+ &:hover {
37
+ color: colors.$textColor;
38
+ }
39
+
40
+ &.is-active {
41
+ color: colors.$accentColor;
42
+ border-bottom-color: colors.$accentColor;
43
+ }
44
+ }
45
+
46
+ .inspect-modal__body {
47
+ flex: 1;
48
+ min-height: 0;
49
+ overflow: auto;
50
+ padding: sizes.$spaceRoomy;
51
+ }
52
+
53
+ .inspect-modal__empty {
54
+ margin: 0;
55
+ font-size: sizes.$fontSizeSmPlus;
56
+ color: colors.$mutedTextColor;
57
+ }
58
+
59
+ .inspect-modal__section-title {
60
+ margin: sizes.$spaceComfy 0 sizes.$spaceCompact;
61
+ font-size: sizes.$fontSizeSmPlus;
62
+ font-weight: 600;
63
+ color: colors.$textColor;
64
+
65
+ &:first-child {
66
+ margin-top: 0;
67
+ }
68
+ }
69
+
70
+ .inspect-modal__table-wrap {
71
+ display: flex;
72
+ flex-direction: column;
73
+ }
74
+
75
+ .inspect-modal__table {
76
+ width: 100%;
77
+ border-collapse: collapse;
78
+ font-size: sizes.$fontSizeSm;
79
+
80
+ th {
81
+ text-align: left;
82
+ padding: sizes.$spaceSnug sizes.$spaceCompact;
83
+ font-weight: 600;
84
+ color: colors.$mutedTextColor;
85
+ border-bottom: 1px solid colors.$borderColor;
86
+ white-space: nowrap;
87
+ }
88
+
89
+ td {
90
+ padding: sizes.$spaceSnug sizes.$spaceCompact;
91
+ border-bottom: 1px solid colors.$borderColor;
92
+ vertical-align: top;
93
+ color: colors.$textColor;
94
+ }
95
+
96
+ tr:last-child td {
97
+ border-bottom: none;
98
+ }
99
+ }
100
+
101
+ .inspect-modal__cell-key {
102
+ font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'Segoe UI Mono', monospace;
103
+ font-size: sizes.$fontSizeXs;
104
+ word-break: break-all;
105
+ max-width: 320px;
106
+ }
107
+
108
+ .inspect-modal__cell-key--nested {
109
+ padding-left: sizes.$spaceRoomy;
110
+ }
111
+
112
+ .inspect-modal__cell-value {
113
+ font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'Segoe UI Mono', monospace;
114
+ font-size: sizes.$fontSizeXs;
115
+ word-break: break-all;
116
+ display: flex;
117
+ align-items: center;
118
+ gap: sizes.$spaceTight;
119
+ }
120
+
121
+ .inspect-modal__badge {
122
+ display: inline-block;
123
+ font-size: sizes.$fontSizeXs;
124
+ padding: 1px 6px;
125
+ border-radius: sizes.$radiusSoft;
126
+ font-weight: 500;
127
+ white-space: nowrap;
128
+ }
129
+
130
+ .inspect-modal__badge--default {
131
+ background: colors.$muted;
132
+ color: colors.$mutedTextColor;
133
+ }
134
+
135
+ .inspect-modal__badge--custom {
136
+ background: colors.$primary;
137
+ color: colors.$primaryForeground;
138
+ }
139
+
140
+ .inspect-modal__color-swatch {
141
+ display: inline-block;
142
+ width: 14px;
143
+ height: 14px;
144
+ border-radius: 3px;
145
+ border: 1px solid colors.$borderColor;
146
+ flex-shrink: 0;
147
+ }
148
+
149
+ .inspect-modal__separator-row td {
150
+ font-weight: 600;
151
+ font-size: sizes.$fontSizeSmPlus;
152
+ color: colors.$mutedTextColor;
153
+ padding-top: sizes.$spaceComfy;
154
+ border-bottom: 1px solid colors.$borderColor;
155
+ }
@@ -1,24 +1,165 @@
1
+ export type Theme = 'light' | 'dark';
2
+
1
3
  export interface AppConfig {
2
- theme: 'light' | 'dark';
3
- isRtl: boolean; //This could be device in future
4
- screenStyle: {
5
- light: { backgroundColor: string; color: string; seperatorColor?: string };
6
- dark: { backgroundColor: string; color: string; seperatorColor?: string };
7
- };
8
4
  localication: Localication;
9
- defaultLanguage?: string;
10
5
  baseSize: { width: number; height: number };
11
6
  }
12
7
 
13
- export const defaultAppConfig: AppConfig = {
14
- theme: 'light',
15
- isRtl: false,
16
- screenStyle: {
17
- light: { backgroundColor: '#FDFDFD', color: '#161827' },
18
- dark: { backgroundColor: '#12131A', color: '#E9EBF9' },
8
+ export type LocalizationKey =
9
+ // paywall – period
10
+ | 'base.builder.paywall.period.monthly'
11
+ | 'base.builder.paywall.period.annual'
12
+ | 'base.builder.paywall.period.weekly'
13
+ | 'base.builder.paywall.period.daily'
14
+ | 'base.builder.paywall.period.monthlyPromo'
15
+ | 'base.builder.paywall.period.annualPromo'
16
+ // paywall – default texts
17
+ | 'base.builder.paywall.pricing.default.text'
18
+ | 'base.builder.paywall.pricing.freeTrial.text'
19
+ | 'base.builder.paywall.pricing.regular.text'
20
+ | 'base.builder.paywall.promo.default.text'
21
+ | 'base.builder.paywall.promo.freeTrial.text'
22
+ | 'base.builder.paywall.promo.regular.text'
23
+ // onboard – titles
24
+ | 'onboard.title.one-page'
25
+ | 'onboard.title.two-page'
26
+ | 'onboard.title.three-page'
27
+ | 'onboard.title.four-page'
28
+ | 'onboard.title.one-page2'
29
+ | 'onboard.title.two-page2'
30
+ | 'onboard.title.three-page2'
31
+ | 'onboard.title.four-page2'
32
+ // onboard – subtitles
33
+ | 'onboard.subtitle.one-page'
34
+ | 'onboard.subtitle.two-page'
35
+ | 'onboard.subtitle.three-page'
36
+ | 'onboard.subtitle.four-page'
37
+ | 'onboard.subtitle.one-page2'
38
+ | 'onboard.subtitle.two-page2'
39
+ | 'onboard.subtitle.three-page2'
40
+ | 'onboard.subtitle.four-page2'
41
+ // onboard – actions
42
+ | 'onboard.next.one-page'
43
+ | 'onboard.next.two-page'
44
+ | 'onboard.next.three-page'
45
+ | 'onboard.skip.one-page'
46
+ | 'onboard.skip.two-page'
47
+ | 'onboard.skip.three-page'
48
+ | 'onboard.allow.four-page'
49
+ // onboard – footer
50
+ | 'view.onboarding.footer.description'
51
+ | 'view.onboarding.btnPrivacy'
52
+ | 'view.onboarding.btnTerms'
53
+ | (string & {});
54
+
55
+ export type Localication = Record<
56
+ string,
57
+ Partial<Record<LocalizationKey, string>>
58
+ >;
59
+
60
+ export const defaultLocalization: Localication = {
61
+ en: {
62
+ // paywall – period
63
+ 'base.builder.paywall.period.monthly': 'per month',
64
+ 'base.builder.paywall.period.annual': 'per year',
65
+ 'base.builder.paywall.period.weekly': 'per week',
66
+ 'base.builder.paywall.period.daily': 'per day',
67
+ 'base.builder.paywall.period.monthlyPromo': 'per month',
68
+ 'base.builder.paywall.period.annualPromo': 'per year',
69
+ // paywall – default texts
70
+ 'base.builder.paywall.pricing.default.text':
71
+ '@promoPrice for the first @promoPeriod, then @localizedPrice @localizedPeriod',
72
+ 'base.builder.paywall.pricing.freeTrial.text':
73
+ '@trialPeriod-@trialPeriodUnit free trial, then @localizedPrice @localizedPeriod',
74
+ 'base.builder.paywall.pricing.regular.text':
75
+ '@localizedPrice @localizedPeriod',
76
+ 'base.builder.paywall.promo.default.text': 'Save @discountPercentage%!',
77
+ 'base.builder.paywall.promo.freeTrial.text':
78
+ '@trialPeriod-@trialPeriodUnit free trial',
79
+ 'base.builder.paywall.promo.regular.text':
80
+ '@localizedPrice @localizedPeriod',
81
+ // onboard – titles
82
+ 'onboard.title.one-page': 'Secure your connection',
83
+ 'onboard.title.two-page': 'Access content worldwide',
84
+ 'onboard.title.three-page': 'Fast and reliable',
85
+ 'onboard.title.four-page': 'Stay notified and safe',
86
+ 'onboard.title.one-page2': 'Secure your connection',
87
+ 'onboard.title.two-page2': 'Access content worldwide',
88
+ 'onboard.title.three-page2': 'Fast and reliable',
89
+ 'onboard.title.four-page2': 'Stay notified and safe',
90
+ // onboard – subtitles
91
+ 'onboard.subtitle.one-page':
92
+ 'Encrypt your traffic and protect your privacy on public Wi\u2011Fi.',
93
+ 'onboard.subtitle.two-page':
94
+ 'Connect to high\u2011speed servers in many countries with one tap.',
95
+ 'onboard.subtitle.three-page':
96
+ 'Auto\u2011connect to the best server for speed and stability.',
97
+ 'onboard.subtitle.four-page':
98
+ 'Enable notifications for connection status and security tips.',
99
+ 'onboard.subtitle.one-page2':
100
+ 'Encrypt your traffic and protect your privacy on public Wi\u2011Fi.',
101
+ 'onboard.subtitle.two-page2':
102
+ 'Connect to high\u2011speed servers in many countries with one tap.',
103
+ 'onboard.subtitle.three-page2':
104
+ 'Auto\u2011connect to the best server for speed and stability.',
105
+ 'onboard.subtitle.four-page2':
106
+ 'Enable notifications for connection status and security tips.',
107
+ // onboard – actions
108
+ 'onboard.next.one-page': 'Next',
109
+ 'onboard.next.two-page': 'Next',
110
+ 'onboard.next.three-page': 'Next',
111
+ 'onboard.skip.one-page': 'Skip',
112
+ 'onboard.skip.two-page': 'Skip',
113
+ 'onboard.skip.three-page': 'Skip',
114
+ 'onboard.allow.four-page': 'Allow',
115
+ // onboard – footer
116
+ 'view.onboarding.footer.description':
117
+ 'By clicking continue, you will be accepting the Terms of service and privacy policy',
118
+ 'view.onboarding.btnPrivacy': 'Privacy Policy',
119
+ 'view.onboarding.btnTerms': 'Terms of Service',
120
+ },
121
+ tr: {
122
+ // paywall – period
123
+ 'base.builder.paywall.period.monthly': 'aylık',
124
+ 'base.builder.paywall.period.annual': 'yıllık',
125
+ 'base.builder.paywall.period.weekly': 'haftalık',
126
+ 'base.builder.paywall.period.daily': 'günlük',
127
+ 'base.builder.paywall.period.monthlyPromo': 'aylık',
128
+ 'base.builder.paywall.period.annualPromo': 'yıllık',
129
+ // paywall – default texts
130
+ 'base.builder.paywall.pricing.default.text':
131
+ 'İlk @promoPeriod için @promoPrice, sonra @localizedPrice @localizedPeriod',
132
+ 'base.builder.paywall.pricing.freeTrial.text':
133
+ '@trialPeriod @trialPeriodUnit ücretsiz deneme, sonra @localizedPrice @localizedPeriod',
134
+ 'base.builder.paywall.pricing.regular.text':
135
+ '@localizedPrice @localizedPeriod',
136
+ 'base.builder.paywall.promo.default.text': '%@discountPercentage indirim!',
137
+ 'base.builder.paywall.promo.freeTrial.text':
138
+ '@trialPeriod @trialPeriodUnit ücretsiz deneme',
139
+ 'base.builder.paywall.promo.regular.text':
140
+ '@localizedPrice @localizedPeriod',
19
141
  },
20
- localication: {},
21
- baseSize: { width: 375, height: 812 },
22
142
  };
23
143
 
24
- export type Localication = Record<string, Record<string, string>>;
144
+ /**
145
+ * Merges custom localization with base localization.
146
+ * Custom values override base values on a per-language, per-key basis.
147
+ */
148
+ export function mergeLocalization(
149
+ base: Localication,
150
+ custom: Localication,
151
+ ): Localication {
152
+ const allLanguages = new Set([...Object.keys(base), ...Object.keys(custom)]);
153
+ const merged: Localication = {};
154
+ for (const lang of allLanguages) {
155
+ merged[lang] = { ...(base[lang] || {}), ...(custom[lang] || {}) };
156
+ }
157
+ return merged;
158
+ }
159
+
160
+ export const defaultTheme: Theme = 'light';
161
+
162
+ export const defaultAppConfig: AppConfig = {
163
+ localication: defaultLocalization,
164
+ baseSize: { width: 375, height: 812 },
165
+ };
@@ -3,8 +3,8 @@ import type {
3
3
  TextPropsGenerated,
4
4
  TextStyleGenerated,
5
5
  } from '../../build-components/Text/TextProps.generated';
6
- import type { AppConfig } from '../../types/PreviewConfig';
7
- import { defaultAppConfig } from '../../types/PreviewConfig';
6
+ import type { Theme } from '../../types/PreviewConfig';
7
+ import { defaultTheme } from '../../types/PreviewConfig';
8
8
  import type { ProjectColors } from '../../types/Project';
9
9
  import type { Fonts } from '../../types/Fonts';
10
10
  import { fs, parseSize } from '../../size-matters';
@@ -85,7 +85,7 @@ function ensureFontWeightLoaded(
85
85
  }
86
86
 
87
87
  export type ExtractTextStyleOptions = {
88
- appConfig?: AppConfig;
88
+ theme?: Theme;
89
89
  projectColors?: ProjectColors;
90
90
  fonts?: Fonts;
91
91
  onFontLoaded?: (fontFamily: string) => void;
@@ -105,10 +105,12 @@ export function extractTextStyle<T extends TextPropsGenerated['attributes']>(
105
105
  if (direct !== undefined && direct !== null) return direct;
106
106
  return styleBag?.[key as keyof TextStyleGenerated];
107
107
  };
108
- const resolvedAppConfig = options.appConfig ?? defaultAppConfig;
109
- const { screenStyle, theme } = resolvedAppConfig;
108
+ const theme = options.theme ?? defaultTheme;
110
109
  const fallbackColor =
111
- theme === 'light' ? screenStyle.light.color : screenStyle.dark.color;
110
+ parseColor('THEME_COLORS.TEXT', {
111
+ projectColors: options.projectColors,
112
+ theme,
113
+ }) ?? 'THEME_COLORS.TEXT';
112
114
 
113
115
  const style: React.CSSProperties = {};
114
116
 
@@ -178,6 +180,11 @@ export function extractTextStyle<T extends TextPropsGenerated['attributes']>(
178
180
  if (textAlign) {
179
181
  style.textAlign = textAlign;
180
182
  }
183
+ const textDecorationLine = get('textDecorationLine') as string | undefined;
184
+ if (textDecorationLine && textDecorationLine !== 'none') {
185
+ style.textDecorationLine =
186
+ textDecorationLine as React.CSSProperties['textDecorationLine'];
187
+ }
181
188
 
182
189
  const viewStyle = extractViewStyle(node, {
183
190
  projectColors: options.projectColors,
@@ -196,6 +203,7 @@ export function extractTextStyle<T extends TextPropsGenerated['attributes']>(
196
203
  'lineHeight',
197
204
  'letterSpacing',
198
205
  'textDecoration',
206
+ 'textDecorationLine',
199
207
  'textTransform',
200
208
  ]);
201
209