@revenuecat/purchases-ui-js 2.0.2 → 2.0.4

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 (144) hide show
  1. package/dist/components/button/ButtonNode.stories.svelte +66 -102
  2. package/dist/components/button/ButtonNode.svelte +18 -26
  3. package/dist/components/carousel/Carousel.stories.svelte +1039 -0
  4. package/dist/components/carousel/Carousel.stories.svelte.d.ts +19 -0
  5. package/dist/components/carousel/Carousel.svelte +298 -0
  6. package/dist/components/carousel/Carousel.svelte.d.ts +4 -0
  7. package/dist/components/carousel/CarouselPage.svelte +39 -0
  8. package/dist/components/carousel/CarouselPage.svelte.d.ts +11 -0
  9. package/dist/components/carousel/PageControl.svelte +93 -0
  10. package/dist/components/carousel/PageControl.svelte.d.ts +4 -0
  11. package/dist/components/carousel/carousel-utils.d.ts +4 -0
  12. package/dist/components/carousel/carousel-utils.js +21 -0
  13. package/dist/components/footer/Footer.stories.svelte +112 -102
  14. package/dist/components/footer/Footer.svelte +8 -4
  15. package/dist/components/icon/Icon.stories.svelte +100 -0
  16. package/dist/components/icon/Icon.stories.svelte.d.ts +19 -0
  17. package/dist/components/icon/Icon.svelte +73 -0
  18. package/dist/components/icon/Icon.svelte.d.ts +4 -0
  19. package/dist/components/image/ClipPath.svelte +49 -0
  20. package/dist/components/image/ClipPath.svelte.d.ts +9 -0
  21. package/dist/components/image/Image.stories.svelte +83 -188
  22. package/dist/components/image/Image.svelte +152 -136
  23. package/dist/components/image/Image.svelte.d.ts +1 -1
  24. package/dist/components/image/Overlay.svelte +36 -0
  25. package/dist/components/image/Overlay.svelte.d.ts +8 -0
  26. package/dist/components/package/Package.stories.svelte +10 -21
  27. package/dist/components/package/Package.svelte +8 -35
  28. package/dist/components/paywall/Node.svelte +25 -32
  29. package/dist/components/paywall/Node.svelte.d.ts +4 -6
  30. package/dist/components/paywall/Paywall.stories.svelte +36 -140
  31. package/dist/components/paywall/Paywall.svelte +23 -7
  32. package/dist/components/paywall/Paywall.svelte.d.ts +4 -2
  33. package/dist/components/paywall/fixtures/override-paywall.d.ts +2 -0
  34. package/dist/components/paywall/fixtures/override-paywall.js +1310 -0
  35. package/dist/components/paywall/fixtures/stack-paywall.d.ts +2 -0
  36. package/dist/components/paywall/fixtures/stack-paywall.js +5223 -0
  37. package/dist/components/paywall/fixtures/variables.d.ts +261 -0
  38. package/dist/components/paywall/fixtures/variables.js +262 -0
  39. package/dist/components/paywall/paywall-utils.d.ts +1 -1
  40. package/dist/components/purchase-button/PurchaseButton.stories.svelte +10 -21
  41. package/dist/components/purchase-button/PurchaseButton.svelte +2 -27
  42. package/dist/components/stack/Stack.stories.svelte +2354 -978
  43. package/dist/components/stack/Stack.svelte +116 -134
  44. package/dist/components/stack/Stack.svelte.d.ts +8 -2
  45. package/dist/components/stack/stack-utils.d.ts +10 -30
  46. package/dist/components/stack/stack-utils.js +77 -255
  47. package/dist/components/text/Text.svelte +3 -37
  48. package/dist/components/text/Text.svelte.d.ts +1 -2
  49. package/dist/components/text/TextNode.stories.svelte +10 -36
  50. package/dist/components/text/TextNode.svelte +25 -28
  51. package/dist/components/text/TextNode.svelte.d.ts +1 -1
  52. package/dist/components/text/text-utils.d.ts +4 -9
  53. package/dist/components/text/text-utils.js +32 -117
  54. package/dist/components/timeline/Timeline.stories.svelte +640 -251
  55. package/dist/components/timeline/Timeline.svelte +42 -28
  56. package/dist/components/timeline/Timeline.svelte.d.ts +1 -1
  57. package/dist/components/timeline/TimelineItem.svelte +80 -112
  58. package/dist/components/timeline/TimelineItem.svelte.d.ts +6 -2
  59. package/dist/components/timeline/timeline-utils.d.ts +24 -6
  60. package/dist/components/timeline/timeline-utils.js +21 -113
  61. package/dist/index.d.ts +3 -2
  62. package/dist/index.js +3 -2
  63. package/dist/stores/color-mode.d.ts +1 -1
  64. package/dist/stores/paywall.d.ts +5 -2
  65. package/dist/stores/selected.d.ts +5 -0
  66. package/dist/stores/selected.js +12 -0
  67. package/dist/stores/variables.d.ts +1 -1
  68. package/dist/stores/variables.js +0 -1
  69. package/dist/stories/component-decorator.d.ts +2 -0
  70. package/dist/stories/component-decorator.js +12 -0
  71. package/dist/stories/fixtures.d.ts +6 -3
  72. package/dist/stories/fixtures.js +5214 -4422
  73. package/dist/stories/paywall-decorator.js +6 -0
  74. package/dist/stories/variables-decorator.d.ts +1 -1
  75. package/dist/stories/viewport-decorator.d.ts +2 -0
  76. package/dist/stories/viewport-decorator.js +8 -0
  77. package/dist/stories/viewport-wrapper.svelte +55 -0
  78. package/dist/stories/viewport-wrapper.svelte.d.ts +10 -0
  79. package/dist/stories/with-layout.d.ts +2 -10
  80. package/dist/stories/with-layout.js +3 -5
  81. package/dist/types/alignment.d.ts +5 -3
  82. package/dist/types/background.d.ts +6 -5
  83. package/dist/types/base.d.ts +7 -0
  84. package/dist/types/colors.d.ts +4 -4
  85. package/dist/types/component.d.ts +7 -2
  86. package/dist/types/components/button.d.ts +6 -1
  87. package/dist/types/components/carousel.d.ts +51 -0
  88. package/dist/types/components/carousel.js +1 -0
  89. package/dist/types/components/footer.d.ts +2 -1
  90. package/dist/types/components/icon.d.ts +28 -0
  91. package/dist/types/components/icon.js +1 -0
  92. package/dist/types/components/image.d.ts +20 -0
  93. package/dist/types/components/image.js +1 -0
  94. package/dist/types/components/package.d.ts +2 -1
  95. package/dist/types/components/purchase-button.d.ts +2 -1
  96. package/dist/types/components/stack.d.ts +32 -0
  97. package/dist/types/components/stack.js +1 -0
  98. package/dist/types/components/text.d.ts +20 -0
  99. package/dist/types/components/text.js +1 -0
  100. package/dist/types/components/timeline.d.ts +35 -0
  101. package/dist/types/components/timeline.js +1 -0
  102. package/dist/types/localization.d.ts +2 -1
  103. package/dist/types/media.d.ts +4 -3
  104. package/dist/types/overrides.d.ts +48 -0
  105. package/dist/types/overrides.js +1 -0
  106. package/dist/types/paywall.d.ts +27 -0
  107. package/dist/types/paywall.js +1 -0
  108. package/dist/types/ui-config.d.ts +20 -0
  109. package/dist/types/ui-config.js +1 -0
  110. package/dist/types/variables.d.ts +13 -0
  111. package/dist/types/variables.js +10 -0
  112. package/dist/types.d.ts +17 -9
  113. package/dist/ui/atoms/typography.stories.svelte +1 -27
  114. package/dist/ui/molecules/button.stories.svelte +3 -8
  115. package/dist/ui/theme/colors.d.ts +0 -6
  116. package/dist/ui/theme/colors.js +1 -1
  117. package/dist/ui/theme/text.d.ts +3 -4
  118. package/dist/ui/theme/utils.d.ts +0 -10
  119. package/dist/ui/theme/utils.js +5 -5
  120. package/dist/utils/background-utils.d.ts +4 -0
  121. package/dist/utils/background-utils.js +39 -0
  122. package/dist/utils/base-utils.d.ts +18 -0
  123. package/dist/utils/base-utils.js +124 -0
  124. package/dist/utils/constants.d.ts +2 -2
  125. package/dist/utils/constants.js +6 -1
  126. package/dist/utils/font-utils.d.ts +4 -0
  127. package/dist/utils/font-utils.js +47 -0
  128. package/dist/utils/style-utils.d.ts +7 -120
  129. package/dist/utils/style-utils.js +29 -304
  130. package/dist/utils/variable-utils.d.ts +1 -22
  131. package/dist/utils/variable-utils.js +28 -24
  132. package/dist/web-components/index.css +1 -1
  133. package/dist/web-components/index.js +1435 -980
  134. package/package.json +36 -26
  135. package/dist/components/button/button-utils.d.ts +0 -2
  136. package/dist/components/button/button-utils.js +0 -19
  137. package/dist/components/image/image-utils.d.ts +0 -19
  138. package/dist/components/image/image-utils.js +0 -33
  139. package/dist/components/purchase-button/purchase-button-utils.d.ts +0 -2
  140. package/dist/components/purchase-button/purchase-button-utils.js +0 -20
  141. package/dist/data/entities.d.ts +0 -162
  142. package/dist/stories/meta-templates.d.ts +0 -12
  143. package/dist/stories/meta-templates.js +0 -155
  144. /package/dist/{data/entities.js → types/base.js} +0 -0
