@revenuecat/purchases-ui-js 3.11.5 → 3.12.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.
@@ -10,7 +10,7 @@
10
10
  const props: InputOptionProps = $props();
11
11
  const { stack, option_id } = props;
12
12
 
13
- const { onButtonAction } = getPaywallContext();
13
+ const { onButtonAction, onInputChanged } = getPaywallContext();
14
14
  const inputChoiceContext = getInputChoiceContext();
15
15
 
16
16
  // Derive selected state from context (similar to Package)
@@ -41,6 +41,15 @@
41
41
  // Update selection state
42
42
  inputChoiceContext?.selectOption(option_id);
43
43
 
44
+ // Notify host (e.g. workflows) so selections can be persisted when every option shares
45
+ // the same workflow `on_press` id (e.g. `btn_*`); `onActionTriggered` alone is ambiguous.
46
+ // The trailing actionId lets hosts skip this callback when their own action path
47
+ // already persists the selection (e.g. a legacy input_option_* action handler).
48
+ const fieldId = inputChoiceContext?.fieldId;
49
+ if (fieldId) {
50
+ onInputChanged?.(fieldId, option_id, actionId);
51
+ }
52
+
44
53
  // Trigger navigation
45
54
  onButtonAction({ type: "workflow" }, actionId);
46
55
  };
@@ -0,0 +1,47 @@
1
+ <script lang="ts">
2
+ import { setPaywallContext } from "../../stores/paywall";
3
+ import {
4
+ createInputChoiceContext,
5
+ setInputChoiceContext,
6
+ } from "../../stores/inputChoice";
7
+ import { readable, writable } from "svelte/store";
8
+ import InputOption from "./InputOption.svelte";
9
+ import type { InputOptionProps } from "../../types/components/options";
10
+
11
+ interface Props extends InputOptionProps {
12
+ fieldId?: string;
13
+ selectionMode?: "single" | "multiple";
14
+ onButtonAction?: (action: { type: string }, actionId?: string) => void;
15
+ onInputChanged?: (
16
+ fieldId: string,
17
+ value: string,
18
+ actionId?: string,
19
+ ) => void;
20
+ }
21
+
22
+ const {
23
+ fieldId,
24
+ selectionMode = "single",
25
+ onButtonAction = () => {},
26
+ onInputChanged,
27
+ ...optionProps
28
+ }: Props = $props();
29
+
30
+ if (fieldId !== undefined) {
31
+ setInputChoiceContext(createInputChoiceContext(fieldId, selectionMode));
32
+ }
33
+
34
+ setPaywallContext({
35
+ selectedPackageId: writable(undefined),
36
+ variablesPerPackage: readable(undefined),
37
+ baseVariables: readable(undefined),
38
+ infoPerPackage: readable(undefined),
39
+ onPurchase: () => {},
40
+ onButtonAction: onButtonAction as never,
41
+ onInputChanged,
42
+ uiConfig: {} as never,
43
+ hideBackButtons: false,
44
+ });
45
+ </script>
46
+
47
+ <InputOption {...optionProps} />
@@ -0,0 +1,12 @@
1
+ import type { InputOptionProps } from "../../types/components/options";
2
+ interface Props extends InputOptionProps {
3
+ fieldId?: string;
4
+ selectionMode?: "single" | "multiple";
5
+ onButtonAction?: (action: {
6
+ type: string;
7
+ }, actionId?: string) => void;
8
+ onInputChanged?: (fieldId: string, value: string, actionId?: string) => void;
9
+ }
10
+ declare const InputOptionTestWrapper: import("svelte").Component<Props, {}, "">;
11
+ type InputOptionTestWrapper = ReturnType<typeof InputOptionTestWrapper>;
12
+ export default InputOptionTestWrapper;
@@ -82,7 +82,11 @@
82
82
  onError?: (error: unknown) => void;
83
83
  hideBackButtons?: boolean;
84
84
  walletButtonRender?: WalletButtonRender;
85
- onInputChanged?: (fieldId: string, value: string) => void;
85
+ onInputChanged?: (
86
+ fieldId: string,
87
+ value: string,
88
+ actionId?: string,
89
+ ) => void;
86
90
  maxContentWidth?: string;
87
91
  initialInputSelections?: InitialInputSelections;
