@revenuecat/purchases-ui-js 4.8.0 → 4.8.2

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.
@@ -364,7 +364,7 @@
364
364
  title: "Components/Carousel",
365
365
  component: Carousel,
366
366
  decorators: [
367
- viewportDecorator(400, 300, 0),
367
+ viewportDecorator(350, 300, 0),
368
368
  componentDecorator(),
369
369
  localizationDecorator({
370
370
  defaultLocale,
@@ -355,6 +355,7 @@
355
355
  display: flex;
356
356
  flex-direction: column;
357
357
  flex-shrink: 0;
358
+ align-self: stretch;
358
359
  }
359
360
 
360
361
  .carousel-clip {
@@ -401,6 +402,8 @@
401
402
  transition-delay: 2500ms;
402
403
  transform: translateX(0);
403
404
 
405
+ touch-action: pan-y;
406
+
404
407
  cursor: grab;
405
408
 
406
409
  &:active {
@@ -445,10 +445,11 @@
445
445
  box-sizing: border-box;
446
446
  margin: 0;
447
447
  padding: 0;
448
- font-family: sans-serif;
448
+ font-family: inherit;
449
449
  }
450
450
 
451
451
  .paywall {
452
+ font-family: sans-serif;
452
453
  line-height: 1.5;
453
454
  -webkit-font-smoothing: antialiased;
454
455
  }
@@ -8,7 +8,10 @@
8
8
  import type { WalletButtonRender } from "../../types/wallet";
9
9
  import type { UIConfig } from "../../types/ui-config";
10
10
  import type { ReservedAttribute } from "../../types/components/input-text";
11
- import type { WorkflowNavData } from "../../types/workflow-nav";
11
+ import type {
12
+ WorkflowNavData,
13
+ WorkflowStepChangeEvent,
14
+ } from "../../types/workflow-nav";
12
15
  import type {
13
16
  WorkflowStepTriggerAction,
14
17
  WorkflowStep,
@@ -59,6 +62,12 @@
59
62
  onComponentInteraction?: OnComponentInteraction;
60
63
  onClose?: () => void;
61
64
  onExitBack?: () => void;
65
+ /**
66
+ * Called whenever the current workflow step changes — on initial load,
67
+ * forward navigation, back navigation, and when leaving a step (close,
68
+ * purchase, error). Used by the host to fire workflow lifecycle events.
69
+ */
70
+ onStepChanged?: (event: WorkflowStepChangeEvent) => void;
62
71
  }
63
72
 
64
73
  const {
@@ -83,6 +92,7 @@
83
92
  onComponentInteraction,
84
93
  onClose,
85
94
  onExitBack,
95
+ onStepChanged,
86
96
  }: Props = $props();
87
97
 
88
98
  // ── Nav stack ─────────────────────────────────────────────────────────────
@@ -118,9 +128,39 @@
118
128
  stepId: workflow.initial_step_id,
119
129
  },
120
130
  ];
131
+ emitStepChange({
132
+ workflowId: workflow.workflowId,
133
+ stepId: workflow.initial_step_id,
134
+ fromStepId: undefined,
135
+ toStepId: undefined,
136
+ reason: "start",
137
+ ...stepFlags(workflow.initial_step_id),
138
+ });
121
139
  }
122
140
  });
123
141
 