@@ -1,36 +1,7 @@
1
- import { DEFAULT_COLOR_MODE, DEFAULT_TEXT_COLOR, } from "./constants.js";
2
- import { FontSizes, FontSizeTags, FontWeights, StackAlignment, StackDirection, StackDistribution, TextAlignments, } from "../types.js";
3
- /**
4
- * Generates CSS spacing styles for margin or padding
5
- * @param spacing - The spacing object containing top, trailing, bottom, and leading values
6
- * @param spacingKey - The type of spacing ('margin' or 'padding')
7
- * @returns CSS style object with logical properties for spacing
8
- */
9
- function getSpacingStyle(spacing, spacingKey) {
10
- const styles = {
11
- [`--${spacingKey}-block-start`]: "0px",
12
- [`--${spacingKey}-inline-end`]: "0px",
13
- [`--${spacingKey}-block-end`]: "0px",
14
- [`--${spacingKey}-inline-start`]: "0px",
15
- };
16
- if (!spacing || !spacingKey)
17
- return styles;
18
- Object.assign(styles, {
19
- [`--${spacingKey}-block-start`]: `${spacing.top ?? 0}px`,
20
- [`--${spacingKey}-inline-end`]: `${spacing.trailing ?? 0}px`,
21
- [`--${spacingKey}-block-end`]: `${spacing.bottom ?? 0}px`,
22
- [`--${spacingKey}-inline-start`]: `${spacing.leading ?? 0}px`,
23
- });
24
- return styles;
25
- }
26
- /**
27
- * Maps font size to appropriate HTML heading tag
28
- * @param fontSize - Key from FontSizeTags enum
29
- * @returns Corresponding HTML heading tag
30
- */
31
- export function getTextComponentTag(fontSize) {
32
- return FontSizeTags[fontSize];
33
- }
1
+ import { getPaywallContext } from "../stores/paywall.js";
2
+ import { DEFAULT_COLOR_MODE } from "./constants.js";
3
+ import { FontSizes, FontWeights, TextAlignments, } from "../types.js";
4
+ import { getScopedFontFamily, isFontRCFMManaged } from "./font-utils.js";
34
5
  /**
35
6
  * Gets color value based on color mode with fallback
36
7
  * @param params - Object containing color map, mode and fallback color
@@ -59,198 +30,6 @@ export function getColor({ colorMap, colorMode = DEFAULT_COLOR_MODE, fallback =
59
30
  return fallback;
60
31
  }
61
32
  }
62
- /**
63
- * Generates CSS border style string
64
- * @param border - Border configuration object
65
- * @param colorMode - Color mode (light/dark)
66
- * @returns CSS border style string
67
- */
68
- export function getBorderStyle(border, colorMode = DEFAULT_COLOR_MODE) {
69
- if (!border)
70
- return "";
71
- const color = getColor({ colorMap: border.color, colorMode });
72
- return `${border.width}px solid ${color}`;
73
- }
74
- /**
75
- * Generates CSS border radius style for corners
76
- * @param corners - Corner radius configuration
77
- * @returns CSS border radius string
78
- */
79
- export function getCornerRadiusStyle(corners) {
80
- return {
81
- "--border-start-start-radius": `${corners.top_leading}px`,
82
- "--border-start-end-radius": `${corners.top_trailing}px`,
83
- "--border-end-start-radius": `${corners.bottom_leading}px`,
84
- "--border-end-end-radius": `${corners.bottom_trailing}px`,
85
- };
86
- }
87
- /**
88
- * Generates comprehensive component styles including spacing, colors, borders and shadows
89
- * @param params - Component style configuration object
90
- * @returns CSS style object with component styles
91
- */
92
- export function getComponentStyles({ background_color, border, margin, padding, color, colorMode = DEFAULT_COLOR_MODE, shape, shadow, }) {
93
- const stylesObject = {
94
- "--margin-block-start": "0px",
95
- "--margin-inline-end": "0px",
96
- "--margin-block-end": "0px",
97
- "--margin-inline-start": "0px",
98
- "--padding-block-start": "0px",
99
- "--padding-inline-end": "0px",
100
- "--padding-block-end": "0px",
101
- "--padding-inline-start": "0px",
102
- "--background": "initial",
103
- "--text-color": "initial",
104
- "--border": "none",
105
- "--border-end-start-radius": "0px",
106
- "--border-end-end-radius": "0px",
107
- "--border-start-start-radius": "0px",
108
- "--border-start-end-radius": "0px",
109
- "--shadow": "none",
110
- };
111
- if (padding) {
112
- Object.assign(stylesObject, getSpacingStyle(padding, "padding"));
113
- }
114
- if (margin) {
115
- Object.assign(stylesObject, getSpacingStyle(margin, "margin"));
116
- }
117
- if (background_color) {
118
- stylesObject["--background"] = getColor({
119
- colorMap: background_color,
120
- colorMode,
121
- fallback: "transparent",
122
- });
123
- }
124
- if (color) {
125
- stylesObject["--text-color"] = getColor({
126
- colorMap: color,
127
- colorMode,
128
- fallback: DEFAULT_TEXT_COLOR,
129
- });
130
- }
131
- if (border) {
132
- stylesObject["--border"] = getBorderStyle(border, colorMode);
133
- }
134
- if (shape?.type === "rectangle" && shape.corners) {
135
- Object.assign(stylesObject, getCornerRadiusStyle(shape.corners));
136
- }
137
- if (shape?.type === "pill") {
138
- Object.assign(stylesObject, getCornerRadiusStyle({
139
- bottom_leading: 9999,
140
- bottom_trailing: 9999,
141
- top_leading: 9999,
142
- top_trailing: 9999,
143
- }));
144
- }
145
- if (shadow) {
146
- stylesObject["--shadow"] = `${shadow.x}px ${shadow.y}px ${shadow.radius}px
147
- ${getColor({ colorMap: shadow.color, colorMode, fallback: DEFAULT_TEXT_COLOR })}`;
148
- }
149
- return stylesObject;
150
- }
151
- function getSizeValue(size) {
152
- if (size.type === "fixed") {
153
- return `${size.value}px`;
154
- }
155
- if (size.type === "fit") {
156
- return "fit-content";
157
- }
158
- if (size.type === "fill") {
159
- const userAgent = navigator.userAgent;
160
- const isFirefox = userAgent.match(/firefox|fxios/i);
161
- return isFirefox ? "-moz-available" : "-webkit-fill-available";
162
- }
163
- return "initial";
164
- }
165
- /**
166
- * Generates size-related CSS styles for components
167
- * @param size - Size configuration object
168
- * @returns CSS style object with size properties
169
- */
170
- export function getSizeStyle(size) {
171
- const styles = {
172
- "--width": "initial",
173
- "--height": "initial",
174
- "--flex": "initial",
175
- };
176
- const width = getSizeValue(size.width);
177
- const height = getSizeValue(size.height);
178
- const isGrow = size.width.type === "fill" || size.height.type === "fill";
179
- Object.assign(styles, {
180
- "--width": width,
181
- "--height": height,
182
- "--flex": isGrow ? "initial" : "0 1 auto",
183
- });
184
- return styles;
185
- }
186
- export function getInsetStyles(dimension) {
187
- const defaultStyles = {
188
- "--inset": "initial",
189
- "--transform": "initial",
190
- };
191
- switch (dimension.alignment) {
192
- case "top_leading":
193
- defaultStyles["--inset"] = "0 auto auto 0";
194
- break;
195
- case "top":
196
- defaultStyles["--inset"] = "auto auto auto 50%";
197
- defaultStyles["--transform"] = "translate(-50%, 0)";
198
- break;
199
- case "top_trailing":
200
- defaultStyles["--inset"] = "0 0 auto auto";
201
- break;
202
- case "leading":
203
- defaultStyles["--inset"] = "50% 0 50% 0";
204
- defaultStyles["--transform"] = "translate(0, -50%)";
205
- break;
206
- case "center":
207
- defaultStyles["--inset"] = "50% auto auto 50%";
208
- defaultStyles["--transform"] = "translate(-50%, -50%)";
209
- break;
210
- case "trailing":
211
- defaultStyles["--inset"] = "50% 0 50% auto";
212
- defaultStyles["--transform"] = "translate(0, -50%)";
213
- break;
214
- case "bottom_leading":
215
- defaultStyles["--inset"] = "auto auto 0 0";
216
- break;
217
- case "bottom":
218
- defaultStyles["--inset"] = "auto 50% 0 auto";
219
- defaultStyles["--transform"] = "translate(50%, 0)";
220
- break;
221
- case "bottom_trailing":
222
- defaultStyles["--inset"] = "auto 0 0 auto";
223
- break;
224
- }
225
- return defaultStyles;
226
- }
227
- /**
228
- * Generates dimension-related styles for stack components
229
- * @param dimension - Dimension configuration object
230
- * @returns CSS style object with flex layout properties
231
- */
232
- export function getDimensionStyle(dimension) {
233
- const styles = {
234
- "--direction": "initial",
235
- "--alignment": "initial",
236
- "--distribution": "initial",
237
- "--position": "relative",
238
- "--inset": "initial",
239
- "--transform": "initial",
240
- };
241
- if (dimension.type !== "zlayer") {
242
- Object.assign(styles, {
243
- "--direction": StackDirection[dimension.type],
244
- "--alignment": StackAlignment[dimension.alignment],
245
- });
246
- if (dimension.distribution) {
247
- Object.assign(styles, {
248
- "--distribution": StackDistribution[dimension.distribution],
249
- });
250
- }
251
- }
252
- return styles;
253
- }
254
33
  /**
255
34
  * Generates text-related styles
256
35
  * @param props - Text component properties
@@ -258,7 +37,12 @@ export function getDimensionStyle(dimension) {
258
37
  * @returns CSS style object with text formatting properties
259
38
  */
