@revenuecat/purchases-ui-js 0.0.19 → 0.0.21

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.
@@ -7,7 +7,7 @@
7
7
  SupportedActions,
8
8
  } from "../../data/entities";
9
9
  import type { PurchaseState } from "../../data/state";
10
- import type { ColorMode } from "../../types";
10
+ import { DEFAULT_COLOR_MODE, DEFAULT_TEXT_COLOR } from "../../utils/constants";
11
11
 
12
12
  const onAction = (action: SupportedActions) => {
13
13
  alert(action.type);
@@ -51,7 +51,7 @@
51
51
  const purchaseState: PurchaseState = {
52
52
  locale: "en_US",
53
53
  defaultLocale: "en_US",
54
- colorMode: "light" as ColorMode,
54
+ colorMode: DEFAULT_COLOR_MODE,
55
55
  };
56
56
 
57
57
  export const labelsData = {
@@ -76,7 +76,7 @@
76
76
  light: { type: "hex", value: "transparent" },
77
77
  },
78
78
  color: {
79
- dark: { type: "hex", value: "#000000" },
79
+ dark: { type: "hex", value: DEFAULT_TEXT_COLOR },
80
80
  light: { type: "hex", value: "#ffffff" },
81
81
  },
82
82
  font_size: "body_m",
@@ -1,12 +1,12 @@
1
1
  <script module lang="ts">
2
2
  import { defineMeta } from "@storybook/addon-svelte-csf";
3
3
  import Footer from "./Footer.svelte";
4
- import type { ColorMode } from "../../types";
4
+ import { DEFAULT_COLOR_MODE } from "../../utils/constants";
5
5
 
6
6
  const purchaseState = {
7
7
  locale: "en_US",
8
8
  defaultLocale: "en_US",
9
- colorMode: "light" as ColorMode,
9
+ colorMode: DEFAULT_COLOR_MODE,
10
10
  };
11
11
 
12
12
  const { Story } = defineMeta({
@@ -2,10 +2,10 @@
2
2
  import { defineMeta } from "@storybook/addon-svelte-csf";
3
3
  import Image from "./Image.svelte";
4
4
  import type { PurchaseState } from "../../data/state";
5
- import type { ColorMode } from "../../types";
5
+ import { DEFAULT_COLOR_MODE } from "../../utils/constants";
6
6
 
7
7
  const purchaseState: PurchaseState = {
8
- colorMode: "light" as ColorMode,
8
+ colorMode: DEFAULT_COLOR_MODE,
9
9
  locale: "en_US",
10
10
  defaultLocale: "en_US",
11
11
  };
@@ -64,6 +64,38 @@
64
64
  mask_shape: { type: "rectangle" },
65
65
  }}
66
66
  />
67
+
68
+ <!-- Rounded Rectangle story -->
69
+ <Story
70
+ name="Rounded Rectangle"
71
+ args={{
72
+ purchaseState,
73
+ id: "example-id",
74
+ fit_mode: "fit",
75
+ size: {
76
+ width: { type: "fill" },
77
+ height: { type: "fill" },
78
+ },
79
+ source: {
80
+ light: {
81
+ original: "https://placehold.co/600x400",
82
+ heic: "https://placehold.co/600x400",
83
+ heic_low_res: "https://placehold.co/600x400",
84
+ webp: "https://placehold.co/600x400",
85
+ webp_low_res: "https://placehold.co/600x400",
86
+ },
87
+ },
88
+ mask_shape: {
89
+ type: "rectangle",
90
+ corners: {
91
+ top_leading: 32,
92
+ top_trailing: 32,
93
+ bottom_leading: 32,
94
+ bottom_trailing: 32,
95
+ },
96
+ },
97
+ }}
98
+ />
67
99
  <!-- Circle story -->
68
100
  <Story
69
101
  name="Circle"
@@ -133,9 +165,50 @@
133
165
  mask_shape: { type: "convex" },
134
166
  }}
135
167
  />
136
- <!-- Gradient story -->
168
+ <!-- Radial Gradient story -->
169
+ <Story
170
+ name="Overlay Radial Gradient"
171
+ args={{
172
+ purchaseState,
173
+ id: "example-id",
174
+ fit_mode: "fit",
175
+ size: {
176
+ width: { type: "fill" },
177
+ height: { type: "fill" },
178
+ },
179
+ source: {
180
+ light: {
181
+ original: "https://placehold.co/600x400",
182
+ heic: "https://placehold.co/600x400",
183
+ heic_low_res: "https://placehold.co/600x400",
184
+ webp: "https://placehold.co/600x400",
185
+ webp_low_res: "https://placehold.co/600x400",
186
+ },
187
+ },
188
+ color_overlay: {
189
+ dark: {
190
+ type: "hex",
191
+ value: "#000000FF",
192
+ },
193
+ light: {
194
+ points: [
195
+ {
196
+ color: "#020024ff",
197
+ percent: 0,
198
+ },
199
+ {
200
+ color: "#00d4ff00",
201
+ percent: 100,
202
+ },
203
+ ],
204
+ type: "radial",
205
+ },
206
+ },
207
+ }}
208
+ />
209
+ <!-- Linear Gradient story -->
137
210
  <Story
138
- name="Background Gradient"
211
+ name="Overlay Linear Gradient"
139
212
  args={{
140
213
  purchaseState,
141
214
  id: "example-id",
@@ -153,19 +226,57 @@
153
226
  webp_low_res: "https://placehold.co/600x400",
154
227
  },
155
228
  },
