@revenuecat/purchases-ui-js 2.0.5 → 2.1.0

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 (39) hide show
  1. package/dist/components/button/ButtonNode.stories.svelte +20 -0
  2. package/dist/components/button/ButtonNode.svelte +12 -4
  3. package/dist/components/paywall/Node.svelte +4 -0
  4. package/dist/components/paywall/Paywall.svelte +11 -5
  5. package/dist/components/paywall/Paywall.svelte.d.ts +1 -0
  6. package/dist/components/stack/Stack.svelte +6 -3
  7. package/dist/components/stack/Stack.svelte.d.ts +2 -0
  8. package/dist/components/tabs/TabControlToggle.svelte +103 -0
  9. package/dist/components/tabs/TabControlToggle.svelte.d.ts +4 -0
  10. package/dist/components/tabs/Tabs.stories.svelte +439 -0
  11. package/dist/components/tabs/Tabs.svelte +12 -5
  12. package/dist/components/tabs/tabs-context.d.ts +1 -0
  13. package/dist/components/text/TextNode.stories.svelte +106 -0
  14. package/dist/components/text/TextNode.svelte +5 -3
  15. package/dist/components/text/text-utils.d.ts +5 -5
  16. package/dist/components/text/text-utils.js +34 -19
  17. package/dist/components/timeline/TimelineItem.svelte +4 -8
  18. package/dist/components/video/Video.stories.svelte +267 -0
  19. package/dist/components/video/Video.stories.svelte.d.ts +19 -0
  20. package/dist/components/video/Video.svelte +248 -0
  21. package/dist/components/video/Video.svelte.d.ts +4 -0
  22. package/dist/index.d.ts +1 -0
  23. package/dist/index.js +1 -0
  24. package/dist/stores/paywall.d.ts +1 -1
  25. package/dist/stories/paywall-decorator.js +1 -1
  26. package/dist/types/base.d.ts +1 -0
  27. package/dist/types/component.d.ts +3 -2
  28. package/dist/types/components/button.d.ts +7 -1
  29. package/dist/types/components/tabs.d.ts +10 -0
  30. package/dist/types/components/text.d.ts +2 -2
  31. package/dist/types/components/video.d.ts +35 -0
  32. package/dist/types/components/video.js +1 -0
  33. package/dist/types/media.d.ts +17 -4
  34. package/dist/types/ui-config.d.ts +1 -1
  35. package/dist/utils/base-utils.d.ts +2 -1
  36. package/dist/utils/base-utils.js +1 -1
  37. package/dist/utils/style-utils.d.ts +0 -30
  38. package/dist/utils/style-utils.js +0 -66
  39. package/package.json +1 -1
@@ -1,38 +1,53 @@
1
+ import { FontSizes, FontWeights, TextAlignments, } from "../../types";
1
2
  import { mapBackground } from "../../utils/background-utils";
2
- import { css, mapColor, mapSize, mapSpacing } from "../../utils/base-utils";
3
+ import { css, mapColorInfo, mapColorMode, mapSize, mapSpacing, } from "../../utils/base-utils";
3
4
  import { DEFAULT_TEXT_COLOR } from "../../utils/constants";
4
- import { getTextStyles } from "../../utils/style-utils";
5
+ import { getScopedFontFamily, isFontRCFMManaged } from "../../utils/font-utils";
5
6
  export const defaultColor = {
6
7
  light: { type: "hex", value: DEFAULT_TEXT_COLOR },
7
8
  };
9
+ export function mapTextColor(colorMode, scheme) {
10
+ const info = mapColorMode(colorMode, scheme);
11
+ const color = mapColorInfo(info);
12
+ switch (info.type) {
13
+ case "alias":
14
+ case "hex":
15
+ return { color };
16
+ case "linear":
17
+ case "radial":
18
+ return {
19
+ color: "transparent",
20
+ background: color,
21
+ "-webkit-background-clip": "text",
22
+ "background-clip": "text",
23
+ };
24
+ }
25
+ }
8
26
  /**
9
27
  * Generates comprehensive styles for text components by combining text, component and size styles
10
28
  * @param props - Text component properties including font, color, background, spacing etc.
11
29
  * @returns Object containing text inline styles and the appropriate HTML tag to render
12
30
  */