260
39
  export function getTextStyles(props) {
261
- const { font_size, horizontal_alignment, font_weight, font_name } = props;
40
+ const { font_size, horizontal_alignment, font_weight, font_name, font_weight_int, } = props;
41
+ const paywall = getPaywallContext();
42
+ const uiConfig = paywall.uiConfig;
43
+ const { fonts } = uiConfig.app;
44
+ const font = fonts[font_name ?? ""];
45
+ const fontFamily = font?.web?.family;
262
46
  const styles = {
263
47
  "--text-align": "initial",
264
48
  "--font-weight": "initial",
@@ -270,9 +54,13 @@ export function getTextStyles(props) {
270
54
  };
271
55
  Object.assign(styles, {
272
56
  "--text-align": TextAlignments[horizontal_alignment] || TextAlignments.leading,
273
- "--font-weight": FontWeights[font_weight] || FontWeights.regular,
274
- "--font-size": FontSizes[font_size] || FontSizes.body_m,
275
- "--font-family": font_name || "sans-serif",
57
+ "--font-weight": font_weight_int ?? FontWeights[font_weight] ?? FontWeights.regular,
58
+ "--font-size": Number.isInteger(Number(font_size))
59
+ ? `${font_size}px`
60
+ : FontSizes[font_size] || FontSizes.body_m,
61
+ "--font-family": isFontRCFMManaged(font_name ?? "")
62
+ ? getScopedFontFamily(fontFamily ?? "")
63
+ : "sans-serif",
276
64
  });
277
65
  return styles;
278
66
  }
@@ -312,36 +100,25 @@ export function findSelectedPackageId(paywallData) {
312
100
  }
313
101
  return undefined;
314
102
  };