156
- gradient_colors: [
157
- {
158
- dark: { type: "hex", value: "#FFFFFF80" },
159
- light: { type: "hex", value: "#FFFFFF80" },
229
+ color_overlay: {
230
+ dark: {
231
+ type: "hex",
232
+ value: "#000000FF",
233
+ },
234
+ light: {
235
+ degrees: 180,
236
+ points: [
237
+ {
238
+ color: "#020024ff",
239
+ percent: 0,
240
+ },
241
+ {
242
+ color: "#00d4ff00",
243
+ percent: 100,
244
+ },
245
+ ],
246
+ type: "linear",
247
+ },
248
+ },
249
+ }}
250
+ />
251
+ <!-- Solid overlay -->
252
+ <Story
253
+ name="Overlay Solid"
254
+ args={{
255
+ purchaseState,
256
+ id: "example-id",
257
+ fit_mode: "fit",
258
+ size: {
259
+ width: { type: "fill" },
260
+ height: { type: "fill" },
261
+ },
262
+ source: {
263
+ light: {
264
+ original: "https://placehold.co/600x400",
265
+ heic: "https://placehold.co/600x400",
266
+ heic_low_res: "https://placehold.co/600x400",
267
+ webp: "https://placehold.co/600x400",
268
+ webp_low_res: "https://placehold.co/600x400",
160
269
  },
161
- {
162
- dark: { type: "hex", value: "#FFFFFF80" },
163
- light: { type: "hex", value: "#FFFFFF80" },
270
+ },
271
+ color_overlay: {
272
+ dark: {
273
+ type: "hex",
274
+ value: "#000000FF",
164
275
  },
165
- {
166
- dark: { type: "hex", value: "#000000" },
167
- light: { type: "hex", value: "#000000" },
276
+ light: {
277
+ type: "hex",
278
+ value: "#e7c00069",
168
279
  },
169
- ],
280
+ },
170
281
  }}
171
282
  />
@@ -1,16 +1,27 @@
1
1
  <script lang="ts">
2
2
  import { getImageComponentStyles } from "./image-utils";
3
3
  import type { ImageProps } from "../../data/entities";
4
+ import { DEFAULT_COLOR_MODE } from "../../utils/constants";
4
5
 
5
6
  const { id, source, purchaseState, ...restProps }: ImageProps = $props();
6
7
 
7
- const { gradientStyles, imageStyles } = $derived(
8
+ let imageAspectRatio = $state(0);
9
+ let imageElement: HTMLImageElement | null;
10
+
11
+ // Calculate aspect ratio once image loads
12
+ function onImageLoad() {
13
+ if (imageElement) {
14
+ imageAspectRatio = imageElement.naturalHeight / imageElement.naturalWidth;
15
+ }
16
+ }
17
+ const { imageStyles, maskPath, linearGradientAngle } = $derived(
8
18
  getImageComponentStyles({
9
19
  id,
10
20
  colorMode: purchaseState.colorMode,
11
21
  source,
12
22
  purchaseState,
13
23
  ...restProps,
24
+ imageAspectRatio,
14
25
  }),
15
26
  );
16
27
 
@@ -19,29 +30,115 @@
19
30
  if (source[colorMode]?.original) {
20
31
  return source[colorMode].original;
21
32
  } else {
22
- return source["light"].original as string;
33
+ return source[DEFAULT_COLOR_MODE]?.original as string;
23
34
  }
24
35
  });
25
36
  </script>
26
37
 
27
- <div
38
+ <img
39
+ src={imageSource}
40
+ bind:this={imageElement}
41
+ onload={onImageLoad}
42
+ style="display: none;"
43
+ alt=""
44
+ />
45
+
46
+ <svg
28
47
  class="rc-pw-image-container"
29
48
  id={`rc-pw-image-container-${id}`}
30
49
  style={imageStyles}
50
+ preserveAspectRatio="xMidYMid slice"
51
+ viewBox={`0 0 100 ${imageAspectRatio * 100}`}
31
52
  >