13
- export const getTextComponentStyles = (colorMode, props) => {
14
- const { color = defaultColor, background_color, padding, margin, size, } = props;
15
- const textStyles = getTextStyles(props);
16
- const style = css({
31
+ export const getTextComponentStyles = (colorMode, props, fonts) => {
32
+ const { font_name, font_size, font_weight, font_weight_int, horizontal_alignment, color = defaultColor, padding, margin, size, } = props;
33
+ const font = fonts[font_name ?? ""];
34
+ const fontFamily = font?.web?.family;
35
+ return css({
17
36
  display: "block",
18
37
  width: mapSize(size.width),
19
38
  height: mapSize(size.height),
20
39
  margin: mapSpacing(margin),
21
40
  padding: mapSpacing(padding),
22
- ...mapBackground(colorMode, background_color, null),
23
- color: mapColor(colorMode, color),
24
- "text-align": textStyles["--text-align"] || "initial",
25
- "font-weight": textStyles["--font-weight"] || "initial",
26
- "font-size": textStyles["--font-size"] || "initial",
27
- "font-family": textStyles["--font-family"] || "sans-serif",
28
- "-webkit-background-clip": textStyles["--background-clip"] || "initial",
29
- "background-clip": textStyles["--background-clip"] || "initial",
30
- "-webkit-text-fill-color": textStyles["--text-fill-color"] || "initial",
41
+ ...mapTextColor(colorMode, color),
42
+ "text-align": TextAlignments[horizontal_alignment] || TextAlignments.leading,
43
+ "font-weight": font_weight_int ?? FontWeights[font_weight] ?? FontWeights.regular,
44
+ "font-size": Number.isInteger(Number(font_size))
45
+ ? `${font_size}px`
46
+ : FontSizes[font_size] || FontSizes.body_m,
47
+ "font-family": isFontRCFMManaged(font_name ?? "")
48
+ ? getScopedFontFamily(fontFamily ?? "")
49
+ : "sans-serif",
31
50
  });
32
- return {
33
- textStyles: style,
34
- tagToRender: "span",
35
- };
36
51
  };
37
52
  export function getTextWrapperInlineStyles(colorMode, _restProps, size, background_color) {
38
53
  return css({
@@ -1,11 +1,11 @@
1
1
  <script lang="ts">
2
2
  import { getTimelineItemContentInlineStyles } from "./timeline-utils";
3
- import type { TimelineItemProps } from "../../types/components/timeline";
4
3
  import { getColorModeContext } from "../../stores/color-mode";
5
- import { getColor, getActiveStateProps } from "../../utils/style-utils";
6
4
  import { getSelectedStateContext } from "../../stores/selected";
5
+ import type { TimelineItemProps } from "../../types/components/timeline";
6
+ import { css, mapColor } from "../../utils/base-utils";
7
+ import { getActiveStateProps } from "../../utils/style-utils";
7
8
  import Icon from "../icon/Icon.svelte";
8
- import { css } from "../../utils/base-utils";
9
9
  import TextNode from "../text/TextNode.svelte";
10
10
 
11
11
  interface Props extends TimelineItemProps {
@@ -64,11 +64,7 @@
64
64
  const connectorBarStyles = $derived.by(() => {
65
65
  if (!connector) return "";
66
66
 
67
- const background = getColor({
68
- colorMap: connector.color,
69
- colorMode,
70
- fallback: "#000000",
71
- });
67
+ const background = mapColor(colorMode, connector.color);
72
68
 
73
69
  return css({
74
70
  width: "100%",
@@ -0,0 +1,267 @@
1
+ <script module lang="ts">
2
+ import Video from "./Video.svelte";
3
+ import { componentDecorator } from "../../stories/component-decorator";
4
+ import { defineMeta } from "@storybook/addon-svelte-csf";
5
+
6
+ const { Story } = defineMeta({
7
+ title: "Components/Video",
8
+ component: Video,
9
+ decorators: [componentDecorator()],
10
+ parameters: {
11
+ chromatic: { disableSnapshot: true },
12
+ },
13
+ args: {
14
+ type: "video",
15
+ id: "video",
16
+ name: "Video",
17
+ fit_mode: "fill",
18
+ auto_play: true,
19
+ loop: true,
20
+ mute_audio: true,
21
+ show_controls: true,
22
+ size: {
23
+ width: { type: "fill" },
24
+ height: { type: "fill" },
25
+ },
26
+ source: {
27
+ dark: null,
28
+ light: {
29
+ width: 1920,
30
+ height: 1080,
31
+ url: "https://videos.pexels.com/video-files/10288594/10288594-hd_1920_1080_25fps.mp4",
32
+ url_low_res:
33
+ "https://videos.pexels.com/video-files/10288594/10288594-hd_1920_1080_25fps.mp4",
34
+ },
35
+ },
36
+ },
37
+ });
38
+ </script>
39
+
40
+ <Story
41
+ name="Rectangle"
42
+ args={{
43
+ mask_shape: {
44
+ type: "rectangle",
45
+ corners: {
46
+ top_leading: 0,
47
+ top_trailing: 0,
48
+ bottom_leading: 0,
49
+ bottom_trailing: 0,
50
+ },
51
+ },
52
+ }}
53
+ />
54
+
55
+ <Story
56
+ name="Rounded Rectangle"
57
+ args={{
58
+ mask_shape: {
59
+ type: "rectangle",
60
+ corners: {
61
+ top_leading: 32,
62
+ top_trailing: 32,
63
+ bottom_leading: 32,
64
+ bottom_trailing: 32,
65
+ },
66
+ },
67
+ }}
68
+ />
69
+
70
+ <Story
71
+ name="Circle"
72
+ args={{
73
+ mask_shape: { type: "circle" },
74
+ }}
75
+ />
76
+
77
+ <Story
78
+ name="Concave"
79
+ args={{
80
+ mask_shape: { type: "concave" },
81
+ }}
82
+ />
83
+
84
+ <Story
85
+ name="Convex"
86
+ args={{
87
+ mask_shape: { type: "convex" },
88
+ }}
89
+ />
90
+
91
+ <Story
92
+ name="With Controls"
93
+ args={{
94
+ show_controls: true,
95
+ auto_play: false,
96
+ mute_audio: false,
97
+ }}
98
+ />
99
+
100
+ <Story
101
+ name="Autoplay Muted"
102
+ args={{
103
+ show_controls: false,
104
+ auto_play: true,
105
+ mute_audio: true,
106
+ loop: true,
107
+ }}
108
+ />
109
+
110
+ <Story
111
+ name="With Fallback Image"
112
+ args={{
113
+ source: null,
114
+ fallback_source: {
115
+ dark: null,
116
+ light: {
117
+ width: 1920,
118
+ height: 1080,
119
+ original:
120
+ "https://placehold.co/1920x1080/blue/white?text=Video+Not+Available",
121
+ heic: "https://placehold.co/1920x1080/blue/white?text=Video+Not+Available",
122
+ heic_low_res:
123
+ "https://placehold.co/1920x1080/blue/white?text=Video+Not+Available",
124
+ webp: "https://placehold.co/1920x1080/blue/white?text=Video+Not+Available",
125
+ webp_low_res:
126
+ "https://placehold.co/1920x1080/blue/white?text=Video+Not+Available",
127
+ },
128
+ },
129
+ }}
130
+ />
131
+
132
+ <Story
133
+ name="Overlay Radial Gradient"
134
+ args={{
135
+ color_overlay: {
136
+ light: {
137
+ type: "radial",
138
+ points: [
139
+ {
140
+ percent: 0,
141
+ color: "#020024ff",
142
+ },
143
+ {
144
+ percent: 100,
145
+ color: "#00d4ff00",
146
+ },
147
+ ],
148
+ },
149
+ },
150
+ }}
151
+ />
152
+
153
+ <Story
154
+ name="Overlay Linear Gradient"
155
+ args={{
156
+ color_overlay: {
157
+ light: {
158
+ degrees: 180,
159
+ points: [
160
+ {
161
+ percent: 0,
162
+ color: "#020024ff",
163
+ },
164
+ {
165
+ percent: 100,
166
+ color: "#00d4ff00",
167
+ },
168
+ ],
169
+ type: "linear",
170
+ },
171
+ },
172
+ }}
173
+ />
174
+
175
+ <Story
176
+ name="Overlay Solid"
177
+ args={{
178
+ color_overlay: {
179
+ light: {
180
+ type: "hex",
181
+ value: "#e7c00069",
182
+ },
183
+ },
184
+ }}
185
+ />
186
+
187
+ <Story
188
+ name="Complex"
189
+ args={{
190
+ size: {
191
+ width: { type: "fixed", value: 400 },
192
+ height: { type: "fixed", value: 300 },
193
+ },
194
+ mask_shape: {
195
+ type: "rectangle",
196
+ corners: {
197
+ top_leading: 0,
198
+ top_trailing: 16,
199
+ bottom_trailing: 32,
200
+ bottom_leading: 64,
201
+ },
202
+ },
203
+ fit_mode: "fill",
204
+ padding: { top: 0, bottom: 0, leading: 0, trailing: 0 },
205
+ margin: { top: 0, bottom: 0, leading: 0, trailing: 0 },
206
+ color_overlay: {
207
+ light: {
208
+ type: "linear",
209
+ degrees: 180,
210
+ points: [
211
+ { percent: 0, color: "#ff000080" },
212
+ { percent: 100, color: "#ff00ff00" },
213
+ ],
214
+ },
215
+ },
216
+ border: {
217
+ width: 3,
218
+ color: {
219
+ light: { type: "hex", value: "#00ff0080" },
220
+ },
221
+ },
222
+ shadow: {
223
+ x: 0,
224
+ y: 0,
225
+ radius: 16,
226
+ color: {
227
+ light: { type: "hex", value: "#0000ff80" },
228
+ },
229
+ },
230
+ show_controls: true,
231
+ auto_play: false,
232
+ mute_audio: false,
233
+ loop: false,
234
+ }}
235
+ />
236
+
237
+ <Story
238
+ name="Fixed Size"
239
+ args={{
240
+ size: {
241
+ width: { type: "fixed", value: 320 },
242
+ height: { type: "fixed", value: 240 },
243
+ },
244
+ }}
245
+ />
246
+
247
+ <Story
248
+ name="Fit Mode - Contain"
249
+ args={{
250
+ fit_mode: "fit",
251
+ size: {
252
+ width: { type: "fixed", value: 400 },
253
+ height: { type: "fixed", value: 300 },
254
+ },
255
+ }}
256
+ />
257
+
258
+ <Story
259
+ name="Fit Mode - Cover"
260
+ args={{
261
+ fit_mode: "fill",
262
+ size: {
263
+ width: { type: "fixed", value: 400 },
264
+ height: { type: "fixed", value: 300 },
265
+ },
266
+ }}
267
+ />
@@ -0,0 +1,19 @@
1
+ import Video from "./Video.svelte";
2
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
3
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
4
+ $$bindings?: Bindings;
5
+ } & Exports;
6
+ (internal: unknown, props: {
7
+ $$events?: Events;
8
+ $$slots?: Slots;
9
+ }): Exports & {
10
+ $set?: any;
11
+ $on?: any;
12
+ };
13
+ z_$$bindings?: Bindings;
14
+ }
15
+ declare const Video: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
16
+ [evt: string]: CustomEvent<any>;
17
+ }, {}, {}, string>;
18
+ type Video = InstanceType<typeof Video>;
19
+ export default Video;
@@ -0,0 +1,248 @@
1
+ <script lang="ts">
2
+ import { getColorModeContext } from "../../stores/color-mode";
3
+ import { getSelectedStateContext } from "../../stores/selected";
4
+ import type { VideoProps } from "../../types/components/video";
5
+ import type { VideoFiles, ImageFiles } from "../../types/media";
6
+ import {
7
+ css,
8
+ mapColor,
9
+ mapColorMode,
10
+ mapFitMode,
11
+ mapSize,
12
+ mapSpacing,
13
+ } from "../../utils/base-utils";
14
+ import { getActiveStateProps } from "../../utils/style-utils";
15
+ import ClipPath from "../image/ClipPath.svelte";
16
+ import Overlay from "../image/Overlay.svelte";
17
+
18
+ const props: VideoProps = $props();
19
+
20
+ const selectedState = getSelectedStateContext();
21
+ const {
22
+ id,
23
+ source,
24
+ fallback_source,
25
+ size,
26
+ mask_shape,
27
+ fit_mode,
28
+ padding,
29
+ margin,
30
+ color_overlay,
31
+ border,
32
+ shadow,
33
+ auto_play,
34
+ loop,
35
+ mute_audio,
36
+ show_controls,
37
+ } = $derived.by(() => {
38
+ return {
39
+ ...props,
40
+ ...getActiveStateProps($selectedState, props.overrides),
41
+ };
42
+ });
43
+
44
+ const getColorMode = getColorModeContext();
45
+ const colorMode = $derived(getColorMode());
46
+ const video: VideoFiles | null = $derived(
47
+ source ? (mapColorMode(colorMode, source as any) as VideoFiles) : null,
48
+ );
49
+ const fallbackImage: ImageFiles | null = $derived(
50
+ fallback_source
51
+ ? (mapColorMode(colorMode, fallback_source as any) as ImageFiles)
52
+ : null,
53
+ );
54
+
55
+ let wrapperWidth = $state(0);
56
+ let videoSize = $state({
57
+ height: 0,
58
+ width: 0,
59
+ });
60
+ let videoElement = $state<HTMLVideoElement | null>(null);
61
+ let hasVideoError = $state(false);
62
+
63
+ // Load video or fallback image metadata to get dimensions
64
+ $effect(() => {
65
+ // If we have a video, load its dimensions
66
+ if (video) {
67
+ if (video.width && video.height) {
68
+ videoSize = {
69
+ height: video.height,
70
+ width: video.width,
71
+ };
72
+ return;
73
+ }
74
+
75
+ // If dimensions aren't provided, try to load them from the video element
76
+ if (videoElement) {
77
+ const onLoadedMetadata = () => {
78
+ videoSize = {
79
+ height: videoElement?.videoHeight ?? 0,
80
+ width: videoElement?.videoWidth ?? 0,
81
+ };
82
+ };
83
+
84
+ const onError = () => {
85
+ hasVideoError = true;
86
+ };
87
+
88
+ videoElement.addEventListener("loadedmetadata", onLoadedMetadata);
89
+ videoElement.addEventListener("error", onError);
90
+
91
+ return () => {
92
+ videoElement?.removeEventListener("loadedmetadata", onLoadedMetadata);
93
+ videoElement?.removeEventListener("error", onError);
94
+ };
95
+ }
96
+ }
97
+ // If we don't have a video but have a fallback image, use its dimensions
98
+ else if (fallbackImage) {
99
+ if (fallbackImage.width && fallbackImage.height) {
100
+ videoSize = {
101
+ height: fallbackImage.height,
102
+ width: fallbackImage.width,
103
+ };
104
+ return;
105
+ }
106
+
107
+ // If dimensions aren't provided, load them from the image
108
+ const img = new window.Image();
109
+ img.src = fallbackImage.original;
110
+
111
+ const onImageLoad = () => {
112
+ videoSize = {
113
+ height: img.height,
114
+ width: img.width,
115
+ };
116
+ };
117
+
118
+ img.addEventListener("load", onImageLoad);
119
+ return () => {
120
+ img.removeEventListener("load", onImageLoad);
121
+ };
122
+ }
123
+ });
124
+
125
+ const height = $derived.by(() => {
126
+ if (size.height.type === "fixed") {
127
+ return size.height.value;
128
+ }
129
+ return Math.round(wrapperWidth * (videoSize.height / videoSize.width));
130
+ });
131
+
132
+ const style = $derived(
133
+ css({
134
+ width: mapSize(size.width),
135
+ height: `${height}px`,
136
+ margin: mapSpacing(margin),
137
+ padding: mapSpacing(padding),
138
+ "line-height": 0,
139
+ "flex-shrink": size.width.type === "fixed" ? 0 : 1,
140
+ }),
141
+ );
142
+
143
+ let svgRect = $state<DOMRect | null>(null);
144
+
145
+ const [svgWidth, svgHeight] = $derived.by(() => {
146
+ return [svgRect?.width ?? 0, svgRect?.height ?? 0];
147
+ });
148
+
149
+ const viewBox = $derived.by(() => {
150
+ return `0 0 ${svgWidth} ${svgHeight}`;
151
+ });
152
+
153
+ const svgStyle = $derived.by(() => {
154
+ if (!shadow?.color) {
155
+ return "";
156
+ }
157
+
158
+ const { x, y, radius, color } = shadow;
159
+ const shadowColor = mapColor(colorMode, color);
160
+ return `filter: drop-shadow(${x}px ${y}px ${radius}px ${shadowColor})`;
161
+ });
162
+
163
+ const overlay = $derived(
164
+ color_overlay && mapColorMode(colorMode, color_overlay),
165
+ );
166
+
167
+ const shouldShowFallback = $derived(hasVideoError || !video);
168
+ </script>
169
+
170
+ <div {style} bind:clientWidth={wrapperWidth}>
171
+ <svg
172
+ bind:contentRect={svgRect}
173
+ width="100%"
174
+ height="100%"
175
+ {viewBox}
176
+ style={svgStyle}
177
+ >
178
+ <defs>
179
+ <clipPath id={`${id}-path`}>
180
+ <ClipPath shape={mask_shape} width={svgWidth} height={svgHeight} />
181
+ </clipPath>
182
+
183
+ <g id={`${id}-border`}>
184
+ <ClipPath shape={mask_shape} width={svgWidth} height={svgHeight} />
185
+ </g>
186
+
187
+ <Overlay id={`${id}-overlay`} {overlay} />
188
+ </defs>
189
+
190
+ {#if border && border.width > 0}
191
+ <use href={`#${id}-border`} fill="transparent" />
192
+ {/if}
193
+
194
+ <g clip-path={`url(#${id}-path)`}>
195
+ <foreignObject x="0" y="0" width="100%" height="100%">
196
+ {#if shouldShowFallback && fallbackImage}
197
+ <img
198
+ src={fallbackImage.original}
199
+ style="object-fit:{mapFitMode(fit_mode)}"
200
+ alt=""
201
+ />
202
+ {:else if video}
203
+ <video
204
+ bind:this={videoElement}
205
+ src={video.url}
206
+ style="object-fit:{mapFitMode(fit_mode)}"
207
+ autoplay={auto_play}
208
+ {loop}
209
+ muted={mute_audio}
210
+ controls={show_controls}
211
+ playsinline
212
+ >
213
+ <track kind="captions" />
214
+ </video>
215
+ {/if}
216
+ </foreignObject>
217
+
218
+ {#if overlay}
219
+ <rect
220
+ x="0"
221
+ y="0"
222
+ height="100%"
223
+ width="100%"
224
+ fill={`url(#${id}-overlay)`}
225
+ />
226
+ {/if}
227
+
228
+ {#if border && border.width > 0}
229
+ <use
230
+ href={`#${id}-border`}
231
+ fill="none"
232
+ stroke={mapColor(colorMode, border.color)}
233
+ stroke-width={border.width}
234
+ />
235
+ {/if}
236
+ </g>
237
+ </svg>
238
+ </div>
239
+
240
+ <style>
241
+ video,
242
+ img {
243
+ width: 100%;
244
+ height: 100%;
245
+ object-fit: contain;
246
+ object-position: center;
247
+ }
248
+ </style>
@@ -0,0 +1,4 @@
1
+ import type { VideoProps } from "../../types/components/video";
2
+ declare const Video: import("svelte").Component<VideoProps, {}, "">;
3
+ type Video = ReturnType<typeof Video>;
4
+ export default Video;
package/dist/index.d.ts CHANGED
@@ -7,6 +7,7 @@ export { default as PurchaseButton } from "./components/purchase-button/Purchase
7
7
  export { default as Stack } from "./components/stack/Stack.svelte";
8
8
  export { default as Text } from "./components/text/Text.svelte";
9
9
  export { default as Timeline } from "./components/timeline/Timeline.svelte";
10
+ export { default as Video } from "./components/video/Video.svelte";
10
11
  export * from "./types";
11
12
  export { type PaywallData } from "./types/paywall";
12
13
  export { type UIConfig } from "./types/ui-config";
package/dist/index.js CHANGED
@@ -8,6 +8,7 @@ export { default as PurchaseButton } from "./components/purchase-button/Purchase
8
8
  export { default as Stack } from "./components/stack/Stack.svelte";
9
9
  export { default as Text } from "./components/text/Text.svelte";
10
10
  export { default as Timeline } from "./components/timeline/Timeline.svelte";
11
+ export { default as Video } from "./components/video/Video.svelte";
11
12
  export * from "./types";
12
13
  export {} from "./types/paywall";
13
14
  export {} from "./types/ui-config";
@@ -6,7 +6,7 @@ type PaywallContext = Readonly<{
6
6
  selectedPackageId: Writable<string | undefined>;
7
7
  variablesPerPackage: Readable<Record<string, VariableDictionary> | undefined>;
8
8
  onPurchase: () => void;
9
- onButtonAction: (action: Action) => void;
9
+ onButtonAction: (action: Action, actionId?: string) => void;
10
10
  uiConfig: UIConfig;
11
11
  }>;
12
12
  export declare function setPaywallContext(context: PaywallContext): void;
@@ -17,7 +17,7 @@ export function paywallDecorator() {
17
17
  selectedPackageId,
18
18
  variablesPerPackage: readable(undefined),
19
19
  onPurchase: () => window.alert("Purchase clicked"),
20
- onButtonAction: (action) => window.alert(`Button clicked: ${JSON.stringify(action, undefined, 2)}`),
20
+ onButtonAction: (action, actionId) => window.alert(`Button clicked: ${JSON.stringify({ action, actionId }, undefined, 2)}`),
21
21
  uiConfig: emptyUiConfig,
22
22
  });
23
23
  return Story();
@@ -4,4 +4,5 @@ export interface BaseComponent {
4
4
  id: string;
5
5
  name: string;
6
6
  fallback?: Component;
7
+ triggers?: Record<string, string>;
7
8
  }