@revenuecat/purchases-ui-js 2.1.0 → 2.1.1

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.
@@ -2,7 +2,6 @@
2
2
  import Package from "./Package.svelte";
3
3
  import { componentDecorator } from "../../stories/component-decorator";
4
4
  import { localizationDecorator } from "../../stories/localization-decorator";
5
- import type { PackageProps } from "../../types/components/package";
6
5
  import { defineMeta } from "@storybook/addon-svelte-csf";
7
6
 
8
7
  const defaultLocale = "en_US";
@@ -8,10 +8,15 @@
8
8
  } from "../../stores/variables";
9
9
  import type { PackageProps } from "../../types/components/package";
10
10
  import { derived } from "svelte/store";
11
+ import {
12
+ getPackageInfoContext,
13
+ setPackageInfoContext,
14
+ } from "../../stores/packageInfo";
11
15
 
12
16
  const { stack, package_id }: PackageProps = $props();
13
17
 
14
- const { selectedPackageId, variablesPerPackage } = getPaywallContext();
18
+ const { selectedPackageId, variablesPerPackage, infoPerPackage } =
19
+ getPaywallContext();
15
20
 
16
21
  setSelectedStateContext(
17
22
  derived(selectedPackageId, (value) => value === package_id),
@@ -27,6 +32,13 @@
27
32
  (fallback) => $variablesPerPackage?.[package_id] ?? fallback,
28
33
  );
29
34
  setVariablesContext(variables);
35
+
36
+ const fallbackPackageInfo = getPackageInfoContext();
37
+ const packageInfo = derived(
38
+ fallbackPackageInfo,
39
+ (fallback) => $infoPerPackage?.[package_id] ?? fallback,
40
+ );
41
+ setPackageInfoContext(packageInfo);
30
42
  </script>
31
43
 
32
44
  <Stack {...stack} onclick={onPackageClick} />
@@ -12,7 +12,7 @@
12
12
  import type { SheetProps } from "../../types/components/sheet";
13
13
  import type { PaywallData } from "../../types/paywall";
14
14
  import type { UIConfig } from "../../types/ui-config";
15
- import type { VariableDictionary } from "../../types/variables";
15
+ import type { PackageInfo, VariableDictionary } from "../../types/variables";
16
16
  import { mapBackground } from "../../utils/background-utils";
17
17
  import { css } from "../../utils/base-utils";
18
18
  import { registerFonts } from "../../utils/font-utils";
@@ -21,11 +21,16 @@
21
21
  import { derived, readable, writable } from "svelte/store";
22
22
  import Stack from "../stack/Stack.svelte";
23
23
  import Sheet from "./Sheet.svelte";
24
+ import {
25
+ type PackageInfoStore,
26
+ setPackageInfoContext,
27
+ } from "../../stores/packageInfo";
24
28
 