315
- for (const component of paywallData.components_config.base.stack.components) {
316
- const pkg = traverseNode(component);
103
+ const { stack, sticky_footer } = paywallData.components_config.base;
104
+ const pkg = traverseNode(stack);
105
+ if (pkg !== undefined) {
106
+ return pkg?.package_id;
107
+ }
108
+ if (sticky_footer != null) {
109
+ const pkg = traverseNode(sticky_footer);
317
110
  if (pkg !== undefined) {
318
111
  return pkg?.package_id;
319
112
  }
320
113
  }
321
114
  return undefined;
322
115
  }
323
- export const getActiveStateProps = (overrides, componentState) => {
324
- if (!componentState)
116
+ export const getActiveStateProps = (selectedState, overrides) => {
117
+ if (!selectedState) {
325
118
  return {};
326
- const activeStateKeys = getComponentActiveStateKeys(componentState);
327
- const activeStateProps = activeStateKeys.reduce((props, key) => {
328
- if (overrides) {
329
- const styles = overrides?.states?.[key] || {};
330
- return { ...props, ...styles };
331
- }
332
- return props;
333
- }, {});
334
- return activeStateProps;
335
- };
336
- const getComponentActiveStateKeys = (componentState) => {
337
- if (!componentState)
338
- return [];
339
- const stateKeys = Object.entries(componentState).reduce((activeStates, [stateKey, stateValue]) => {
340
- if (stateValue)
341
- activeStates.push(stateKey);
342
- return activeStates;
343
- }, []);
344
- return stateKeys;
119
+ }
120
+ const override = overrides?.find((override) => override.conditions.find((condition) => condition.type === "selected"));
121
+ return override?.properties ?? {};
345
122
  };
346
123
  export function prefixObject(object, prefix) {
347
124
  if (!object)
@@ -354,55 +131,3 @@ export function prefixObject(object, prefix) {
354
131
  return acc;
355
132
  }, {});
356
133
  }
