@revenuecat/purchases-ui-js 2.0.2 → 2.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/dist/components/button/ButtonNode.stories.svelte +66 -102
  2. package/dist/components/button/ButtonNode.svelte +2 -27
  3. package/dist/components/footer/Footer.stories.svelte +112 -102
  4. package/dist/components/footer/Footer.svelte +8 -4
  5. package/dist/components/icon/Icon.stories.svelte +100 -0
  6. package/dist/components/icon/Icon.stories.svelte.d.ts +19 -0
  7. package/dist/components/icon/Icon.svelte +73 -0
  8. package/dist/components/icon/Icon.svelte.d.ts +4 -0
  9. package/dist/components/image/ClipPath.svelte +49 -0
  10. package/dist/components/image/ClipPath.svelte.d.ts +9 -0
  11. package/dist/components/image/Image.stories.svelte +83 -188
  12. package/dist/components/image/Image.svelte +152 -136
  13. package/dist/components/image/Image.svelte.d.ts +1 -1
  14. package/dist/components/image/Overlay.svelte +36 -0
  15. package/dist/components/image/Overlay.svelte.d.ts +8 -0
  16. package/dist/components/package/Package.stories.svelte +10 -21
  17. package/dist/components/package/Package.svelte +8 -35
  18. package/dist/components/paywall/Node.svelte +27 -28
  19. package/dist/components/paywall/Node.svelte.d.ts +3 -6
  20. package/dist/components/paywall/Paywall.stories.svelte +36 -140
  21. package/dist/components/paywall/Paywall.svelte +22 -6
  22. package/dist/components/paywall/Paywall.svelte.d.ts +3 -2
  23. package/dist/components/paywall/fixtures/override-paywall.d.ts +2 -0
  24. package/dist/components/paywall/fixtures/override-paywall.js +1310 -0
  25. package/dist/components/paywall/fixtures/stack-paywall.d.ts +2 -0
  26. package/dist/components/paywall/fixtures/stack-paywall.js +5223 -0
  27. package/dist/components/paywall/fixtures/variables.d.ts +261 -0
  28. package/dist/components/paywall/fixtures/variables.js +262 -0
  29. package/dist/components/purchase-button/PurchaseButton.stories.svelte +10 -21
  30. package/dist/components/purchase-button/PurchaseButton.svelte +2 -27
  31. package/dist/components/stack/Stack.stories.svelte +2354 -978
  32. package/dist/components/stack/Stack.svelte +111 -134
  33. package/dist/components/stack/Stack.svelte.d.ts +6 -2
  34. package/dist/components/stack/stack-utils.d.ts +10 -30
  35. package/dist/components/stack/stack-utils.js +77 -255
  36. package/dist/components/text/Text.svelte +3 -37
  37. package/dist/components/text/Text.svelte.d.ts +1 -2
  38. package/dist/components/text/TextNode.stories.svelte +10 -36
  39. package/dist/components/text/TextNode.svelte +25 -28
  40. package/dist/components/text/TextNode.svelte.d.ts +1 -1
  41. package/dist/components/text/text-utils.d.ts +4 -9
  42. package/dist/components/text/text-utils.js +32 -117
  43. package/dist/components/timeline/Timeline.stories.svelte +640 -251
  44. package/dist/components/timeline/Timeline.svelte +42 -28
  45. package/dist/components/timeline/Timeline.svelte.d.ts +1 -1
  46. package/dist/components/timeline/TimelineItem.svelte +80 -112
  47. package/dist/components/timeline/TimelineItem.svelte.d.ts +6 -2
  48. package/dist/components/timeline/timeline-utils.d.ts +24 -6
  49. package/dist/components/timeline/timeline-utils.js +21 -113
  50. package/dist/data/entities.d.ts +19 -135
  51. package/dist/index.d.ts +2 -1
  52. package/dist/index.js +2 -1
  53. package/dist/stores/color-mode.d.ts +1 -1
  54. package/dist/stores/paywall.d.ts +5 -2
  55. package/dist/stores/selected.d.ts +5 -0
  56. package/dist/stores/selected.js +12 -0
  57. package/dist/stores/variables.d.ts +1 -1
  58. package/dist/stores/variables.js +0 -1
  59. package/dist/stories/component-decorator.d.ts +2 -0
  60. package/dist/stories/component-decorator.js +12 -0
  61. package/dist/stories/fixtures.d.ts +5 -3
  62. package/dist/stories/fixtures.js +5214 -4422
  63. package/dist/stories/paywall-decorator.js +6 -0
  64. package/dist/stories/variables-decorator.d.ts +1 -1
  65. package/dist/stories/viewport-decorator.d.ts +2 -0
  66. package/dist/stories/viewport-decorator.js +8 -0
  67. package/dist/stories/viewport-wrapper.svelte +53 -0
  68. package/dist/stories/viewport-wrapper.svelte.d.ts +10 -0
  69. package/dist/stories/with-layout.d.ts +2 -10
  70. package/dist/stories/with-layout.js +3 -5
  71. package/dist/types/alignment.d.ts +5 -3
  72. package/dist/types/background.d.ts +6 -5
  73. package/dist/types/base.d.ts +7 -0
  74. package/dist/types/base.js +1 -0
  75. package/dist/types/colors.d.ts +4 -4
  76. package/dist/types/component.d.ts +6 -2
  77. package/dist/types/components/button.d.ts +4 -1
  78. package/dist/types/components/footer.d.ts +2 -1
  79. package/dist/types/components/icon.d.ts +28 -0
  80. package/dist/types/components/icon.js +1 -0
  81. package/dist/types/components/image.d.ts +20 -0
  82. package/dist/types/components/image.js +1 -0
  83. package/dist/types/components/package.d.ts +2 -1
  84. package/dist/types/components/purchase-button.d.ts +2 -1
  85. package/dist/types/components/stack.d.ts +32 -0
  86. package/dist/types/components/stack.js +1 -0
  87. package/dist/types/components/text.d.ts +20 -0
  88. package/dist/types/components/text.js +1 -0
  89. package/dist/types/components/timeline.d.ts +35 -0
  90. package/dist/types/components/timeline.js +1 -0
  91. package/dist/types/localization.d.ts +2 -1
  92. package/dist/types/media.d.ts +4 -3
  93. package/dist/types/overrides.d.ts +48 -0
  94. package/dist/types/overrides.js +1 -0
  95. package/dist/types/variables.d.ts +13 -0
  96. package/dist/types/variables.js +10 -0
  97. package/dist/types.d.ts +17 -9
  98. package/dist/ui/atoms/typography.stories.svelte +1 -27
  99. package/dist/ui/molecules/button.stories.svelte +3 -8
  100. package/dist/ui/theme/colors.d.ts +0 -6
  101. package/dist/ui/theme/colors.js +1 -1
  102. package/dist/ui/theme/text.d.ts +3 -4
  103. package/dist/ui/theme/utils.d.ts +0 -10
  104. package/dist/ui/theme/utils.js +5 -5
  105. package/dist/utils/background-utils.d.ts +4 -0
  106. package/dist/utils/background-utils.js +39 -0
  107. package/dist/utils/base-utils.d.ts +18 -0
  108. package/dist/utils/base-utils.js +124 -0
  109. package/dist/utils/constants.d.ts +2 -2
  110. package/dist/utils/constants.js +6 -1
  111. package/dist/utils/font-utils.d.ts +4 -0
  112. package/dist/utils/font-utils.js +47 -0
  113. package/dist/utils/style-utils.d.ts +7 -120
  114. package/dist/utils/style-utils.js +22 -302
  115. package/dist/utils/variable-utils.d.ts +1 -22
  116. package/dist/utils/variable-utils.js +28 -24
  117. package/dist/web-components/index.css +1 -1
  118. package/dist/web-components/index.js +1323 -895
  119. package/package.json +34 -24
  120. package/dist/components/button/button-utils.d.ts +0 -2
  121. package/dist/components/button/button-utils.js +0 -19
  122. package/dist/components/image/image-utils.d.ts +0 -19
  123. package/dist/components/image/image-utils.js +0 -33
  124. package/dist/components/purchase-button/purchase-button-utils.d.ts +0 -2
  125. package/dist/components/purchase-button/purchase-button-utils.js +0 -20
  126. package/dist/stories/meta-templates.d.ts +0 -12
  127. package/dist/stories/meta-templates.js +0 -155
@@ -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
  }
@@ -320,28 +108,12 @@ export function findSelectedPackageId(paywallData) {
320
108
  }
321
109
  return undefined;
322
110
  }
323
- export const getActiveStateProps = (overrides, componentState) => {
324
- if (!componentState)
111
+ export const getActiveStateProps = (selectedState, overrides) => {
112
+ if (!selectedState) {
325
113
  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;
114
+ }
115
+ const override = overrides?.find((override) => override.conditions.find((condition) => condition.type === "selected"));
116
+ return override?.properties ?? {};
345
117
  };
346
118
  export function prefixObject(object, prefix) {
347
119
  if (!object)
@@ -354,55 +126,3 @@ export function prefixObject(object, prefix) {
354
126
  return acc;
355
127
  }, {});
356
128
  }
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)}