25
29
  interface Props {
26
30
  paywallData: PaywallData;
27
31
  selectedLocale?: string;
28
32
  variablesPerPackage?: Record<string, VariableDictionary>;
33
+ infoPerPackage?: Record<string, PackageInfo>;
29
34
  uiConfig: UIConfig;
30
35
  preferredColorMode?: ColorMode;
31
36
  onPurchaseClicked?: (selectedPackageId: string) => void;
@@ -41,6 +46,7 @@
41
46
  paywallData,
42
47
  selectedLocale,
43
48
  variablesPerPackage = {},
49
+ infoPerPackage = {},
44
50
  preferredColorMode,
45
51
  onPurchaseClicked,
46
52
  onBackClicked,
@@ -118,6 +124,7 @@
118
124
  setPaywallContext({
119
125
  selectedPackageId,
120
126
  variablesPerPackage: readable(variablesPerPackage),
127
+ infoPerPackage: readable(infoPerPackage),
121
128
  onPurchase,
122
129
  onButtonAction,
123
130
  uiConfig,
@@ -130,6 +137,13 @@
130
137
 
131
138
  setVariablesContext(variables);
132
139
 
140
+ const packageInfo: PackageInfoStore = derived(
141
+ selectedPackageId,
142
+ (packageId) => infoPerPackage[packageId || ""],
143
+ );
144
+
145
+ setPackageInfoContext(packageInfo);
146
+
133
147
  const style = $derived(
134
148
  css({
135
149
  ...mapBackground(colorMode, null, base.background),
@@ -192,10 +206,12 @@
192
206
  padding: 0;
193
207
  font-family: sans-serif;
194
208
  }
209
+
195
210
  body {
196
211
  line-height: 1.5;
197
212
  -webkit-font-smoothing: antialiased;
198
213
  }
214
+
199
215
  img,
200
216
  picture,
201
217
  video,
@@ -211,6 +227,7 @@
211
227
  select {
212
228
  font: inherit;
213
229
  }
230
+
214
231
  p,
215
232
  h1,
216
233
  h2,
@@ -1,11 +1,12 @@
1
1
  import type { ColorMode } from "../../types";
2
2
  import type { PaywallData } from "../../types/paywall";
3
3
  import type { UIConfig } from "../../types/ui-config";
4
- import type { VariableDictionary } from "../../types/variables";
4
+ import type { PackageInfo, VariableDictionary } from "../../types/variables";
5
5
  interface Props {
6
6
  paywallData: PaywallData;
7
7
  selectedLocale?: string;
8
8
  variablesPerPackage?: Record<string, VariableDictionary>;
9
+ infoPerPackage?: Record<string, PackageInfo>;
9
10
  uiConfig: UIConfig;
10
11
  preferredColorMode?: ColorMode;
11
12
  onPurchaseClicked?: (selectedPackageId: string) => void;
@@ -3,7 +3,7 @@
3
3
  import { localizationDecorator } from "../../stories/localization-decorator";
4
4
  import { variablesDecorator } from "../../stories/variables-decorator";
5
5
  import type { Localizations } from "../../types/localization";
6
- import type { VariableDictionary } from "../../types/variables";
6
+ import type { PackageInfo, VariableDictionary } from "../../types/variables";
7
7
  import { defineMeta } from "@storybook/addon-svelte-csf";
8
8
  import { VARIABLES } from "../paywall/fixtures/variables";
9
9
  import { DEFAULT_SPACING } from "../../utils/constants";
@@ -52,6 +52,18 @@
52
52
  "product.price": "$39.99",
53
53
  "product.price_per_period": "$39.99/yr",
54
54
  } satisfies VariableDictionary;
55
+
56
+ const mockPackageInfoWithIntroOffer: PackageInfo = {
57
+ hasIntroOffer: true,
58
+ };
59
+
60
+ const mockPackageInfoWithTrial: PackageInfo = {
61
+ hasTrial: true,
62
+ };
63
+ </script>
64
+
65
+ <script>
66
+ import { packageInfoDecorator } from "../../stories/packageInfo-decorator";
55
67
  </script>
56
68
 
57
69
  <Story name="Default" args={{ name: "hello world!" }} />
@@ -246,3 +258,35 @@
246
258
  } satisfies Localizations,
247
259
  }}
248
260
  />
261
+
262
+ <Story
263
+ name="With overrides for Intro Offers"
264
+ args={{
265
+ font_weight: "regular",
266
+ horizontal_alignment: "leading",
267
+ padding: { top: 16, trailing: 24, bottom: 16, leading: 24 },
268
+ margin: { top: 8, trailing: 0, bottom: 8, leading: 0 },
269
+ name: "hello world!",
270
+ overrides: [
271
+ {
272
+ conditions: [{ type: "intro_offer" }],
273
+ properties: { text_lid: "override_text_lid" },
274
+ },
275
+ ],
276
+ text_lid,
277
+ }}
278
+ decorators={[
279
+ variablesDecorator(mockVariableDictionary),
280
+ packageInfoDecorator(mockPackageInfoWithIntroOffer),
281
+ ]}
282
+ parameters={{
283
+ localizations: {
284
+ [defaultLocale]: {
285
+ [text_lid]:
286
+ "This is a text with variables: {{ product.store_product_name }} for {{ product.price }} per {{ product.price_per_period }} and a missing variable: {{ sub_period_abbreviated }}",
287
+ override_text_lid:
288
+ "This text is shown only when an intro offer is defined for the current package.",
289
+ },
290
+ } satisfies Localizations,
291
+ }}
292
+ />
@@ -11,16 +11,25 @@
11
11
  import { getSelectedStateContext } from "../../stores/selected";
12
12
  import { getVariablesContext } from "../../stores/variables";
13
13
  import type { TextNodeProps } from "../../types/components/text";
14
- import { getActiveStateProps } from "../../utils/style-utils";
14
+ import {
15
+ getActiveStateProps,
16
+ getIntroOfferStateProps,
17
+ } from "../../utils/style-utils";
15
18
  import { replaceVariables } from "../../utils/variable-utils";
19
+ import { getPackageInfoContext } from "../../stores/packageInfo";
16
20
 
17
21
  const props: TextNodeProps = $props();
18
22
 
19
23
  const selectedState = getSelectedStateContext();
24
+ const packageInfo = getPackageInfoContext();
20
25
  const actualProps = $derived.by(() => {
21
26
  return {
22
27
  ...props,
23
28
  ...getActiveStateProps($selectedState, props.overrides),
29
+ ...getIntroOfferStateProps(
30
+ !!$packageInfo?.hasIntroOffer,
31
+ props.overrides,
32
+ ),
24
33
  };
25
34
  });
26
35
 
package/dist/index.d.ts CHANGED
@@ -11,6 +11,6 @@ export { default as Video } from "./components/video/Video.svelte";
11
11
  export * from "./types";
12
12
  export { type PaywallData } from "./types/paywall";
13
13
  export { type UIConfig } from "./types/ui-config";
14
- export { type VariableDictionary } from "./types/variables";
14
+ export { type VariableDictionary, type PackageInfo, } from "./types/variables";
15
15
  export * from "./ui/globals";
16
16
  export { default as Button } from "./ui/molecules/button.svelte";
@@ -0,0 +1,5 @@
1
+ import type { PackageInfo } from "../types/variables";
2
+ import { type Readable } from "svelte/store";
3
+ export type PackageInfoStore = Readable<PackageInfo | undefined>;
4
+ export declare function setPackageInfoContext(variables: PackageInfoStore): void;
5
+ export declare function getPackageInfoContext(): PackageInfoStore;
@@ -0,0 +1,13 @@
1
+ import { getContext, setContext } from "svelte";
2
+ import {} from "svelte/store";
3
+ const key = Symbol("packageInfo");
4
+ export function setPackageInfoContext(variables) {
5
+ setContext(key, variables);
6
+ }
7
+ export function getPackageInfoContext() {
8
+ const context = getContext(key);
9
+ if (context === undefined) {
10
+ throw new Error("PackageInfo context not found");
11
+ }
12
+ return context;
13
+ }
@@ -1,10 +1,11 @@
1
1
  import type { Action } from "../types/components/button";
2
2
  import type { UIConfig } from "../types/ui-config";
3
- import type { VariableDictionary } from "../types/variables";
3
+ import type { PackageInfo, VariableDictionary } from "../types/variables";
4
4
  import { type Readable, type Writable } from "svelte/store";
5
5
  type PaywallContext = Readonly<{
6
6
  selectedPackageId: Writable<string | undefined>;
7
7
  variablesPerPackage: Readable<Record<string, VariableDictionary> | undefined>;
8
+ infoPerPackage: Readable<Record<string, PackageInfo> | undefined>;
8
9
  onPurchase: () => void;
9
10
  onButtonAction: (action: Action, actionId?: string) => void;
10
11
  uiConfig: UIConfig;
@@ -0,0 +1,3 @@
1
+ import type { PackageInfo } from "../types/variables";
2
+ import type { DecoratorFunction, Renderer } from "storybook/internal/csf";
3
+ export declare function packageInfoDecorator<TRenderer extends Renderer, TArgs>(packageInfo?: PackageInfo): DecoratorFunction<TRenderer, TArgs>;
@@ -0,0 +1,9 @@
1
+ import { readable } from "svelte/store";
2
+ import { setPackageInfoContext } from "../stores/packageInfo";
3
+ export function packageInfoDecorator(packageInfo) {
4
+ const store = readable(packageInfo);
5
+ return (Story) => {
6
+ setPackageInfoContext(store);
7
+ return Story();
8
+ };
9
+ }
@@ -16,6 +16,7 @@ export function paywallDecorator() {
16
16
  setPaywallContext({
17
17
  selectedPackageId,
18
18
  variablesPerPackage: readable(undefined),
19
+ infoPerPackage: readable(undefined),
19
20
  onPurchase: () => window.alert("Purchase clicked"),
20
21
  onButtonAction: (action, actionId) => window.alert(`Button clicked: ${JSON.stringify({ action, actionId }, undefined, 2)}`),
21
22
  uiConfig: emptyUiConfig,
@@ -10,4 +10,8 @@ export declare enum PackageIdentifier {
10
10
  type VariableName = "product.price" | "product.price_per_period" | "product.price_per_period_abbreviated" | "product.price_per_day" | "product.price_per_week" | "product.price_per_month" | "product.price_per_year" | "product.period" | "product.period_abbreviated" | "product.periodly" | "product.period_in_days" | "product.period_in_weeks" | "product.period_in_months" | "product.period_in_years" | "product.period_with_unit" | "product.currency_code" | "product.currency_symbol" | "product.offer_price" | "product.offer_price_per_day" | "product.offer_price_per_week" | "product.offer_price_per_month" | "product.offer_price_per_year" | "product.offer_period" | "product.offer_period_abbreviated" | "product.offer_period_in_days" | "product.offer_period_in_weeks" | "product.offer_period_in_months" | "product.offer_period_in_years" | "product.offer_period_with_unit" | "product.offer_end_date" | "product.secondary_offer_price" | "product.secondary_offer_period" | "product.secondary_offer_period_abbreviated" | "product.relative_discount" | "product.store_product_name";
11
11
  export type VariableDictionary = Record<VariableName, string>;
12
12
  export type VariablesDictionary = Record<PackageIdentifier, VariableDictionary>;
13
+ export interface PackageInfo {
14
+ hasIntroOffer?: boolean;
15
+ hasTrial?: boolean;
16
+ }
13
17
  export {};
@@ -8,3 +8,4 @@ import type { RootPaywall } from "../types/paywall.js";
8
8
  */
9
9
  export declare function findSelectedPackageId({ stack, sticky_footer, }: RootPaywall): string | undefined;
10
10
  export declare const getActiveStateProps: <T extends Component>(selectedState: boolean, overrides?: Overrides<T>) => Partial<T>;
11
+ export declare const getIntroOfferStateProps: <T extends Component>(hasIntroOffer: boolean, overrides?: Overrides<T>) => Partial<T>;
@@ -43,3 +43,11 @@ export const getActiveStateProps = (selectedState, overrides) => {
43
43
  const override = overrides?.find((override) => override.conditions.find((condition) => condition.type === "selected"));
44
44
  return override?.properties ?? {};
45
45
  };
46
+ export const getIntroOfferStateProps = (hasIntroOffer, overrides) => {
47
+ if (!hasIntroOffer) {
48
+ return {};
49
+ }
50
+ const override = overrides?.find((override) => override.conditions.find((condition) => condition.type === "intro_offer" ||
51
+ condition.type === "introductory_offer"));
52
+ return override?.properties ?? {};
53
+ };