357
- export function getMaskPath(props) {
358
- const { mask_shape: maskShape, imageAspectRatio } = props;
359
- let maskPath = "";
360
- if (maskShape?.type === "concave") {
361
- maskPath = `M 0 0
362
- H 100
363
- V ${imageAspectRatio * 100}
364
- Q 50 ${imageAspectRatio * 80} 0 ${imageAspectRatio * 100}
365
- Z`;
366
- }
367
- else if (maskShape?.type === "convex") {
368
- maskPath = `M 0 0
369
- H 100
370
- V ${imageAspectRatio * 80}
371
- Q 50 ${imageAspectRatio * 120} 0 ${imageAspectRatio * 80}
372
- Z`;
373
- }
374
- else {
375
- maskPath = `M 0 0 H 100 V ${imageAspectRatio * 100} H 0 Z`;
376
- }
377
- return maskPath;
378
- }
379
- /**
380
- * Generates mask styles for images
381
- * @param maskShape - Shape configuration for image mask
382
- * @returns CSS style object with mask properties
383
- */
384
- export const getMaskStyle = (maskShape) => {
385
- const maskStyles = {
386
- "--border-end-start-radius": "0px",
387
- "--border-end-end-radius": "0px",
388
- "--border-start-start-radius": "0px",
389
- "--border-start-end-radius": "0px",
390
- };
391
- if (maskShape?.corners) {
392
- Object.assign(maskStyles, getCornerRadiusStyle(maskShape.corners));
393
- }
394
- return maskStyles;
395
- };
396
- export function getLinearGradientAngle(colorMode, props) {
397
- const { color_overlay: colorOverlay } = props;
398
- if (colorOverlay?.[colorMode]?.type !== "linear") {
399
- return { x1: "0%", y1: "0%", x2: "0%", y2: "0%" };
400
- }
401
- const defaultColor = colorOverlay?.[DEFAULT_COLOR_MODE];
402
- const angle = defaultColor?.type === "linear" ? defaultColor.degrees : 0;
403
- const x1 = "50%";
404
- const y1 = "0%";
405
- const x2 = `${Math.round(50 + Math.sin(((angle + 90) * Math.PI) / 90) * 50)}%`;
406
- const y2 = `${Math.round(50 - Math.cos(((angle + 90) * Math.PI) / 90) * 50)}%`;
407
- return { x1, y1, x2, y2 };
408
- }
@@ -1,23 +1,2 @@
1
- export declare const VARIABLE_NAMES: string[];
2
- export type VariableDictionary = {
3
- product_name: string | number | undefined;
4
- price: string | number | undefined;
5
- price_per_period: string | number | undefined;
6
- price_per_period_full: string | number | undefined;
7
- total_price_and_per_month: string | number | undefined;
8
- total_price_and_per_month_full: string | number | undefined;
9
- sub_price_per_month: string | number | undefined;
10
- sub_price_per_week: string | number | undefined;
11
- sub_duration: string | number | undefined;
12
- sub_duration_in_months: string | number | undefined;
13
- sub_period: string | number | undefined;
14
- sub_period_length: string | number | undefined;
15
- sub_period_abbreviated: string | number | undefined;
16
- sub_offer_duration: string | number | undefined;
17
- sub_offer_duration_2: string | number | undefined;
18
- sub_offer_price: string | number | undefined;
19
- sub_offer_price_2: string | number | undefined;
20
- sub_relative_discount: string | number | undefined;
21
- [key: string]: string | number | undefined;
22
- };
1
+ import type { VariableDictionary } from "../types/variables";
23
2
  export declare function replaceVariables(input?: string, variables?: VariableDictionary): string | undefined;