142
+ function stepFlags(stepId: string): {
143
+ isFirstStep: boolean;
144
+ isLastStep: boolean;
145
+ } {
146
+ const step = workflow.steps[stepId];
147
+ return {
148
+ isFirstStep: stepId === workflow.initial_step_id,
149
+ // A step is "last" when it has no step-type trigger actions — i.e. no
150
+ // client-resolvable forward navigation. Condition-based actions fall back
151
+ // to onActionTriggered and are not counted here.
152
+ isLastStep:
153
+ step !== undefined &&
154
+ !Object.values(step.trigger_actions).some((a) =>
155
+ isStepTriggerAction(a),
156
+ ),
157
+ };
158
+ }
159
+
160
+ function emitStepChange(event: WorkflowStepChangeEvent) {
161
+ onStepChanged?.(event);
162
+ }
163
+
124
164
  /**
125
165
  * Resolves a "workflow"-type button action and navigates to the next screen.
126
166
  *
@@ -191,17 +231,58 @@
191
231
  return;
192
232
  }
193
233
 
194
- navStack = [
195
- ...navStack,
196
- { pageId: nextPageId, stepId: triggerAction.step_id },
197
- ];
234
+ const fromStepId = current.stepId;
235
+ const toStepId = triggerAction.step_id;
236
+ emitStepChange({
237
+ workflowId: workflow.workflowId,
238
+ stepId: fromStepId,
239
+ fromStepId: undefined,
240
+ toStepId,
241
+ reason: "completed",
242
+ ...stepFlags(fromStepId),
243
+ });
244
+ navStack = [...navStack, { pageId: nextPageId, stepId: toStepId }];
245
+ emitStepChange({
246
+ workflowId: workflow.workflowId,
247
+ stepId: toStepId,
248
+ fromStepId,
249
+ toStepId: undefined,
250
+ reason: "forward",
251
+ ...stepFlags(toStepId),
252
+ });
198
253
  }
199
254
 
200
255
  function handleBackClicked() {
201
256
  if (navStack.length > 1) {
257
+ const fromStepId = current.stepId;
258
+ const toStepId = navStack[navStack.length - 2].stepId;
259
+ emitStepChange({
260
+ workflowId: workflow.workflowId,
261
+ stepId: fromStepId,
262
+ fromStepId: undefined,
263
+ toStepId,
264
+ reason: "completed",
265
+ ...stepFlags(fromStepId),
266
+ });
202
267
  navStack = navStack.slice(0, -1);
268
+ emitStepChange({
269
+ workflowId: workflow.workflowId,
270
+ stepId: toStepId,
271
+ fromStepId,
272
+ toStepId: undefined,
273
+ reason: "back",
274
+ ...stepFlags(toStepId),
275
+ });
203
276
  } else {
204
277
  // Already at root — bubble up to the host.
278
+ emitStepChange({
279
+ workflowId: workflow.workflowId,
280
+ stepId: current.stepId,
281
+ fromStepId: undefined,
282
+ toStepId: undefined,
283
+ reason: "completed",
284
+ ...stepFlags(current.stepId),
285
+ });
205
286
  onExitBack?.();
206
287
  }
207
288
  }
@@ -229,6 +310,16 @@
229
310
  {onVisitCustomerCenterClicked}
230
311
  {onComponentInteraction}
231
312
  onBackClicked={handleBackClicked}
232
- {onClose}
313
+ onClose={() => {
314
+ emitStepChange({
315
+ workflowId: workflow.workflowId,
316
+ stepId: current.stepId,
317
+ fromStepId: undefined,
318
+ toStepId: undefined,
319
+ reason: "completed",
320
+ ...stepFlags(current.stepId),
321
+ });
322
+ onClose?.();
323
+ }}
233
324
  />
234
325
  {/key}
@@ -6,7 +6,7 @@ import type { VariableDictionary } from "../../types/variables";
6
6
  import type { WalletButtonRender } from "../../types/wallet";
7
7
  import type { UIConfig } from "../../types/ui-config";
8
8
  import type { ReservedAttribute } from "../../types/components/input-text";
9
- import type { WorkflowNavData } from "../../types/workflow-nav";
9
+ import type { WorkflowNavData, WorkflowStepChangeEvent } from "../../types/workflow-nav";
10
10
  interface Props {
11
11
  workflow: WorkflowNavData;
12
12
  uiConfig: UIConfig;
@@ -29,6 +29,12 @@ interface Props {
29
29
  onComponentInteraction?: OnComponentInteraction;
30
30
  onClose?: () => void;
31
31
  onExitBack?: () => void;
32
+ /**
33
+ * Called whenever the current workflow step changes — on initial load,
34
+ * forward navigation, back navigation, and when leaving a step (close,
35
+ * purchase, error). Used by the host to fire workflow lifecycle events.
36
+ */
37
+ onStepChanged?: (event: WorkflowStepChangeEvent) => void;
32
38
  }
33
39
  declare const Workflow: import("svelte").Component<Props, {}, "">;
34
40
  type Workflow = ReturnType<typeof Workflow>;
@@ -7,6 +7,7 @@
7
7
  * "rcpaywall_details" — details screen with "← Back" and "✕ Close" buttons
8
8
  */
9
9
  export const TWO_PAGE_WORKFLOW = {
10
+ workflowId: "wf_two_page_fixture",
10
11
  initial_page_id: "rcpaywall_welcome",
11
12
  initial_step_id: "step_welcome",
12
13
  steps: {
package/dist/index.d.ts CHANGED
@@ -17,7 +17,7 @@ export { default as Workflow } from "./components/workflows/Workflow.svelte";
17
17
  export { default as Video } from "./components/video/Video.svelte";
18
18
  export * from "./types";
19
19
  export { type PaywallData } from "./types/paywall";
20
- export { type WorkflowNavData, workflowDataToNavData, } from "./types/workflow-nav";
20
+ export { type WorkflowNavData, type WorkflowStepChangeEvent, workflowDataToNavData, } from "./types/workflow-nav";
21
21
  export { type WorkflowData, type WorkflowStep } from "./types/workflow";
22
22
  export { type InitialInputSelections } from "./stores/inputValidation";
23
23
  export { type UIConfig } from "./types/ui-config";
@@ -1,10 +1,27 @@
1
1
  import type { WorkflowData, WorkflowScreen, WorkflowStep } from "./workflow";
2
+ export interface WorkflowStepChangeEvent {
3
+ workflowId: string;
4
+ stepId: string;
5
+ fromStepId: string | undefined;
6
+ toStepId: string | undefined;
7
+ isFirstStep: boolean;
8
+ isLastStep: boolean;
9
+ /**
10
+ * Why this event was emitted:
11
+ * - `"start"` / `"forward"` / `"back"` — step is being entered (fire step_started)
12
+ * - `"completed"` — step is being left (fire step_completed); toStepId is set
13
+ * for forward nav, undefined when the workflow is dismissed/closed.
14
+ */
15
+ reason: "start" | "forward" | "back" | "completed";
16
+ }
2
17
  /**
3
18
  * A self-contained multi-page workflow payload for use with the Workflow
4
19
  * component. This mirrors the shape of rc-workflows' WorkflowData but only
5
20
  * includes the fields needed for client-side page navigation.
6
21
  */
7
22
  export interface WorkflowNavData {
23
+ /** The workflow's unique identifier. */
24
+ workflowId: string;
8
25
  /** ID of the first page to display. Must be a key in `pages`. */
9
26
  initial_page_id: string;
10
27
  /**
@@ -24,6 +24,7 @@ export function workflowDataToNavData(data) {
24
24
  return null;
25
25
  }
26
26
  return {
27
+ workflowId: data.id,
27
28
  initial_page_id: initialPageId,
28
29
  initial_step_id: data.initial_step_id,
29
30
  // Shared by reference intentionally — screens/steps are never mutated after
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": "4.8.0",
5
+ "version": "4.8.2",
6
6
  "author": {
7
7
  "name": "RevenueCat, Inc."
8
8
  },