32
- <img class="rc-pw-image" src={imageSource} alt="" {id} />
33
- <span class="rc-pw-image-overlay" style={gradientStyles}></span>
34
- </div>
53
+ <defs>
54
+ <clipPath id={`clip-path-${id}`}>
55
+ {#if restProps.mask_shape?.type === "circle"}
56
+ <ellipse
57
+ cx="50"
58
+ cy={imageAspectRatio * 50}
59
+ rx="50"
60
+ ry={imageAspectRatio * 50}
61
+ />
62
+ {:else}
63
+ <path d={maskPath} />
64
+ {/if}
65
+ </clipPath>
66
+
67
+ {#if restProps.color_overlay?.[purchaseState.colorMode]?.type === "linear"}
68
+ <linearGradient
69
+ id={`gradient-${id}`}
70
+ x1={linearGradientAngle.x1}
71
+ y1={linearGradientAngle.y1}
72
+ x2={linearGradientAngle.x2}
73
+ y2={linearGradientAngle.y2}
74
+ >
75
+ {#each restProps.color_overlay?.[purchaseState.colorMode]?.points || restProps.color_overlay?.[DEFAULT_COLOR_MODE]?.points || [] as stop}
76
+ <stop
77
+ offset={`${stop.percent}%`}
78
+ style={`stop-color: ${stop.color}`}
79
+ />
80
+ {/each}
81
+ </linearGradient>
82
+ {:else if restProps.color_overlay?.[purchaseState.colorMode]?.type === "radial"}
83
+ <radialGradient
84
+ id={`gradient-${id}`}
85
+ cx="50%"
86
+ cy="50%"
87
+ r="50%"
88
+ fx="50%"
89
+ fy="50%"
90
+ >
91
+ {#each restProps.color_overlay?.[purchaseState.colorMode]?.points || restProps.color_overlay?.[DEFAULT_COLOR_MODE]?.points || [] as stop}
92
+ <stop
93
+ offset={`${stop.percent}%`}
94
+ style={`stop-color: ${stop.color}`}
95
+ />
96
+ {/each}
97
+ </radialGradient>
98
+ {:else if restProps.color_overlay?.[purchaseState.colorMode]?.type === "hex"}
99
+ <linearGradient id={`gradient-${id}`}>
100
+ <stop
101
+ offset="0%"
102
+ style={`stop-color: ${
103
+ restProps.color_overlay[purchaseState.colorMode]?.value ||
104
+ restProps.color_overlay?.[DEFAULT_COLOR_MODE]?.value
105
+ }`}
106
+ />
107
+ </linearGradient>
108
+ {/if}
109
+ </defs>
110
+
111
+ <image
112
+ class="rc-pw-image"
113
+ href={imageSource}
114
+ x="0"
115
+ y="0"
116
+ width="100"
117
+ height={imageAspectRatio * 100}
118
+ clip-path={`url(#clip-path-${id})`}
119
+ preserveAspectRatio="xMidYMid slice"
120
+ {id}
121
+ />
122
+
123
+ <rect
124
+ class="rc-pw-image-overlay"
125
+ x="0"
126
+ y="0"
127
+ width="100"
128
+ height={imageAspectRatio * 100}
129
+ clip-path={`url(#clip-path-${id})`}
130
+ fill={`url(#gradient-${id})`}
131
+ />
132
+ </svg>
35
133
 
36
134
  <style>
37
135
  .rc-pw-image-container {
38
- position: relative;
39
- overflow: hidden;
40
136
  border-end-start-radius: var(--image-border-end-start-radius, 0px);
41
137
  border-end-end-radius: var(--image-border-end-end-radius, 0px);
42
138
  border-start-start-radius: var(--image-border-start-start-radius, 0px);
43
139
  border-start-end-radius: var(--image-border-start-end-radius, 0px);
44
- clip-path: var(--image-clip-path, initial);
140
+ position: relative;
141
+ overflow: hidden;
45
142
  display: flex;
46
143
  flex: var(--image-flex, 1 1 auto);
47
144
  position: var(--image-position, relative);
@@ -61,6 +158,5 @@
61
158
  .rc-pw-image-overlay {
62
159
  position: absolute;
63
160
  inset: 0;
64
- background: var(--image-background, none);
65
161
  }
66
162
  </style>
@@ -4,7 +4,15 @@ import type { ImageProps } from "../../data/entities";
4
4
  * @param props - Image component properties including gradient, mask and size
5
5
  * @returns Object containing image style variables and gradient style variables
6
6
  */
7
- export declare const getImageComponentStyles: (props: ImageProps) => {
7
+ export declare const getImageComponentStyles: (props: ImageProps & {
8
+ imageAspectRatio: number;
9
+ }) => {
8
10
  imageStyles: string;
9
- gradientStyles: string;
11
+ maskPath: string;
12
+ linearGradientAngle: {
13
+ x1: string;
14
+ y1: string;
15
+ x2: string;
16
+ y2: string;
17
+ };
10
18
  };
@@ -1,14 +1,11 @@
1
- import { getActiveStateProps, getGradientStyle, getMaskStyle, getSizeStyle, prefixObject, stringifyStyles, } from "../../utils/style-utils";
1
+ import { getActiveStateProps, getLinearGradientAngle, getMaskPath, getMaskStyle, getSizeStyle, prefixObject, stringifyStyles, } from "../../utils/style-utils";
2
2
  /**
3
3
  * Generates comprehensive styles for image components by combining gradient and size styles
4
4
  * @param props - Image component properties including gradient, mask and size
5
5
  * @returns Object containing image style variables and gradient style variables
6
6
  */
7
7
  export const getImageComponentStyles = (props) => {
8
- const { gradient_colors, mask_shape, size,
9
- // max_height, // TODO: implement this. still waiting on spec
10
- // override_source_lid, // TODO: Implement this once structure is defined
11
- overrides, componentState, purchaseState, zStackChildStyles, } = props;
8
+ const { size, overrides, componentState, zStackChildStyles } = props;
12
9
  const imageStyles = {
13
10
  "--height": "unset",
14
11
  "--width": "unset",
@@ -21,21 +18,16 @@ export const getImageComponentStyles = (props) => {
21
18
  "--inset": "0",
22
19
  "--transform": "initial",
23
20
  };
24
- Object.assign(imageStyles, zStackChildStyles);
25
- const backgroundStyles = {
26
- "--background": "none",
27
- };
28
21
  const activeStateProps = getActiveStateProps(overrides, componentState);
29
- Object.assign(backgroundStyles, getGradientStyle(purchaseState.colorMode, activeStateProps?.gradient_colors || gradient_colors));
22
+ Object.assign(imageStyles, zStackChildStyles);
30
23
  Object.assign(imageStyles, getSizeStyle({ ...size, ...activeStateProps }));
31
- Object.assign(imageStyles, getMaskStyle({
32
- ...mask_shape,
33
- ...activeStateProps,
34
- }));
24
+ Object.assign(imageStyles, getMaskStyle(props.mask_shape));
35
25
  const prefixedImageStyles = prefixObject(imageStyles, "image");
36
- const prefixedGradientStyles = prefixObject(backgroundStyles, "image");
26
+ const maskPath = getMaskPath(props);
27
+ const linearGradientAngle = getLinearGradientAngle(props);
37
28
  return {
38
29
  imageStyles: stringifyStyles(prefixedImageStyles),
39
- gradientStyles: stringifyStyles(prefixedGradientStyles),
30
+ maskPath,
31
+ linearGradientAngle,
40
32
  };
41
33
  };
@@ -3,7 +3,7 @@
3
3
  import Package from "./Package.svelte";
4
4
  import type { Extra, SupportedActions } from "../../data/entities";
5
5
  import type { PurchaseState } from "../../data/state";
6
- import type { ColorMode } from "../../types";
6
+ import { DEFAULT_COLOR_MODE } from "../../utils/constants";
7
7
 
8
8
  const onAction = (action: SupportedActions, data?: Extra) => {
9
9
  alert(`${action.type} ${JSON.stringify(data)}`);
@@ -34,7 +34,7 @@
34
34
  },
35
35
  };
36
36
  const purchaseState: PurchaseState = {
37
- colorMode: "light" as ColorMode,
37
+ colorMode: DEFAULT_COLOR_MODE,
38
38
  locale: "en_US",
39
39
  defaultLocale: "en_US",
40
40
  };
@@ -21,6 +21,7 @@
21
21
  import { prefersDarkMode } from "../../stores/theme";
22
22
  import type { ColorMode } from "../../types";
23
23
  import Footer from "../footer/Footer.svelte";
24
+ import { DEFAULT_COLOR_MODE } from "../../utils/constants";
24
25
 
25
26
  interface Props {
26
27
  paywallData: PaywallData;
@@ -53,7 +54,8 @@
53
54
  locale: selectedLocale || paywallData.default_locale,
54
55
  defaultLocale: paywallData.default_locale,
55
56
  variablesPerPackage,
56
- colorMode: preferredColorMode || ($prefersDarkMode ? "dark" : "light"),
57
+ colorMode:
58
+ preferredColorMode || ($prefersDarkMode ? "dark" : DEFAULT_COLOR_MODE),
57
59
  });
58
60
 
59
61
  let variableDictionary = $state(
@@ -1,7 +1,7 @@
1
1
  import type { PaywallData } from "../../data/entities";
2
2
  import { type VariableDictionary } from "../../utils/variable-utils";
3
3
  import type { ColorMode } from "../../types";
4
- declare const Paywall: import("svelte").Component<{
4
+ interface Props {
5
5
  paywallData: PaywallData;
6
6
  onPurchaseClicked?: (selectedPackageId: string) => void;
7
7
  onBackClicked?: () => void;
@@ -12,6 +12,7 @@ declare const Paywall: import("svelte").Component<{
12
12
  variablesPerPackage?: Record<string, VariableDictionary>;
13
13
  preferredColorMode?: ColorMode;
14
14
  onError?: (error: unknown) => void;
15
- }, {}, "">;
15
+ }
16
+ declare const Paywall: import("svelte").Component<Props, {}, "">;
16
17
  type Paywall = ReturnType<typeof Paywall>;
17
18
  export default Paywall;
@@ -4,4 +4,4 @@ export declare function getBackgroundStyles({ background, colorMode, }: {
4
4
  background?: BaseNodeBackgroundType;
5
5
  colorMode: ColorMode;
6
6
  }): string;
7
- export declare function getBackgroundImageSource(paywallData: PaywallData, colorMode: ColorMode): string;
7
+ export declare function getBackgroundImageSource(paywallData: PaywallData, colorMode: ColorMode): string | undefined;
@@ -1,3 +1,4 @@
1
+ import { DEFAULT_COLOR_MODE } from "../../utils/constants";
1
2
  import { getColor, prefixObject, stringifyStyles, } from "../../utils/style-utils";
2
3
  export function getBackgroundStyles({ background, colorMode, }) {
3
4
  const styles = { "--background": "initial" };
@@ -19,5 +20,5 @@ export function getBackgroundImageSource(paywallData, colorMode) {
19
20
  const backgroundObject = paywallData.components_config.base
20
21
  .background;
21
22
  return (backgroundObject.value[colorMode]?.original ||
22
- backgroundObject.value["light"]?.original);
23
+ backgroundObject.value[DEFAULT_COLOR_MODE]?.original);
23
24
  }
@@ -2,8 +2,9 @@
2
2
  import { defineMeta } from "@storybook/addon-svelte-csf";
3
3
  import PurchaseButton from "./PurchaseButton.svelte";
4
4
  import type { PurchaseState } from "../../data/state";
5
- import type { ColorMode, DimensionType } from "../../types";
5
+ import type { DimensionType } from "../../types";
6
6
  import type { SupportedActions } from "../../data/entities";
7
+ import { DEFAULT_COLOR_MODE } from "../../utils/constants";
7
8
 
8
9
  const onAction = (action: SupportedActions) => {
9
10
  alert(action.type);
@@ -33,7 +34,7 @@
33
34
  },
34
35
  };
35
36
  const purchaseState: PurchaseState = {
36
- colorMode: "light" as ColorMode,
37
+ colorMode: DEFAULT_COLOR_MODE,
37
38
  locale: "en_US",
38
39
  defaultLocale: "en_US",
39
40
  };
@@ -12,6 +12,11 @@
12
12
  import { labelsData as labels } from "../../stories/fixtures";
13
13
  import type { PurchaseState } from "../../data/state";
14
14
  import type { PaywallComponent } from "../../data/entities";
15
+ import {
16
+ DEFAULT_BACKGROUND_COLOR,
17
+ DEFAULT_COLOR_MODE,
18
+ DEFAULT_TEXT_COLOR,
19
+ } from "../../utils/constants";
15
20
 
16
21
  const { Story } = defineMeta({
17
22
  title: "Components/Stack",
@@ -35,7 +40,7 @@
35
40
  const purchaseState: PurchaseState = {
36
41
  locale: "en_US",
37
42
  defaultLocale: "en_US",
38
- colorMode: "light",
43
+ colorMode: DEFAULT_COLOR_MODE,
39
44
  };
40
45
 
41
46
  const components: PaywallComponent[] = [
@@ -379,7 +384,7 @@
379
384
  padding: { top: 4, trailing: 12, bottom: 4, leading: 12 },
380
385
  margin: { top: 4, trailing: 4, bottom: 4, leading: 4 },
381
386
  text_lid: "badge",
382
- color: { light: { type: "hex", value: "#000000" } },
387
+ color: { light: { type: "hex", value: DEFAULT_TEXT_COLOR } },
383
388
  font_weight: "bold",
384
389
  font_size: "body_s",
385
390
  horizontal_alignment: "center",
@@ -422,7 +427,7 @@
422
427
  padding: { top: 4, trailing: 12, bottom: 4, leading: 12 },
423
428
  margin: { top: 4, trailing: 4, bottom: 4, leading: 4 },
424
429
  text_lid: "badge",
425
- color: { light: { type: "hex", value: "#000000" } },
430
+ color: { light: { type: "hex", value: DEFAULT_TEXT_COLOR } },
426
431
  font_weight: "bold",
427
432
  font_size: "body_s",
428
433
  horizontal_alignment: "center",
@@ -465,7 +470,7 @@
465
470
  padding: { top: 4, trailing: 12, bottom: 4, leading: 12 },
466
471
  margin: { top: 4, trailing: 4, bottom: 4, leading: 4 },
467
472
  text_lid: "badge",
468
- color: { light: { type: "hex", value: "#000000" } },
473
+ color: { light: { type: "hex", value: DEFAULT_TEXT_COLOR } },
469
474
  font_weight: "bold",
470
475
  font_size: "body_s",
471
476
  horizontal_alignment: "center",
@@ -508,7 +513,7 @@
508
513
  padding: { top: 4, trailing: 12, bottom: 4, leading: 12 },
509
514
  margin: { top: 4, trailing: 4, bottom: 4, leading: 4 },
510
515
  text_lid: "badge",
511
- color: { light: { type: "hex", value: "#000000" } },
516
+ color: { light: { type: "hex", value: DEFAULT_TEXT_COLOR } },
512
517
  font_weight: "bold",
513
518
  font_size: "body_s",
514
519
  horizontal_alignment: "center",
@@ -551,7 +556,7 @@
551
556
  padding: { top: 4, trailing: 12, bottom: 4, leading: 12 },
552
557
  margin: { top: 4, trailing: 4, bottom: 4, leading: 4 },
553
558
  text_lid: "badge",
554
- color: { light: { type: "hex", value: "#000000" } },
559
+ color: { light: { type: "hex", value: DEFAULT_TEXT_COLOR } },
555
560
  font_weight: "bold",
556
561
  font_size: "body_s",
557
562
  horizontal_alignment: "center",
@@ -594,7 +599,7 @@
594
599
  padding: { top: 4, trailing: 12, bottom: 4, leading: 12 },
595
600
  margin: { top: 4, trailing: 4, bottom: 4, leading: 4 },
596
601
  text_lid: "badge",
597
- color: { light: { type: "hex", value: "#000000" } },
602
+ color: { light: { type: "hex", value: DEFAULT_TEXT_COLOR } },
598
603
  font_weight: "bold",
599
604
  font_size: "body_s",
600
605
  horizontal_alignment: "center",
@@ -637,7 +642,7 @@
637
642
  padding: { top: 4, trailing: 12, bottom: 4, leading: 12 },
638
643
  margin: { top: 4, trailing: 4, bottom: 4, leading: 4 },
639
644
  text_lid: "badge",
640
- color: { light: { type: "hex", value: "#000000" } },
645
+ color: { light: { type: "hex", value: DEFAULT_TEXT_COLOR } },
641
646
  font_weight: "bold",
642
647
  font_size: "body_s",
643
648
  horizontal_alignment: "center",
@@ -680,7 +685,7 @@
680
685
  padding: { top: 4, trailing: 12, bottom: 4, leading: 12 },
681
686
  margin: { top: 4, trailing: 4, bottom: 4, leading: 4 },
682
687
  text_lid: "badge",
683
- color: { light: { type: "hex", value: "#000000" } },
688
+ color: { light: { type: "hex", value: DEFAULT_TEXT_COLOR } },
684
689
  font_weight: "bold",
685
690
  font_size: "body_s",
686
691
  horizontal_alignment: "center",
@@ -775,7 +780,7 @@
775
780
  padding: { top: 4, trailing: 12, bottom: 4, leading: 12 },
776
781
  margin: { top: 4, trailing: 4, bottom: 4, leading: 4 },
777
782
  text_lid: "badge",
778
- color: { light: { type: "hex", value: "#000000" } },
783
+ color: { light: { type: "hex", value: DEFAULT_TEXT_COLOR } },
779
784
  font_weight: "bold",
780
785
  font_size: "body_s",
781
786
  horizontal_alignment: "center",
@@ -818,7 +823,7 @@
818
823
  padding: { top: 4, trailing: 12, bottom: 4, leading: 12 },
819
824
  margin: { top: 4, trailing: 4, bottom: 4, leading: 4 },
820
825
  text_lid: "badge",
821
- color: { light: { type: "hex", value: "#000000" } },
826
+ color: { light: { type: "hex", value: DEFAULT_TEXT_COLOR } },
822
827
  font_weight: "bold",
823
828
  font_size: "body_s",
824
829
  horizontal_alignment: "center",
@@ -861,7 +866,7 @@
861
866
  padding: { top: 4, trailing: 12, bottom: 4, leading: 12 },
862
867
  margin: { top: 4, trailing: 4, bottom: 4, leading: 4 },
863
868
  text_lid: "badge",
864
- color: { light: { type: "hex", value: "#000000" } },
869
+ color: { light: { type: "hex", value: DEFAULT_TEXT_COLOR } },
865
870
  font_weight: "bold",
866
871
  font_size: "body_s",
867
872
  horizontal_alignment: "center",
@@ -904,7 +909,7 @@
904
909
  padding: { top: 4, trailing: 12, bottom: 4, leading: 12 },
905
910
  margin: { top: 4, trailing: 4, bottom: 4, leading: 4 },
906
911
  text_lid: "badge",
907
- color: { light: { type: "hex", value: "#000000" } },
912
+ color: { light: { type: "hex", value: DEFAULT_TEXT_COLOR } },
908
913
  font_weight: "bold",
909
914
  font_size: "body_s",
910
915
  horizontal_alignment: "center",
@@ -947,7 +952,7 @@
947
952
  padding: { top: 4, trailing: 12, bottom: 4, leading: 12 },
948
953
  margin: { top: 4, trailing: 4, bottom: 4, leading: 4 },
949
954
  text_lid: "badge",
950
- color: { light: { type: "hex", value: "#000000" } },
955
+ color: { light: { type: "hex", value: DEFAULT_TEXT_COLOR } },
951
956
  font_weight: "bold",
952
957
  font_size: "body_s",
953
958
  horizontal_alignment: "center",
@@ -990,7 +995,7 @@
990
995
  padding: { top: 4, trailing: 12, bottom: 4, leading: 12 },
991
996
  margin: { top: 4, trailing: 4, bottom: 4, leading: 4 },
992
997
  text_lid: "badge",
993
- color: { light: { type: "hex", value: "#000000" } },
998
+ color: { light: { type: "hex", value: DEFAULT_TEXT_COLOR } },
994
999
  font_weight: "bold",
995
1000
  font_size: "body_s",
996
1001
  horizontal_alignment: "center",
@@ -1036,8 +1041,8 @@
1036
1041
  distribution: "space_around",
1037
1042
  },
1038
1043
  background_color: {
1039
- light: { type: "hex", value: "#FFFFFF" },
1040
- dark: { type: "hex", value: "#FFFFFF" },
1044
+ light: { type: "hex", value: DEFAULT_BACKGROUND_COLOR },
1045
+ dark: { type: "hex", value: DEFAULT_BACKGROUND_COLOR },
1041
1046
  },
1042
1047
  components,
1043
1048
  labels,
@@ -1082,7 +1087,7 @@
1082
1087
  padding: { top: 4, trailing: 12, bottom: 4, leading: 12 },
1083
1088
  margin: { top: 0, trailing: 0, bottom: 0, leading: 0 },
1084
1089
  text_lid: "badge",
1085
- color: { light: { type: "hex", value: "#000000" } },
1090
+ color: { light: { type: "hex", value: DEFAULT_TEXT_COLOR } },
1086
1091
  font_weight: "bold",
1087
1092
  font_size: "body_s",
1088
1093
  horizontal_alignment: "center",
@@ -1104,8 +1109,8 @@
1104
1109
  distribution: "space_around",
1105
1110
  },
1106
1111
  background_color: {
1107
- light: { type: "hex", value: "#FFFFFF" },
1108
- dark: { type: "hex", value: "#FFFFFF" },
1112
+ light: { type: "hex", value: DEFAULT_BACKGROUND_COLOR },
1113
+ dark: { type: "hex", value: DEFAULT_BACKGROUND_COLOR },
1109
1114
  },
1110
1115
  components,
1111
1116
  labels,
@@ -1150,7 +1155,7 @@
1150
1155
  padding: { top: 4, trailing: 12, bottom: 4, leading: 12 },
1151
1156
  margin: { top: 0, trailing: 0, bottom: 0, leading: 0 },
1152
1157
  text_lid: "badge",
1153
- color: { light: { type: "hex", value: "#000000" } },
1158
+ color: { light: { type: "hex", value: DEFAULT_TEXT_COLOR } },
1154
1159
  font_weight: "bold",
1155
1160
  font_size: "body_s",
1156
1161
  horizontal_alignment: "center",
@@ -1171,7 +1176,7 @@
1171
1176
  alignment: "center",
1172
1177
  },
1173
1178
  background_color: {
1174
- light: { type: "hex", value: "#FFFFFF" },
1179
+ light: { type: "hex", value: DEFAULT_BACKGROUND_COLOR },
1175
1180
  },
1176
1181
  components: [
1177
1182
  {
@@ -1231,7 +1236,7 @@
1231
1236
  padding: { top: 4, trailing: 12, bottom: 4, leading: 12 },
1232
1237
  margin: { top: 4, trailing: 4, bottom: 4, leading: 4 },
1233
1238
  text_lid: "badge",
1234
- color: { light: { type: "hex", value: "#000000" } },
1239
+ color: { light: { type: "hex", value: DEFAULT_TEXT_COLOR } },
1235
1240
  font_weight: "bold",
1236
1241
  font_size: "body_s",
1237
1242
  horizontal_alignment: "center",
@@ -1290,7 +1295,7 @@
1290
1295
  padding: { top: 4, trailing: 12, bottom: 4, leading: 12 },
1291
1296
  margin: { top: 4, trailing: 4, bottom: 4, leading: 4 },
1292
1297
  text_lid: "badge",
1293
- color: { light: { type: "hex", value: "#000000" } },
1298
+ color: { light: { type: "hex", value: DEFAULT_TEXT_COLOR } },
1294
1299
  font_weight: "bold",
1295
1300
  font_size: "body_s",
1296
1301
  horizontal_alignment: "center",
@@ -1349,7 +1354,7 @@
1349
1354
  padding: { top: 4, trailing: 12, bottom: 4, leading: 12 },
1350
1355
  margin: { top: 4, trailing: 4, bottom: 4, leading: 4 },
1351
1356
  text_lid: "badge",
1352
- color: { light: { type: "hex", value: "#000000" } },
1357
+ color: { light: { type: "hex", value: DEFAULT_TEXT_COLOR } },
1353
1358
  font_weight: "bold",
1354
1359
  font_size: "body_s",
1355
1360
  horizontal_alignment: "center",
@@ -1408,7 +1413,7 @@
1408
1413
  padding: { top: 4, trailing: 12, bottom: 4, leading: 12 },
1409
1414
  margin: { top: 4, trailing: 4, bottom: 4, leading: 4 },
1410
1415
  text_lid: "badge",
1411
- color: { light: { type: "hex", value: "#000000" } },
1416
+ color: { light: { type: "hex", value: DEFAULT_TEXT_COLOR } },
1412
1417
  font_weight: "bold",
1413
1418
  font_size: "body_s",
1414
1419
  horizontal_alignment: "center",
@@ -10,6 +10,7 @@
10
10
  horizontalAlignmentStoryMeta,
11
11
  } from "../../stories/meta-templates";
12
12
  import type { VariableDictionary } from "../../utils/variable-utils";
13
+ import { DEFAULT_COLOR_MODE, DEFAULT_TEXT_COLOR } from "../../utils/constants";
13
14
 
14
15
  /*
15
16
  * Documentation for this component can be found in https://www.notion.so/revenuecat/Text-e257cb046e104351861f8364ede617be?pvs=4
@@ -52,7 +53,7 @@
52
53
  purchaseState: {
53
54
  locale: "en_US",
54
55
  defaultLocale: "en_US",
55
- colorMode: "light",
56
+ colorMode: DEFAULT_COLOR_MODE,
56
57
  },
57
58
  },
58
59
  });
@@ -157,8 +158,8 @@
157
158
  },
158
159
  },
159
160
  background_color: {
160
- dark: { type: "hex", value: "#000000" },
161
- light: { type: "hex", value: "#000000" },
161
+ dark: { type: "hex", value: DEFAULT_TEXT_COLOR },
162
+ light: { type: "hex", value: DEFAULT_TEXT_COLOR },
162
163
  },
163
164
  name: "hello world!",
164
165
  }}
@@ -212,8 +213,8 @@
212
213
  },
213
214
  },
214
215
  background_color: {
215
- dark: { type: "hex", value: "#000000" },
216
- light: { type: "hex", value: "#000000" },
216
+ dark: { type: "hex", value: DEFAULT_TEXT_COLOR },
217
+ light: { type: "hex", value: DEFAULT_TEXT_COLOR },
217
218
  },
218
219
  name: "hello world!",
219
220
  }}
@@ -1,9 +1,10 @@
1
1
  import { getActiveStateProps, getComponentStyles, getSizeStyle, getTextComponentTag, getTextStyles, prefixObject, stringifyStyles, } from "../../utils/style-utils";
2
+ import { DEFAULT_BACKGROUND_COLOR, DEFAULT_TEXT_COLOR, } from "../../utils/constants";
2
3
  export const defaultColor = {
3
- light: { type: "hex", value: "#000000" },
4
+ light: { type: "hex", value: DEFAULT_TEXT_COLOR },
4
5
  };
5
6
  export const defaultBackgroundColor = {
6
- light: { type: "hex", value: "#FFFFFF" },
7
+ light: { type: "hex", value: DEFAULT_BACKGROUND_COLOR },
7
8
  };
8
9
  /**
9
10
  * Generates comprehensive styles for text components by combining text, component and size styles
@@ -4,6 +4,7 @@
4
4
  import type { PurchaseState } from "../../data/state";
5
5
  import { defaultColor } from "../text/text-utils";
6
6
  import { type TimelineProps } from "../../data/entities";
7
+ import { DEFAULT_COLOR_MODE } from "../../utils/constants";
7
8
 
8
9
  const { Story } = defineMeta({
9
10
  title: "Components/Timeline",
@@ -15,7 +16,7 @@
15
16
  const purchaseState: PurchaseState = {
16
17
  locale: "en_US",
17
18
  defaultLocale: "en_US",
18
- colorMode: "light",
19
+ colorMode: DEFAULT_COLOR_MODE,
19
20
  };
20
21
 
21
22
  const labels = {
@@ -194,7 +194,7 @@ export interface ImageProps extends SharedComponentProps {
194
194
  fit_mode: FitTypes;
195
195
  size: SizeType;
196
196
  source: ImageSourceType;
197
- gradient_colors?: ColorType[];
197
+ color_overlay?: ColorType;
198
198
  mask_shape?: ImageMaskShapeType;
199
199
  max_height?: number;
200
200
  override_source_lid?: string;
@@ -1,3 +1,4 @@
1
+ import { DEFAULT_BACKGROUND_COLOR, DEFAULT_TEXT_COLOR, } from "../utils/constants";
1
2
  export const getSpacingStoryMeta = (description) => ({
2
3
  description,
3
4
  control: { type: "object" },
@@ -18,7 +19,10 @@ export const getColorStoryMeta = (description) => ({
18
19
  dark: { type: "color" },
19
20
  },
20
21
  },
21
- defaultValue: { dark: "#FFFFFF", light: "#FFFFFF" },
22
+ defaultValue: {
23
+ dark: DEFAULT_BACKGROUND_COLOR,
24
+ light: DEFAULT_BACKGROUND_COLOR,
25
+ },
22
26
  table: {
23
27
  type: {
24
28
  summary: "object",
@@ -99,7 +103,7 @@ export const shadowStoryMeta = {
99
103
  x: 0,
100
104
  y: 0,
101
105
  radius: 0,
102
- color: { dark: "#000000", light: "#000000" },
106
+ color: { dark: DEFAULT_TEXT_COLOR, light: DEFAULT_TEXT_COLOR },
103
107
  },
104
108
  table: {
105
109
  type: {
@@ -119,7 +123,7 @@ export const borderStoryMeta = {
119
123
  },
120
124
  defaultValue: {
121
125
  width: 0,
122
- color: { dark: "#000000", light: "#000000" },
126
+ color: { dark: DEFAULT_TEXT_COLOR, light: DEFAULT_TEXT_COLOR },
123
127
  },
124
128
  description: "Border properties including width, color, style, and radius",
125
129
  };
@@ -0,0 +1,4 @@
1
+ import type { ColorMode } from "../types";
2
+ export declare const DEFAULT_COLOR_MODE: ColorMode;
3
+ export declare const DEFAULT_TEXT_COLOR = "#000000";
4
+ export declare const DEFAULT_BACKGROUND_COLOR = "#FFFFFF";
@@ -0,0 +1,3 @@
1
+ export const DEFAULT_COLOR_MODE = "light";
2
+ export const DEFAULT_TEXT_COLOR = "#000000";
3
+ export const DEFAULT_BACKGROUND_COLOR = "#FFFFFF";
@@ -1,4 +1,4 @@
1
- import type { ComponentState, ImageMaskShapeType, PaywallData, TextNodeProps } from "../data/entities.js";
1
+ import type { ComponentState, ImageMaskShapeType, ImageProps, PaywallData, TextNodeProps } from "../data/entities.js";
2
2
  import { type BorderType, type ColorMode, type ColorType, type CornerRadiusType, type DimensionType, FontSizeTags, type ShadowType, type ShapeType, type SizeType, type Spacing } from "../types.js";
3
3
  type MarginVariables = {
4
4
  "--margin-block-start": string;
@@ -81,29 +81,6 @@ type SizeStyleVariables = {
81
81
  * @returns CSS style object with size properties
82
82
  */
83
83
  export declare function getSizeStyle(size: SizeType): SizeStyleVariables;
84
- type GradientStyleVariables = {
85
- "--background": string;
86
- };
87
- /**
88
- * Generates gradient background styles
89
- * @param colorMode - Color mode (light/dark)
90
- * @param gradientColors - Array of colors for gradient
91
- * @returns CSS style object with gradient background
92
- */
93
- export declare function getGradientStyle(colorMode: ColorMode, gradientColors?: ColorType[]): GradientStyleVariables;
94
- type MaskStyleVariables = {
95
- "--border-start-start-radius": string;
96
- "--border-start-end-radius": string;
97
- "--border-end-start-radius": string;
98
- "--border-end-end-radius": string;
99
- "--clip-path": string;
100
- };
101
- /**
102
- * Generates mask styles for images
103
- * @param maskShape - Shape configuration for image mask
104
- * @returns CSS style object with mask properties
105
- */
106
- export declare const getMaskStyle: (maskShape?: ImageMaskShapeType) => MaskStyleVariables;
107
84
  type InsetStyleVariables = {
108
85
  "--inset": string;
109
86
  "--transform": string;
@@ -155,4 +132,27 @@ export declare const getActiveStateProps: <T>(overrides?: {
155
132
  states?: Record<string, T>;
156
133
  }, componentState?: ComponentState) => T;
157
134
  export declare function prefixObject(object?: Record<string, string | number>, prefix?: string): Record<string, string | number>;
135
+ export declare function getMaskPath(props: ImageProps & {
136
+ imageAspectRatio: number;
137
+ }): string;
138
+ type MaskStyleVariables = {
139
+ "--border-start-start-radius": string;
140
+ "--border-start-end-radius": string;
141
+ "--border-end-start-radius": string;
142
+ "--border-end-end-radius": string;
143
+ };
144
+ /**
145
+ * Generates mask styles for images
146
+ * @param maskShape - Shape configuration for image mask
147
+ * @returns CSS style object with mask properties
148
+ */
149
+ export declare const getMaskStyle: (maskShape?: ImageMaskShapeType) => MaskStyleVariables;
150
+ export declare function getLinearGradientAngle(props: ImageProps & {
151
+ imageAspectRatio: number;
152
+ }): {
153
+ x1: string;
154
+ y1: string;
155
+ x2: string;
156
+ y2: string;
157
+ };
158
158
  export {};
@@ -1,3 +1,4 @@
1
+ import { DEFAULT_COLOR_MODE, DEFAULT_TEXT_COLOR, } from "./constants.js";
1
2
  import { FontSizes, FontSizeTags, FontWeights, StackAlignment, StackDirection, StackDistribution, TextAlignments, } from "../types.js";
2
3
  /**
3
4
  * Generates CSS spacing styles for margin or padding
@@ -35,10 +36,10 @@ export function getTextComponentTag(fontSize) {
35
36
  * @param params - Object containing color map, mode and fallback color
36
37
  * @returns Color value as string
37
38
  */
38
- export function getColor({ colorMap, colorMode = "light", fallback = "FFFFFF", }) {
39
+ export function getColor({ colorMap, colorMode = DEFAULT_COLOR_MODE, fallback = "FFFFFF", }) {
39
40
  if (!colorMap)
40
41
  return fallback;
41
- const color = colorMap[colorMode] || colorMap["light"];
42
+ const color = colorMap[colorMode] || colorMap[DEFAULT_COLOR_MODE];
42
43
  let colorPoints = "";
43
44
  switch (color.type) {
44
45
  case "hex":
@@ -64,7 +65,7 @@ export function getColor({ colorMap, colorMode = "light", fallback = "FFFFFF", }
64
65
  * @param colorMode - Color mode (light/dark)
65
66
  * @returns CSS border style string
66
67
  */
67
- export function getBorderStyle(border, colorMode = "light") {
68
+ export function getBorderStyle(border, colorMode = DEFAULT_COLOR_MODE) {
68
69
  if (!border)
69
70
  return "";
70
71
  const color = getColor({ colorMap: border.color, colorMode });
@@ -88,7 +89,7 @@ export function getCornerRadiusStyle(corners) {
88
89
  * @param params - Component style configuration object
89
90
  * @returns CSS style object with component styles
90
91
  */
91
- export function getComponentStyles({ background_color, border, margin, padding, color, colorMode = "light", shape, shadow, }) {
92
+ export function getComponentStyles({ background_color, border, margin, padding, color, colorMode = DEFAULT_COLOR_MODE, shape, shadow, }) {
92
93
  const stylesObject = {
93
94
  "--margin-block-start": "0px",
94
95
  "--margin-inline-end": "0px",
@@ -124,7 +125,7 @@ export function getComponentStyles({ background_color, border, margin, padding,
124
125
  stylesObject["--text-color"] = getColor({
125
126
  colorMap: color,
126
127
  colorMode,
127
- fallback: "#000000",
128
+ fallback: DEFAULT_TEXT_COLOR,
128
129
  });
129
130
  }
130
131
  if (border) {
@@ -143,7 +144,7 @@ export function getComponentStyles({ background_color, border, margin, padding,
143
144
  }
144
145
  if (shadow) {
145
146
  stylesObject["--shadow"] = `${shadow.x}px ${shadow.y}px ${shadow.radius}px
146
- ${getColor({ colorMap: shadow.color, colorMode, fallback: "#000000" })}`;
147
+ ${getColor({ colorMap: shadow.color, colorMode, fallback: DEFAULT_TEXT_COLOR })}`;
147
148
  }
148
149
  return stylesObject;
149
150
  }
@@ -182,59 +183,6 @@ export function getSizeStyle(size) {
182
183
  });
183
184
  return styles;
184
185
  }
185
- /**
186
- * Generates gradient background styles
187
- * @param colorMode - Color mode (light/dark)
188
- * @param gradientColors - Array of colors for gradient
189
- * @returns CSS style object with gradient background
190
- */
191
- export function getGradientStyle(colorMode, gradientColors) {
192
- if (!gradientColors)
193
- return { "--background": "none" };
194
- return {
195
- "--background": `linear-gradient(${gradientColors.map((color) => color[colorMode]?.value || color["light"].value).join(", ")})`,
196
- };
197
- }
198
- /**
199
- * Generates mask styles for images
200
- * @param maskShape - Shape configuration for image mask
201
- * @returns CSS style object with mask properties
202
- */
203
- export const getMaskStyle = (maskShape) => {
204
- const maskStyles = {
205
- "--border-end-start-radius": "0px",
206
- "--border-end-end-radius": "0px",
207
- "--border-start-start-radius": "0px",
208
- "--border-start-end-radius": "0px",
209
- "--clip-path": "none",
210
- };
211
- if (maskShape?.type === "circle") {
212
- Object.assign(maskStyles, {
213
- "--border-end-start-radius": "50%",
214
- "--border-end-end-radius": "50%",
215
- "--border-start-start-radius": "50%",
216
- "--border-start-end-radius": "50%",
217
- });
218
- }
219
- if (maskShape?.type === "rectangle" && maskShape.corners) {
220
- Object.assign(maskStyles, getCornerRadiusStyle(maskShape.corners));
221
- }
222
- // TODO: rework this implementation
223
- if (maskShape?.type === "convex") {
224
- Object.assign(maskStyles, {
225
- "--border-start-start-radius": "0%",
226
- "--border-start-end-radius": "0%",
227
- "--border-end-start-radius": "50%",
228
- "--border-end-end-radius": "50%",
229
- });
230
- }
231
- if (maskShape?.type === "concave") {
232
- Object.assign(maskStyles, {
233
- "--clip-path": "polygon(0 0, 100% 0, 100% 100%, 50% 110%, 0 100%);",
234
- });
235
- }
236
- return maskStyles;
237
- };
238
186
  export function getInsetStyles(dimension) {
239
187
  const defaultStyles = {
240
188
  "--inset": "initial",
@@ -309,7 +257,7 @@ export function getDimensionStyle(dimension) {
309
257
  * @param colorMode - The currently selected ColorMode (dark/light)
310
258
  * @returns CSS style object with text formatting properties
311
259
  */
312
- export function getTextStyles(props, colorMode = "light") {
260
+ export function getTextStyles(props, colorMode = DEFAULT_COLOR_MODE) {
313
261
  const { font_size, horizontal_alignment, font_weight, font_name, color } = props;
314
262
  const styles = {
315
263
  "--text-align": "initial",
@@ -413,3 +361,54 @@ export function prefixObject(object, prefix) {
413
361
  return acc;
414
362
  }, {});
415
363
  }
364
+ export function getMaskPath(props) {
365
+ const { mask_shape: maskShape, imageAspectRatio } = props;
366
+ let maskPath = "";
367
+ if (maskShape?.type === "concave") {
368
+ maskPath = `M 0 0
369
+ H 100
370
+ V ${imageAspectRatio * 100}
371
+ Q 50 ${imageAspectRatio * 80} 0 ${imageAspectRatio * 100}
372
+ Z`;
373
+ }
374
+ else if (maskShape?.type === "convex") {
375
+ maskPath = `M 0 0
376
+ H 100
377
+ V ${imageAspectRatio * 80}
378
+ Q 50 ${imageAspectRatio * 120} 0 ${imageAspectRatio * 80}
379
+ Z`;
380
+ }
381
+ else {
382
+ maskPath = `M 0 0 H 100 V ${imageAspectRatio * 100} H 0 Z`;
383
+ }
384
+ return maskPath;
385
+ }
386
+ /**
387
+ * Generates mask styles for images
388
+ * @param maskShape - Shape configuration for image mask
389
+ * @returns CSS style object with mask properties
390
+ */
391
+ export const getMaskStyle = (maskShape) => {
392
+ const maskStyles = {
393
+ "--border-end-start-radius": "0px",
394
+ "--border-end-end-radius": "0px",
395
+ "--border-start-start-radius": "0px",
396
+ "--border-start-end-radius": "0px",
397
+ };
398
+ if (maskShape?.corners) {
399
+ Object.assign(maskStyles, getCornerRadiusStyle(maskShape.corners));
400
+ }
401
+ return maskStyles;
402
+ };
403
+ export function getLinearGradientAngle(props) {
404
+ if (props.color_overlay?.[props.purchaseState.colorMode]?.type !== "linear") {
405
+ return { x1: "0%", y1: "0%", x2: "0%", y2: "0%" };
406
+ }
407
+ const { color_overlay: colorOverlay } = props;
408
+ const angle = colorOverlay?.[DEFAULT_COLOR_MODE]?.degrees || 0;
409
+ const x1 = "50%";
410
+ const y1 = "0%";
411
+ const x2 = `${Math.round(50 + Math.sin(((angle + 90) * Math.PI) / 90) * 50)}%`;
412
+ const y2 = `${Math.round(50 - Math.cos(((angle + 90) * Math.PI) / 90) * 50)}%`;
413
+ return { x1, y1, x2, y2 };
414
+ }
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@revenuecat/purchases-ui-js",
3
3
  "description": "Web components for Paywalls. Powered by RevenueCat",
4
4
  "private": false,
5
- "version": "0.0.19",
5
+ "version": "0.0.21",
6
6
  "author": {
7
7
  "name": "RevenueCat, Inc."
8
8
  },
@@ -61,37 +61,37 @@
61
61
  "svelte": "^5.3.0"
62
62
  },
63
63
  "devDependencies": {
64
- "@chromatic-com/storybook": "^3.2.2",
65
- "@eslint/js": "^9.16.0",
66
- "@storybook/addon-essentials": "^8.4.7",
67
- "@storybook/addon-interactions": "^8.4.7",
68
- "@storybook/addon-links": "^8.4.7",
64
+ "@chromatic-com/storybook": "^3.2.3",
65
+ "@eslint/js": "^9.18.0",
66
+ "@storybook/addon-essentials": "^8.5.0",
67
+ "@storybook/addon-interactions": "^8.5.0",
68
+ "@storybook/addon-links": "^8.5.0",
69
69
  "@storybook/addon-svelte-csf": "^5.0.0-next.14",
70
- "@storybook/blocks": "^8.4.7",
71
- "@storybook/svelte": "^8.4.7",
72
- "@storybook/sveltekit": "^8.4.7",
73
- "@storybook/test": "^8.4.7",
74
- "@sveltejs/adapter-node": "^5.2.9",
75
- "@sveltejs/kit": "^2.9.0",
70
+ "@storybook/blocks": "^8.5.0",
71
+ "@storybook/svelte": "^8.5.0",
72
+ "@storybook/sveltekit": "^8.5.0",
73
+ "@storybook/test": "^8.5.0",
74
+ "@sveltejs/adapter-node": "^5.2.11",
75
+ "@sveltejs/kit": "^2.15.3",
76
76
  "@sveltejs/package": "^2.3.7",
77
- "@sveltejs/vite-plugin-svelte": "^5.0.2",
78
- "@typescript-eslint/parser": "^8.17.0",
79
- "chromatic": "^11.20.0",
80
- "eslint": "^9.16.0",
77
+ "@sveltejs/vite-plugin-svelte": "^5.0.3",
78
+ "@typescript-eslint/parser": "^8.20.0",
79
+ "chromatic": "^11.24.0",
80
+ "eslint": "^9.18.0",
81
81
  "eslint-plugin-svelte": "^2.46.1",
82
- "globals": "^15.13.0",
82
+ "globals": "^15.14.0",
83
83
  "husky": "^9.1.7",
84
- "jsdom": "^25.0.1",
85
- "lint-staged": "^15.2.10",
84
+ "jsdom": "^26.0.0",
85
+ "lint-staged": "^15.3.0",
86
86
  "prettier": "^3.4.2",
87
- "prettier-plugin-svelte": "^3.3.2",
87
+ "prettier-plugin-svelte": "^3.3.3",
88
88
  "publint": "^0.2.12",
89
- "storybook": "^8.4.7",
90
- "svelte": "^5.9.1",
91
- "svelte-check": "^4.1.1",
92
- "typescript": "^5.7.2",
93
- "typescript-eslint": "^8.17.0",
94
- "vite": "^6.0.3",
89
+ "storybook": "^8.5.0",
90
+ "svelte": "^5.18.0",
91
+ "svelte-check": "^4.1.4",
92
+ "typescript": "^5.7.3",
93
+ "typescript-eslint": "^8.20.0",
94
+ "vite": "^6.0.7",
95
95
  "vitest": "^2.1.8"
96
96
  },
97
97
  "lint-staged": {