@@ -1,29 +1,33 @@
1
- export const VARIABLE_NAMES = [
2
- "product_name",
3
- "price",
4
- "price_per_period",
5
- "price_per_period_full",
6
- "total_price_and_per_month",
7
- "total_price_and_per_month_full",
8
- "sub_price_per_month",
9
- "sub_price_per_week",
10
- "sub_duration",
11
- "sub_duration_in_months",
12
- "sub_period",
13
- "sub_period_length",
14
- "sub_period_abbreviated",
15
- "sub_offer_duration",
16
- "sub_offer_duration_2",
17
- "sub_offer_price",
18
- "sub_offer_price_2",
19
- "sub_relative_discount",
20
- ];
1
+ function capitalize(value) {
2
+ return value.charAt(0).toUpperCase() + value.slice(1).toLowerCase();
3
+ }
4
+ const VARIABLE_MODIFIERS = {
5
+ uppercase: (value) => String(value ?? "").toUpperCase(),
6
+ lowercase: (value) => String(value ?? "").toLowerCase(),
7
+ capitalize: (value) => {
8
+ return String(value ?? "").replaceAll(/(\p{Letter}+)/giu, capitalize);
9
+ },
10
+ };
11
+ function parseTemplate(template, variables) {
12
+ // Match {{ product.variable | helper1 | helper2:arg1,arg2 }} patterns
13
+ const templateRegex = /\{\{\s*([a-z0-9._-]+)(?:\s*\|\s*([^}]*))?\s*\}\}/gi;
14
+ return template.replace(templateRegex, (match, namePart, modifierPart) => {
15
+ const variableName = namePart?.trim();
16
+ const variable = variableName && variables[variableName];
17
+ if (variable === undefined) {
18
+ return "N/A";
19
+ }
20
+ const modifierName = modifierPart?.trim();
21
+ if (modifierName === undefined) {
22
+ return variable;
23
+ }
24
+ const modifier = VARIABLE_MODIFIERS[modifierName];
25
+ return modifier ? modifier(variable) : variable;
26
+ });
27
+ }
21
28
  export function replaceVariables(input = "", variables) {
22
29
  if (variables === undefined) {
23
30
  return input;
24
31
  }
25
- return VARIABLE_NAMES.reduce((result, variableName) => {
26
- const currentVariableReplaced = result.replaceAll(`{{ ${variableName} }}`, variables[variableName]?.toString() || "N/A");
27
- return currentVariableReplaced;
28
- }, input);
32
+ return parseTemplate(input, variables);
29
33
  }