88
92
  /**
@@ -39,7 +39,7 @@ interface Props {
39
39
  onError?: (error: unknown) => void;
40
40
  hideBackButtons?: boolean;
41
41
  walletButtonRender?: WalletButtonRender;
42
- onInputChanged?: (fieldId: string, value: string) => void;
42
+ onInputChanged?: (fieldId: string, value: string, actionId?: string) => void;
43
43
  maxContentWidth?: string;
44
44
  initialInputSelections?: InitialInputSelections;
45
45
  /**
@@ -5,6 +5,7 @@
5
5
  import { localizations } from "../../stories/fixtures";
6
6
  import { localizationDecorator } from "../../stories/localization-decorator";
7
7
  import type { StackProps } from "../../types/components/stack";
8
+ import type { Component } from "../../types/component";
8
9
  import { DEFAULT_TEXT_COLOR } from "../../utils/constants";
9
10
  import { defineMeta } from "@storybook/addon-svelte-csf";
10
11
 
@@ -58,6 +59,53 @@
58
59
  },
59
60
  ] as unknown as TextNodeProps[];
60
61
 
62
+ /** Z-layer: full-bleed image (layer 0), text label (layer 1). */
63
+ const zlayerImageUnderText: Component[] = [
64
+ {
65
+ type: "image",
66
+ id: "zlayer-bg",
67
+ name: "Background image",
68
+ size: { width: { type: "fill" }, height: { type: "fill" } },
69
+ fit_mode: "fill",
70
+ source: {
71
+ light: {
72
+ width: 600,
73
+ height: 400,
74
+ original:
75
+ "https://placehold.co/600x400/1e293b/94a3b8?text=Layer+0+%28back%29",
76
+ heic: "https://placehold.co/600x400",
77
+ heic_low_res: "https://placehold.co/600x400",
78
+ webp: "https://placehold.co/600x400",
79
+ webp_low_res: "https://placehold.co/600x400",
80
+ },
81
+ dark: null,
82
+ },
83
+ margin: { top: 0, bottom: 0, leading: 0, trailing: 0 },
84
+ padding: { top: 0, bottom: 0, leading: 0, trailing: 0 },
85
+ },
86
+ {
87
+ type: "text",
88
+ id: "zlayer-top",
89
+ name: "Top label",
90
+ text_lid: "id1",
91
+ color: {
92
+ light: { type: "hex", value: "#f8fafc" },
93
+ dark: { type: "hex", value: "#f8fafc" },
94
+ },
95
+ font_name: null,
96
+ font_size: "heading_m",
97
+ font_weight: "semibold",
98
+ horizontal_alignment: "center",
99
+ background_color: {
100
+ light: { type: "hex", value: "#0f172acc" },
101
+ dark: { type: "hex", value: "#0f172acc" },
102
+ },
103
+ margin: { top: 0, bottom: 0, leading: 0, trailing: 0 },
104
+ padding: { top: 12, bottom: 12, leading: 16, trailing: 16 },
105
+ size: { width: { type: "fit" }, height: { type: "fit" } },
106
+ },
107
+ ];
108
+
61
109
  const { Story } = defineMeta({
62
110
  title: "Components/Stack",
63
111
  component: Stack,
@@ -140,6 +188,46 @@
140
188
  }}
141
189
  />
142
190
 
191
+ <Story
192
+ name="Z layer — image under text"
193
+ args={{
194
+ components: zlayerImageUnderText,
195
+ size: {
196
+ width: { type: "fixed", value: 320 },
197
+ height: { type: "fixed", value: 220 },
198
+ },
199
+ dimension: {
200
+ type: "zlayer",
201
+ alignment: "center",
202
+ },
203
+ background: {
204
+ type: "color",
205
+ value: {
206
+ light: { type: "hex", value: "#0f172a" },
207
+ },
208
+ },
209
+ border: {
210
+ width: 1,
211
+ color: {
212
+ light: { type: "hex", value: "#334155" },
213
+ dark: { type: "hex", value: "#334155" },
214
+ },
215
+ },
216
+ shape: {
217
+ type: "rectangle",
218
+ corners: {
219
+ top_leading: 12,
220
+ top_trailing: 12,
221
+ bottom_leading: 12,
222
+ bottom_trailing: 12,
223
+ },
224
+ },
225
+ }}
226
+ parameters={{
227
+ chromatic: { disable: true },
228
+ }}
229
+ />
230
+
143
231
  <Story
144
232
  name="Space Around"
145
233
  args={{
@@ -114,7 +114,10 @@
114
114
  {/if}
115
115
 
116
116
  {#if dimension.type === "zlayer"}
117
- <div style="position: relative; width: 100%; height: 100%;">
117
+ <!-- isolate: layer z-index values only sort within this stack, not the host page -->
118
+ <div
119
+ style="position: relative; width: 100%; height: 100%; isolation: isolate;"
120
+ >
118
121
  {#each components as component, index}
119
122
  <div style={getLayerStyle(index)}>
120
123
  <Node nodeData={component} />
@@ -23,7 +23,11 @@
23
23
  maxContentWidth?: string;
24
24
  variablesPerPackage?: Record<string, VariableDictionary>;
25
25
  initialInputSelections?: InitialInputSelections;
26
- onInputChanged?: (fieldId: string, value: string) => void;
26
+ onInputChanged?: (
27
+ fieldId: string,
28
+ value: string,
29
+ actionId?: string,
30
+ ) => void;
27
31
  onCompleteWorkflowNavigate?: (
28
32
  args: CompleteWorkflowNavigateArgs,
29
33
  ) => void | Promise<void>;
@@ -18,7 +18,7 @@ interface Props {
18
18
  maxContentWidth?: string;
19
19
  variablesPerPackage?: Record<string, VariableDictionary>;
20
20
  initialInputSelections?: InitialInputSelections;
21
- onInputChanged?: (fieldId: string, value: string) => void;
21
+ onInputChanged?: (fieldId: string, value: string, actionId?: string) => void;
22
22
  onCompleteWorkflowNavigate?: (args: CompleteWorkflowNavigateArgs) => void | Promise<void>;
23
23
  walletButtonRender?: WalletButtonRender;
24
24
  }
@@ -20,7 +20,7 @@ type PaywallContext = Readonly<{
20
20
  onPurchase: (actionId?: string) => void;
21
21
  emitComponentInteraction: (data: ComponentInteractionData) => void;
22
22
  onNavigateToUrl?: (url: string) => void;
23
- onInputChanged?: (fieldId: string, value: string) => void;
23
+ onInputChanged?: (fieldId: string, value: string, actionId?: string) => void;
24
24
  walletButtonRender?: WalletButtonRender;
25
25
  onWalletButtonReady?: (walletButtonAvailable?: boolean) => void;
26
26
  onButtonAction: (action: Action, actionId?: string) => void;
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": "3.11.5",
5
+ "version": "3.12.0",
6
6
  "author": {
7
7
  "name": "RevenueCat, Inc."
8
8
  },