@@ -1 +1 @@
1
- .rcb-typography-heading-2xl.svelte-byrjto{font:var(--rc-text-heading2xl-mobile)}.rcb-typography-heading-xl.svelte-byrjto{font:var(--rc-text-headingXl-mobile)}.rcb-typography-heading-lg.svelte-byrjto{font:var(--rc-text-headingLg-mobile)}.rcb-typography-heading-md.svelte-byrjto{font:var(--rc-text-headingMd-mobile)}.rcb-typography-body-base.svelte-byrjto{font:var(--rc-text-bodyBase-mobile)}.rcb-typography-body-small.svelte-byrjto{font:var(--rc-text-bodySmall-mobile)}.rcb-typography-label-button.svelte-byrjto{font:var(--rc-text-labelButton-mobile)}.rcb-typography-label-default.svelte-byrjto{font:var(--rc-text-labelDefault-mobile)}.rcb-typography-caption-default.svelte-byrjto{font:var(--rc-text-captionDefault-mobile)}.rcb-typography-caption-link.svelte-byrjto{font:var(--rc-text-captionLink-mobile)}@container layout-query-container (width >= 768px){.rcb-typography-heading-2xl.svelte-byrjto{font:var(--rc-text-heading2xl-desktop)}.rcb-typography-heading-xl.svelte-byrjto{font:var(--rc-text-headingXl-desktop)}.rcb-typography-heading-lg.svelte-byrjto{font:var(--rc-text-headingLg-desktop)}.rcb-typography-heading-md.svelte-byrjto{font:var(--rc-text-headingMd-desktop)}.rcb-typography-body-base.svelte-byrjto{font:var(--rc-text-bodyBase-desktop)}.rcb-typography-body-small.svelte-byrjto{font:var(--rc-text-bodySmall-desktop)}.rcb-typography-label-button.svelte-byrjto{font:var(--rc-text-labelButton-desktop)}.rcb-typography-label-default.svelte-byrjto{font:var(--rc-text-labelDefault-desktop)}.rcb-typography-caption-default.svelte-byrjto{font:var(--rc-text-captionDefault-desktop)}.rcb-typography-caption-link.svelte-byrjto{font:var(--rc-text-captionLink-desktop)}}.rcb-processing.svelte-2j2b59{width:var(--width, 12px);aspect-ratio:1;border-radius:50%;animation:svelte-2j2b59-l5 1.5s infinite linear}@keyframes svelte-2j2b59-l5{0%{box-shadow:var(--shadow-offset) 0 #fff2,calc(-1 * var(--shadow-offset)) 0 #fff2;background:#fff2}25%{box-shadow:var(--shadow-offset) 0 #fff2,calc(-1 * var(--shadow-offset)) 0 #fff;background:#fff2}50%{box-shadow:var(--shadow-offset) 0 #fff2,calc(-1 * var(--shadow-offset)) 0 #fff2;background:#fff}75%{box-shadow:var(--shadow-offset) 0 #fff,calc(-1 * var(--shadow-offset)) 0 #fff2;background:#fff2}to{box-shadow:var(--shadow-offset) 0 #fff2,calc(-1 * var(--shadow-offset)) 0 #fff2;background:#fff2}}button.svelte-td7xvm{border:none;border-radius:var(--rc-shape-input-button-border-radius);cursor:pointer;height:var(--rc-spacing-inputHeight-mobile);color:var(--rc-color-grey-text-dark);background-color:var(--rc-color-grey-ui-dark);display:flex;align-items:center;justify-content:center;-webkit-tap-highlight-color:rgba(0,0,0,0);-webkit-tap-highlight-color:transparent;transition:background-color .15s ease-in-out;-webkit-user-select:none;user-select:none}@container layout-query-container (width >= 768px){button.svelte-td7xvm{height:var(--rc-spacing-inputHeight-desktop)}}button.svelte-td7xvm:focus-visible{outline:2px solid var(--rc-color-focus)}button.intent-primary.svelte-td7xvm{background-color:var(--rc-color-primary);color:var(--rc-color-primary-text)}button.svelte-td7xvm:disabled{color:var(--rc-color-grey-text-light);background-color:var(--rc-color-grey-ui-dark);outline:none}button.intent-primary.svelte-td7xvm:not(:disabled):hover{background-color:var(--rc-color-primary-hover)}button.intent-primary.svelte-td7xvm:not(:disabled):active,button.svelte-td7xvm:active{background-color:var(--rc-color-primary-pressed);outline:none}button.intent-primary.svelte-td7xvm:disabled{color:var(--rc-color-grey-text-light);background-color:var(--rc-color-grey-ui-dark)}
1
+ .rcb-typography-heading-2xl.svelte-jt3g5k{font:var(--rc-text-heading2xl-mobile)}.rcb-typography-heading-xl.svelte-jt3g5k{font:var(--rc-text-headingXl-mobile)}.rcb-typography-heading-lg.svelte-jt3g5k{font:var(--rc-text-headingLg-mobile)}.rcb-typography-heading-md.svelte-jt3g5k{font:var(--rc-text-headingMd-mobile)}.rcb-typography-body-base.svelte-jt3g5k{font:var(--rc-text-bodyBase-mobile)}.rcb-typography-body-small.svelte-jt3g5k{font:var(--rc-text-bodySmall-mobile)}.rcb-typography-label-button.svelte-jt3g5k{font:var(--rc-text-labelButton-mobile)}.rcb-typography-label-default.svelte-jt3g5k{font:var(--rc-text-labelDefault-mobile)}.rcb-typography-caption-default.svelte-jt3g5k{font:var(--rc-text-captionDefault-mobile)}.rcb-typography-caption-link.svelte-jt3g5k{font:var(--rc-text-captionLink-mobile)}@container layout-query-container (width >= 768px){.rcb-typography-heading-2xl.svelte-jt3g5k{font:var(--rc-text-heading2xl-desktop)}.rcb-typography-heading-xl.svelte-jt3g5k{font:var(--rc-text-headingXl-desktop)}.rcb-typography-heading-lg.svelte-jt3g5k{font:var(--rc-text-headingLg-desktop)}.rcb-typography-heading-md.svelte-jt3g5k{font:var(--rc-text-headingMd-desktop)}.rcb-typography-body-base.svelte-jt3g5k{font:var(--rc-text-bodyBase-desktop)}.rcb-typography-body-small.svelte-jt3g5k{font:var(--rc-text-bodySmall-desktop)}.rcb-typography-label-button.svelte-jt3g5k{font:var(--rc-text-labelButton-desktop)}.rcb-typography-label-default.svelte-jt3g5k{font:var(--rc-text-labelDefault-desktop)}.rcb-typography-caption-default.svelte-jt3g5k{font:var(--rc-text-captionDefault-desktop)}.rcb-typography-caption-link.svelte-jt3g5k{font:var(--rc-text-captionLink-desktop)}}.rcb-processing.svelte-1i80tfc{width:var(--width, 12px);aspect-ratio:1;border-radius:50%;animation:svelte-1i80tfc-l5 1.5s infinite linear}@keyframes svelte-1i80tfc-l5{0%{box-shadow:var(--shadow-offset) 0 #fff2,calc(-1 * var(--shadow-offset)) 0 #fff2;background:#fff2}25%{box-shadow:var(--shadow-offset) 0 #fff2,calc(-1 * var(--shadow-offset)) 0 #fff;background:#fff2}50%{box-shadow:var(--shadow-offset) 0 #fff2,calc(-1 * var(--shadow-offset)) 0 #fff2;background:#fff}75%{box-shadow:var(--shadow-offset) 0 #fff,calc(-1 * var(--shadow-offset)) 0 #fff2;background:#fff2}to{box-shadow:var(--shadow-offset) 0 #fff2,calc(-1 * var(--shadow-offset)) 0 #fff2;background:#fff2}}button.svelte-hnboq0{border:none;border-radius:var(--rc-shape-input-button-border-radius);cursor:pointer;height:var(--rc-spacing-inputHeight-mobile);color:var(--rc-color-grey-text-dark);background-color:var(--rc-color-grey-ui-dark);display:flex;align-items:center;justify-content:center;-webkit-tap-highlight-color:rgba(0,0,0,0);-webkit-tap-highlight-color:transparent;transition:background-color .15s ease-in-out;-webkit-user-select:none;user-select:none}@container layout-query-container (width >= 768px){button.svelte-hnboq0{height:var(--rc-spacing-inputHeight-desktop)}}button.svelte-hnboq0:focus-visible{outline:2px solid var(--rc-color-focus)}button.intent-primary.svelte-hnboq0{background-color:var(--rc-color-primary);color:var(--rc-color-primary-text)}button.svelte-hnboq0:disabled{color:var(--rc-color-grey-text-light);background-color:var(--rc-color-grey-ui-dark);outline:none}button.intent-primary.svelte-hnboq0:not(:disabled):hover{background-color:var(--rc-color-primary-hover)}button.intent-primary.svelte-hnboq0:not(:disabled):active,button.svelte-hnboq0:active{background-color:var(--rc-color-primary-pressed);outline:none}button.intent-primary.svelte-hnboq0:disabled{color:var(--rc-color-grey-text-light);background-color:var(--rc-color-grey-ui-dark)}