@churnkey/react 0.6.4 → 0.7.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.
- package/dist/{chunk-WTFB6T53.cjs → chunk-4ZAJYXG2.cjs} +7 -6
- package/dist/chunk-4ZAJYXG2.cjs.map +1 -0
- package/dist/{chunk-4WYZSTRI.js → chunk-BODEVZ3K.js} +189 -10
- package/dist/chunk-BODEVZ3K.js.map +1 -0
- package/dist/{chunk-EYIGZLVT.js → chunk-GH5EKETT.js} +6 -3
- package/dist/chunk-GH5EKETT.js.map +1 -0
- package/dist/{chunk-OC5H64F4.js → chunk-KS3RWFRH.js} +4 -3
- package/dist/chunk-KS3RWFRH.js.map +1 -0
- package/dist/{chunk-WVSNF3TN.cjs → chunk-M6Z3BHEH.cjs} +6 -2
- package/dist/chunk-M6Z3BHEH.cjs.map +1 -0
- package/dist/{chunk-WGS432VT.cjs → chunk-SWEPUHIN.cjs} +192 -9
- package/dist/chunk-SWEPUHIN.cjs.map +1 -0
- package/dist/core.cjs +30 -14
- package/dist/core.d.cts +22 -6
- package/dist/core.d.ts +22 -6
- package/dist/core.js +2 -2
- package/dist/headless.cjs +3 -3
- package/dist/headless.d.cts +4 -1
- package/dist/headless.d.ts +4 -1
- package/dist/headless.js +2 -2
- package/dist/index.cjs +235 -161
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -14
- package/dist/index.d.ts +14 -14
- package/dist/index.js +136 -78
- package/dist/index.js.map +1 -1
- package/dist/{step-graph-BpoAhd0d.d.cts → step-graph-s1L1wV-P.d.cts} +184 -2
- package/dist/{step-graph-BpoAhd0d.d.ts → step-graph-s1L1wV-P.d.ts} +184 -2
- package/dist/styles.css +13 -0
- package/package.json +1 -1
- package/dist/chunk-4WYZSTRI.js.map +0 -1
- package/dist/chunk-EYIGZLVT.js.map +0 -1
- package/dist/chunk-OC5H64F4.js.map +0 -1
- package/dist/chunk-WGS432VT.cjs.map +0 -1
- package/dist/chunk-WTFB6T53.cjs.map +0 -1
- package/dist/chunk-WVSNF3TN.cjs.map +0 -1
|
@@ -1,5 +1,131 @@
|
|
|
1
1
|
import { ReactNode, ReactElement, ComponentType } from 'react';
|
|
2
2
|
|
|
3
|
+
/** Both timing variants of a message. Which one renders is decided by the
|
|
4
|
+
* flow's effective cancel timing (`FlowState.cancelAtPeriodEnd`). */
|
|
5
|
+
interface TimingVariants {
|
|
6
|
+
immediate: string;
|
|
7
|
+
atPeriodEnd: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* A message that may differ by cancellation timing. Plain strings render
|
|
11
|
+
* as-is in both cases; pass the object form when the wording should change
|
|
12
|
+
* (e.g. "Cancel" vs "Turn off auto-renew").
|
|
13
|
+
*/
|
|
14
|
+
type TimingAware = string | TimingVariants;
|
|
15
|
+
/** Success-screen copy for one accepted offer type. */
|
|
16
|
+
interface SavedOfferCopy {
|
|
17
|
+
title: string;
|
|
18
|
+
description: string;
|
|
19
|
+
}
|
|
20
|
+
interface CancelFlowMessages {
|
|
21
|
+
common: {
|
|
22
|
+
continue: string;
|
|
23
|
+
back: string;
|
|
24
|
+
close: string;
|
|
25
|
+
done: string;
|
|
26
|
+
processing: string;
|
|
27
|
+
tryAgain: string;
|
|
28
|
+
loading: string;
|
|
29
|
+
loadError: string;
|
|
30
|
+
error: string;
|
|
31
|
+
day: string;
|
|
32
|
+
days: string;
|
|
33
|
+
month: string;
|
|
34
|
+
months: string;
|
|
35
|
+
};
|
|
36
|
+
survey: {
|
|
37
|
+
title: string;
|
|
38
|
+
followupPlaceholder: string;
|
|
39
|
+
followupAriaLabel: string;
|
|
40
|
+
};
|
|
41
|
+
feedback: {
|
|
42
|
+
title: string;
|
|
43
|
+
placeholder: string;
|
|
44
|
+
/** Used instead of `placeholder` when the step sets `minLength`. Supports `{minLength}`. */
|
|
45
|
+
placeholderWithMin: string;
|
|
46
|
+
};
|
|
47
|
+
confirm: {
|
|
48
|
+
title: string;
|
|
49
|
+
lossesLabel: string;
|
|
50
|
+
cta: TimingAware;
|
|
51
|
+
goBack: string;
|
|
52
|
+
/** Shown between the description and the confirm button. Supports
|
|
53
|
+
* `{periodEnd}`. The default immediate variant is empty, which
|
|
54
|
+
* suppresses the notice for immediate timing; empty OVERRIDE values are
|
|
55
|
+
* dropped like any blank override, so to remove the notice entirely
|
|
56
|
+
* replace the `Confirm` component or hide it via
|
|
57
|
+
* `classNames.periodEndNotice`. */
|
|
58
|
+
periodEndNotice: TimingAware;
|
|
59
|
+
};
|
|
60
|
+
success: {
|
|
61
|
+
saved: {
|
|
62
|
+
/** Generic fallback — used for offer types without their own entry
|
|
63
|
+
* (contact, redirect, custom types). */
|
|
64
|
+
title: string;
|
|
65
|
+
description: string;
|
|
66
|
+
discount: SavedOfferCopy;
|
|
67
|
+
pause: SavedOfferCopy & {
|
|
68
|
+
/** Used instead of `description` when the resume date is known
|
|
69
|
+
* (the built-in pause UI always supplies it). Supports `{resumeDate}`. */
|
|
70
|
+
resumeDescription: string;
|
|
71
|
+
};
|
|
72
|
+
trial_extension: SavedOfferCopy;
|
|
73
|
+
plan_change: SavedOfferCopy;
|
|
74
|
+
rebate: SavedOfferCopy;
|
|
75
|
+
};
|
|
76
|
+
cancelled: {
|
|
77
|
+
title: TimingAware;
|
|
78
|
+
description: TimingAware;
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
offer: {
|
|
82
|
+
limitedTimeEyebrow: string;
|
|
83
|
+
pauseEyebrow: string;
|
|
84
|
+
newEndDateLabel: string;
|
|
85
|
+
currentPlanBadge: string;
|
|
86
|
+
/** CTA when a plan is selected in a plan-change offer. Supports `{planName}`. */
|
|
87
|
+
switchToCta: string;
|
|
88
|
+
rebate: {
|
|
89
|
+
paidLabel: string;
|
|
90
|
+
moneyBackLabel: string;
|
|
91
|
+
/** Tax parenthetical after the money-back label. Supports `{amount}`. */
|
|
92
|
+
inclTax: string;
|
|
93
|
+
netLabel: string;
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Recursive partial of the catalog for overrides. Timing-aware fields also
|
|
99
|
+
* accept a plain string (applies to both timings) or a partial variant pair
|
|
100
|
+
* (the missing variant keeps the base value).
|
|
101
|
+
*/
|
|
102
|
+
type MessagesPatch<T = CancelFlowMessages> = {
|
|
103
|
+
[K in keyof T]?: T[K] extends string ? string : T[K] extends TimingAware ? string | Partial<TimingVariants> : MessagesPatch<T[K]>;
|
|
104
|
+
};
|
|
105
|
+
interface I18nConfig {
|
|
106
|
+
/** UI language. Defaults to the browser language, falling back to `'en'`. */
|
|
107
|
+
locale?: string;
|
|
108
|
+
/** Per-locale message overrides, e.g. `{ en: { confirm: { cta: 'Turn off auto-renew' } } }`. */
|
|
109
|
+
messages?: Record<string, MessagesPatch>;
|
|
110
|
+
}
|
|
111
|
+
declare const defaultMessages: CancelFlowMessages;
|
|
112
|
+
/**
|
|
113
|
+
* Pick the string for the flow's effective cancel timing. `null` (timing
|
|
114
|
+
* unknown — local mode) behaves like period-end, matching the default the
|
|
115
|
+
* cancel action itself uses.
|
|
116
|
+
*/
|
|
117
|
+
declare function selectTiming(value: TimingAware, cancelAtPeriodEnd: boolean | null): string;
|
|
118
|
+
/** Replace `{name}` tokens. Unknown tokens are left in place. */
|
|
119
|
+
declare function formatMessage(template: string, vars: Record<string, string | number>): string;
|
|
120
|
+
/**
|
|
121
|
+
* Resolve the full catalog for a locale. Layers, lowest to highest: built-in
|
|
122
|
+
* defaults, then `orgMessages` (dashboard-configured overrides delivered on
|
|
123
|
+
* the token-mode config), then the config's own per-locale messages — the
|
|
124
|
+
* developer's overrides always end up on top. Both per-language layers run
|
|
125
|
+
* through the same locale fallback chain.
|
|
126
|
+
*/
|
|
127
|
+
declare function buildMessages(i18n?: I18nConfig, orgMessages?: Record<string, MessagesPatch>): CancelFlowMessages;
|
|
128
|
+
|
|
3
129
|
interface DirectAddress {
|
|
4
130
|
line1?: string;
|
|
5
131
|
line2?: string;
|
|
@@ -190,7 +316,9 @@ interface OfferCopy {
|
|
|
190
316
|
* May contain HTML when the flow was authored in the Churnkey dashboard
|
|
191
317
|
* (the description editor is rich text). Render with the exported
|
|
192
318
|
* `RichText` component — the same renderer the built-in steps use — rather
|
|
193
|
-
* than as a text node, or the markup shows escaped.
|
|
319
|
+
* than as a text node, or the markup shows escaped. Pass `as="div"` (built-in
|
|
320
|
+
* steps do) so block content like an embedded video iframe isn't hoisted out
|
|
321
|
+
* of the default `<p>` wrapper.
|
|
194
322
|
*/
|
|
195
323
|
body: string;
|
|
196
324
|
cta: string;
|
|
@@ -442,10 +570,14 @@ interface ModalProps {
|
|
|
442
570
|
interface CloseButtonProps {
|
|
443
571
|
onClose: () => void;
|
|
444
572
|
className?: string;
|
|
573
|
+
/** Accessible label. Defaults to "Close". */
|
|
574
|
+
label?: string;
|
|
445
575
|
}
|
|
446
576
|
interface BackButtonProps {
|
|
447
577
|
onBack: () => void;
|
|
448
578
|
className?: string;
|
|
579
|
+
/** Visible label. Defaults to "Back". */
|
|
580
|
+
label?: string;
|
|
449
581
|
}
|
|
450
582
|
interface SurveyStepProps {
|
|
451
583
|
title: string;
|
|
@@ -462,6 +594,8 @@ interface SurveyStepProps {
|
|
|
462
594
|
onNext: () => void;
|
|
463
595
|
classNames?: SurveyClassNames;
|
|
464
596
|
components?: Partial<ComponentOverrides>;
|
|
597
|
+
/** Resolved message catalog. Default components fall back to `defaultMessages` when absent. */
|
|
598
|
+
messages?: CancelFlowMessages;
|
|
465
599
|
}
|
|
466
600
|
interface OfferStepProps {
|
|
467
601
|
title?: string;
|
|
@@ -484,6 +618,8 @@ interface OfferStepProps {
|
|
|
484
618
|
* implementations can ignore this.
|
|
485
619
|
*/
|
|
486
620
|
components?: Partial<ComponentOverrides>;
|
|
621
|
+
/** Resolved message catalog. Default components fall back to `defaultMessages` when absent. */
|
|
622
|
+
messages?: CancelFlowMessages;
|
|
487
623
|
}
|
|
488
624
|
interface FeedbackStepProps {
|
|
489
625
|
title: string;
|
|
@@ -497,6 +633,8 @@ interface FeedbackStepProps {
|
|
|
497
633
|
onChange: (text: string) => void;
|
|
498
634
|
onSubmit: () => void;
|
|
499
635
|
classNames?: FeedbackClassNames;
|
|
636
|
+
/** Resolved message catalog. Default components fall back to `defaultMessages` when absent. */
|
|
637
|
+
messages?: CancelFlowMessages;
|
|
500
638
|
}
|
|
501
639
|
interface ConfirmStepProps {
|
|
502
640
|
title: string;
|
|
@@ -507,20 +645,33 @@ interface ConfirmStepProps {
|
|
|
507
645
|
lossesLabel?: string;
|
|
508
646
|
confirmLabel: string;
|
|
509
647
|
goBackLabel: string;
|
|
648
|
+
/**
|
|
649
|
+
* Timing-resolved access notice ("Your access continues until June 14, 2026."),
|
|
650
|
+
* rendered between the loss list and the confirm button. Absent when the
|
|
651
|
+
* period end is unknown or the resolved message is empty.
|
|
652
|
+
*/
|
|
653
|
+
periodEndNotice?: string;
|
|
510
654
|
onConfirm: () => Promise<void>;
|
|
511
655
|
onGoBack: () => void;
|
|
512
656
|
isProcessing: boolean;
|
|
513
657
|
classNames?: ConfirmClassNames;
|
|
658
|
+
/** Resolved message catalog. Default components fall back to `defaultMessages` when absent. */
|
|
659
|
+
messages?: CancelFlowMessages;
|
|
514
660
|
}
|
|
515
661
|
interface SuccessStepProps {
|
|
516
662
|
outcome: 'saved' | 'cancelled';
|
|
517
663
|
offer?: OfferDecision;
|
|
664
|
+
/** The accepted offer (consumer-facing shape, incl. any accept result).
|
|
665
|
+
* Present when outcome is 'saved'. */
|
|
666
|
+
acceptedOffer?: AcceptedOffer;
|
|
518
667
|
title: string;
|
|
519
668
|
description?: string;
|
|
520
669
|
customer: DirectCustomer | null;
|
|
521
670
|
subscriptions: DirectSubscription[];
|
|
522
671
|
onClose: () => void;
|
|
523
672
|
classNames?: SuccessClassNames;
|
|
673
|
+
/** Resolved message catalog. Default components fall back to `defaultMessages` when absent. */
|
|
674
|
+
messages?: CancelFlowMessages;
|
|
524
675
|
}
|
|
525
676
|
interface ReasonButtonProps {
|
|
526
677
|
reason: ReasonConfig;
|
|
@@ -535,10 +686,21 @@ interface FlowState {
|
|
|
535
686
|
followupResponse: string;
|
|
536
687
|
feedback: string;
|
|
537
688
|
outcome: 'saved' | 'cancelled' | null;
|
|
689
|
+
/** The offer the customer accepted, set alongside outcome 'saved'. Drives
|
|
690
|
+
* per-offer success copy; `null` until then and for cancellations. */
|
|
691
|
+
acceptedOffer: AcceptedOffer | null;
|
|
538
692
|
isProcessing: boolean;
|
|
539
693
|
error: Error | null;
|
|
540
694
|
customer: DirectCustomer | null;
|
|
541
695
|
subscriptions: DirectSubscription[];
|
|
696
|
+
/**
|
|
697
|
+
* Effective cancel timing for this flow: the server-resolved value per
|
|
698
|
+
* blueprint (token mode), else the developer's `cancelAtPeriodEnd`
|
|
699
|
+
* declaration (local mode), else `null` — unknown. Drives timing-aware
|
|
700
|
+
* messages; `null` reads as period-end, matching the default the cancel
|
|
701
|
+
* action uses.
|
|
702
|
+
*/
|
|
703
|
+
cancelAtPeriodEnd: boolean | null;
|
|
542
704
|
}
|
|
543
705
|
declare const MODES: readonly ["live", "test", "sandbox"];
|
|
544
706
|
/**
|
|
@@ -572,6 +734,22 @@ interface FlowConfig extends FlowCallbacks {
|
|
|
572
734
|
* this field.
|
|
573
735
|
*/
|
|
574
736
|
mode?: Mode;
|
|
737
|
+
/**
|
|
738
|
+
* Locale and message overrides for the SDK's own strings (buttons, loading
|
|
739
|
+
* and confirmation chrome). Flow content — step titles, offer copy — is
|
|
740
|
+
* authored in the dashboard and localized server-side; override it there or
|
|
741
|
+
* via `steps`. Read once at mount, like `steps`.
|
|
742
|
+
*/
|
|
743
|
+
i18n?: I18nConfig;
|
|
744
|
+
/**
|
|
745
|
+
* Local-mode declaration of your billing behavior: does cancellation take
|
|
746
|
+
* effect at the end of the billing period (`true`) or immediately
|
|
747
|
+
* (`false`)? Drives timing-aware messages and the confirm step's
|
|
748
|
+
* access-until notice. In token mode this is ignored: the server-resolved
|
|
749
|
+
* value is authoritative, and if the server omits it the timing is treated
|
|
750
|
+
* as unknown rather than falling back to this declaration.
|
|
751
|
+
*/
|
|
752
|
+
cancelAtPeriodEnd?: boolean;
|
|
575
753
|
}
|
|
576
754
|
type OfferCallback = (offer: AcceptedOffer, customer: DirectCustomer | null) => Promise<void> | void;
|
|
577
755
|
type CancelCallback = (customer: DirectCustomer | null) => Promise<void> | void;
|
|
@@ -615,6 +793,10 @@ interface CancelFlowProps extends FlowCallbacks {
|
|
|
615
793
|
apiBaseUrl?: string;
|
|
616
794
|
/** See FlowConfig.mode. Ignored in token mode (the token is authoritative). */
|
|
617
795
|
mode?: Mode;
|
|
796
|
+
/** See FlowConfig.i18n. */
|
|
797
|
+
i18n?: I18nConfig;
|
|
798
|
+
/** See FlowConfig.cancelAtPeriodEnd. Local mode only; the token config wins. */
|
|
799
|
+
cancelAtPeriodEnd?: boolean;
|
|
618
800
|
appearance?: Appearance;
|
|
619
801
|
classNames?: StructuralClassNames;
|
|
620
802
|
components?: Partial<ComponentOverrides>;
|
|
@@ -647,4 +829,4 @@ interface ResolvedStep {
|
|
|
647
829
|
classNames?: unknown;
|
|
648
830
|
}
|
|
649
831
|
|
|
650
|
-
export type
|
|
832
|
+
export { type Step as $, type AcceptedOffer as A, type BackButtonProps as B, type CancelFlowProps as C, type DirectAddress as D, type FlowConfig as E, type FeedbackStepProps as F, type FlowState as G, type MessagesPatch as H, type I18nConfig as I, type Mode as J, type OfferClassNames as K, type OfferConfig as L, type ModalProps as M, type OfferCopy as N, type OfferStepProps as O, type OfferDecision as P, type OfferStep as Q, type ReasonButtonProps as R, type SuccessStepProps as S, type PauseOffer as T, type PlanChangeOffer as U, type PlanOption as V, type ReasonConfig as W, type RebateOffer as X, type RedirectOffer as Y, type ResolvedStep as Z, type SavedOfferCopy as _, type ConfirmStepProps as a, type StructuralClassNames as a0, type SubscriptionStatus as a1, type SuccessClassNames as a2, type SuccessStep as a3, type SurveyClassNames as a4, type SurveyStep as a5, type TimingAware as a6, type TimingVariants as a7, type TrialExtensionOffer as a8, buildMessages as a9, defaultMessages as aa, formatMessage as ab, selectTiming as ac, type SurveyStepProps as b, type CloseButtonProps as c, type Appearance as d, type AppearanceVariables as e, type BuiltInOfferConfig as f, type BuiltInStep as g, type BuiltInStepType as h, type CancelFlowMessages as i, type ComponentOverrides as j, type ConfirmClassNames as k, type ConfirmStep as l, type ContactOffer as m, type CustomComponents as n, type CustomOfferConfig as o, type CustomOfferProps as p, type CustomStepConfig as q, type CustomStepProps as r, type DirectCoupon as s, type DirectCustomer as t, type DirectPrice as u, type DirectSubscription as v, type DirectSubscriptionItem as w, type DiscountOffer as x, type FeedbackClassNames as y, type FeedbackStep as z };
|
|
@@ -1,5 +1,131 @@
|
|
|
1
1
|
import { ReactNode, ReactElement, ComponentType } from 'react';
|
|
2
2
|
|
|
3
|
+
/** Both timing variants of a message. Which one renders is decided by the
|
|
4
|
+
* flow's effective cancel timing (`FlowState.cancelAtPeriodEnd`). */
|
|
5
|
+
interface TimingVariants {
|
|
6
|
+
immediate: string;
|
|
7
|
+
atPeriodEnd: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* A message that may differ by cancellation timing. Plain strings render
|
|
11
|
+
* as-is in both cases; pass the object form when the wording should change
|
|
12
|
+
* (e.g. "Cancel" vs "Turn off auto-renew").
|
|
13
|
+
*/
|
|
14
|
+
type TimingAware = string | TimingVariants;
|
|
15
|
+
/** Success-screen copy for one accepted offer type. */
|
|
16
|
+
interface SavedOfferCopy {
|
|
17
|
+
title: string;
|
|
18
|
+
description: string;
|
|
19
|
+
}
|
|
20
|
+
interface CancelFlowMessages {
|
|
21
|
+
common: {
|
|
22
|
+
continue: string;
|
|
23
|
+
back: string;
|
|
24
|
+
close: string;
|
|
25
|
+
done: string;
|
|
26
|
+
processing: string;
|
|
27
|
+
tryAgain: string;
|
|
28
|
+
loading: string;
|
|
29
|
+
loadError: string;
|
|
30
|
+
error: string;
|
|
31
|
+
day: string;
|
|
32
|
+
days: string;
|
|
33
|
+
month: string;
|
|
34
|
+
months: string;
|
|
35
|
+
};
|
|
36
|
+
survey: {
|
|
37
|
+
title: string;
|
|
38
|
+
followupPlaceholder: string;
|
|
39
|
+
followupAriaLabel: string;
|
|
40
|
+
};
|
|
41
|
+
feedback: {
|
|
42
|
+
title: string;
|
|
43
|
+
placeholder: string;
|
|
44
|
+
/** Used instead of `placeholder` when the step sets `minLength`. Supports `{minLength}`. */
|
|
45
|
+
placeholderWithMin: string;
|
|
46
|
+
};
|
|
47
|
+
confirm: {
|
|
48
|
+
title: string;
|
|
49
|
+
lossesLabel: string;
|
|
50
|
+
cta: TimingAware;
|
|
51
|
+
goBack: string;
|
|
52
|
+
/** Shown between the description and the confirm button. Supports
|
|
53
|
+
* `{periodEnd}`. The default immediate variant is empty, which
|
|
54
|
+
* suppresses the notice for immediate timing; empty OVERRIDE values are
|
|
55
|
+
* dropped like any blank override, so to remove the notice entirely
|
|
56
|
+
* replace the `Confirm` component or hide it via
|
|
57
|
+
* `classNames.periodEndNotice`. */
|
|
58
|
+
periodEndNotice: TimingAware;
|
|
59
|
+
};
|
|
60
|
+
success: {
|
|
61
|
+
saved: {
|
|
62
|
+
/** Generic fallback — used for offer types without their own entry
|
|
63
|
+
* (contact, redirect, custom types). */
|
|
64
|
+
title: string;
|
|
65
|
+
description: string;
|
|
66
|
+
discount: SavedOfferCopy;
|
|
67
|
+
pause: SavedOfferCopy & {
|
|
68
|
+
/** Used instead of `description` when the resume date is known
|
|
69
|
+
* (the built-in pause UI always supplies it). Supports `{resumeDate}`. */
|
|
70
|
+
resumeDescription: string;
|
|
71
|
+
};
|
|
72
|
+
trial_extension: SavedOfferCopy;
|
|
73
|
+
plan_change: SavedOfferCopy;
|
|
74
|
+
rebate: SavedOfferCopy;
|
|
75
|
+
};
|
|
76
|
+
cancelled: {
|
|
77
|
+
title: TimingAware;
|
|
78
|
+
description: TimingAware;
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
offer: {
|
|
82
|
+
limitedTimeEyebrow: string;
|
|
83
|
+
pauseEyebrow: string;
|
|
84
|
+
newEndDateLabel: string;
|
|
85
|
+
currentPlanBadge: string;
|
|
86
|
+
/** CTA when a plan is selected in a plan-change offer. Supports `{planName}`. */
|
|
87
|
+
switchToCta: string;
|
|
88
|
+
rebate: {
|
|
89
|
+
paidLabel: string;
|
|
90
|
+
moneyBackLabel: string;
|
|
91
|
+
/** Tax parenthetical after the money-back label. Supports `{amount}`. */
|
|
92
|
+
inclTax: string;
|
|
93
|
+
netLabel: string;
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Recursive partial of the catalog for overrides. Timing-aware fields also
|
|
99
|
+
* accept a plain string (applies to both timings) or a partial variant pair
|
|
100
|
+
* (the missing variant keeps the base value).
|
|
101
|
+
*/
|
|
102
|
+
type MessagesPatch<T = CancelFlowMessages> = {
|
|
103
|
+
[K in keyof T]?: T[K] extends string ? string : T[K] extends TimingAware ? string | Partial<TimingVariants> : MessagesPatch<T[K]>;
|
|
104
|
+
};
|
|
105
|
+
interface I18nConfig {
|
|
106
|
+
/** UI language. Defaults to the browser language, falling back to `'en'`. */
|
|
107
|
+
locale?: string;
|
|
108
|
+
/** Per-locale message overrides, e.g. `{ en: { confirm: { cta: 'Turn off auto-renew' } } }`. */
|
|
109
|
+
messages?: Record<string, MessagesPatch>;
|
|
110
|
+
}
|
|
111
|
+
declare const defaultMessages: CancelFlowMessages;
|
|
112
|
+
/**
|
|
113
|
+
* Pick the string for the flow's effective cancel timing. `null` (timing
|
|
114
|
+
* unknown — local mode) behaves like period-end, matching the default the
|
|
115
|
+
* cancel action itself uses.
|
|
116
|
+
*/
|
|
117
|
+
declare function selectTiming(value: TimingAware, cancelAtPeriodEnd: boolean | null): string;
|
|
118
|
+
/** Replace `{name}` tokens. Unknown tokens are left in place. */
|
|
119
|
+
declare function formatMessage(template: string, vars: Record<string, string | number>): string;
|
|
120
|
+
/**
|
|
121
|
+
* Resolve the full catalog for a locale. Layers, lowest to highest: built-in
|
|
122
|
+
* defaults, then `orgMessages` (dashboard-configured overrides delivered on
|
|
123
|
+
* the token-mode config), then the config's own per-locale messages — the
|
|
124
|
+
* developer's overrides always end up on top. Both per-language layers run
|
|
125
|
+
* through the same locale fallback chain.
|
|
126
|
+
*/
|
|
127
|
+
declare function buildMessages(i18n?: I18nConfig, orgMessages?: Record<string, MessagesPatch>): CancelFlowMessages;
|
|
128
|
+
|
|
3
129
|
interface DirectAddress {
|
|
4
130
|
line1?: string;
|
|
5
131
|
line2?: string;
|
|
@@ -190,7 +316,9 @@ interface OfferCopy {
|
|
|
190
316
|
* May contain HTML when the flow was authored in the Churnkey dashboard
|
|
191
317
|
* (the description editor is rich text). Render with the exported
|
|
192
318
|
* `RichText` component — the same renderer the built-in steps use — rather
|
|
193
|
-
* than as a text node, or the markup shows escaped.
|
|
319
|
+
* than as a text node, or the markup shows escaped. Pass `as="div"` (built-in
|
|
320
|
+
* steps do) so block content like an embedded video iframe isn't hoisted out
|
|
321
|
+
* of the default `<p>` wrapper.
|
|
194
322
|
*/
|
|
195
323
|
body: string;
|
|
196
324
|
cta: string;
|
|
@@ -442,10 +570,14 @@ interface ModalProps {
|
|
|
442
570
|
interface CloseButtonProps {
|
|
443
571
|
onClose: () => void;
|
|
444
572
|
className?: string;
|
|
573
|
+
/** Accessible label. Defaults to "Close". */
|
|
574
|
+
label?: string;
|
|
445
575
|
}
|
|
446
576
|
interface BackButtonProps {
|
|
447
577
|
onBack: () => void;
|
|
448
578
|
className?: string;
|
|
579
|
+
/** Visible label. Defaults to "Back". */
|
|
580
|
+
label?: string;
|
|
449
581
|
}
|
|
450
582
|
interface SurveyStepProps {
|
|
451
583
|
title: string;
|
|
@@ -462,6 +594,8 @@ interface SurveyStepProps {
|
|
|
462
594
|
onNext: () => void;
|
|
463
595
|
classNames?: SurveyClassNames;
|
|
464
596
|
components?: Partial<ComponentOverrides>;
|
|
597
|
+
/** Resolved message catalog. Default components fall back to `defaultMessages` when absent. */
|
|
598
|
+
messages?: CancelFlowMessages;
|
|
465
599
|
}
|
|
466
600
|
interface OfferStepProps {
|
|
467
601
|
title?: string;
|
|
@@ -484,6 +618,8 @@ interface OfferStepProps {
|
|
|
484
618
|
* implementations can ignore this.
|
|
485
619
|
*/
|
|
486
620
|
components?: Partial<ComponentOverrides>;
|
|
621
|
+
/** Resolved message catalog. Default components fall back to `defaultMessages` when absent. */
|
|
622
|
+
messages?: CancelFlowMessages;
|
|
487
623
|
}
|
|
488
624
|
interface FeedbackStepProps {
|
|
489
625
|
title: string;
|
|
@@ -497,6 +633,8 @@ interface FeedbackStepProps {
|
|
|
497
633
|
onChange: (text: string) => void;
|
|
498
634
|
onSubmit: () => void;
|
|
499
635
|
classNames?: FeedbackClassNames;
|
|
636
|
+
/** Resolved message catalog. Default components fall back to `defaultMessages` when absent. */
|
|
637
|
+
messages?: CancelFlowMessages;
|
|
500
638
|
}
|
|
501
639
|
interface ConfirmStepProps {
|
|
502
640
|
title: string;
|
|
@@ -507,20 +645,33 @@ interface ConfirmStepProps {
|
|
|
507
645
|
lossesLabel?: string;
|
|
508
646
|
confirmLabel: string;
|
|
509
647
|
goBackLabel: string;
|
|
648
|
+
/**
|
|
649
|
+
* Timing-resolved access notice ("Your access continues until June 14, 2026."),
|
|
650
|
+
* rendered between the loss list and the confirm button. Absent when the
|
|
651
|
+
* period end is unknown or the resolved message is empty.
|
|
652
|
+
*/
|
|
653
|
+
periodEndNotice?: string;
|
|
510
654
|
onConfirm: () => Promise<void>;
|
|
511
655
|
onGoBack: () => void;
|
|
512
656
|
isProcessing: boolean;
|
|
513
657
|
classNames?: ConfirmClassNames;
|
|
658
|
+
/** Resolved message catalog. Default components fall back to `defaultMessages` when absent. */
|
|
659
|
+
messages?: CancelFlowMessages;
|
|
514
660
|
}
|
|
515
661
|
interface SuccessStepProps {
|
|
516
662
|
outcome: 'saved' | 'cancelled';
|
|
517
663
|
offer?: OfferDecision;
|
|
664
|
+
/** The accepted offer (consumer-facing shape, incl. any accept result).
|
|
665
|
+
* Present when outcome is 'saved'. */
|
|
666
|
+
acceptedOffer?: AcceptedOffer;
|
|
518
667
|
title: string;
|
|
519
668
|
description?: string;
|
|
520
669
|
customer: DirectCustomer | null;
|
|
521
670
|
subscriptions: DirectSubscription[];
|
|
522
671
|
onClose: () => void;
|
|
523
672
|
classNames?: SuccessClassNames;
|
|
673
|
+
/** Resolved message catalog. Default components fall back to `defaultMessages` when absent. */
|
|
674
|
+
messages?: CancelFlowMessages;
|
|
524
675
|
}
|
|
525
676
|
interface ReasonButtonProps {
|
|
526
677
|
reason: ReasonConfig;
|
|
@@ -535,10 +686,21 @@ interface FlowState {
|
|
|
535
686
|
followupResponse: string;
|
|
536
687
|
feedback: string;
|
|
537
688
|
outcome: 'saved' | 'cancelled' | null;
|
|
689
|
+
/** The offer the customer accepted, set alongside outcome 'saved'. Drives
|
|
690
|
+
* per-offer success copy; `null` until then and for cancellations. */
|
|
691
|
+
acceptedOffer: AcceptedOffer | null;
|
|
538
692
|
isProcessing: boolean;
|
|
539
693
|
error: Error | null;
|
|
540
694
|
customer: DirectCustomer | null;
|
|
541
695
|
subscriptions: DirectSubscription[];
|
|
696
|
+
/**
|
|
697
|
+
* Effective cancel timing for this flow: the server-resolved value per
|
|
698
|
+
* blueprint (token mode), else the developer's `cancelAtPeriodEnd`
|
|
699
|
+
* declaration (local mode), else `null` — unknown. Drives timing-aware
|
|
700
|
+
* messages; `null` reads as period-end, matching the default the cancel
|
|
701
|
+
* action uses.
|
|
702
|
+
*/
|
|
703
|
+
cancelAtPeriodEnd: boolean | null;
|
|
542
704
|
}
|
|
543
705
|
declare const MODES: readonly ["live", "test", "sandbox"];
|
|
544
706
|
/**
|
|
@@ -572,6 +734,22 @@ interface FlowConfig extends FlowCallbacks {
|
|
|
572
734
|
* this field.
|
|
573
735
|
*/
|
|
574
736
|
mode?: Mode;
|
|
737
|
+
/**
|
|
738
|
+
* Locale and message overrides for the SDK's own strings (buttons, loading
|
|
739
|
+
* and confirmation chrome). Flow content — step titles, offer copy — is
|
|
740
|
+
* authored in the dashboard and localized server-side; override it there or
|
|
741
|
+
* via `steps`. Read once at mount, like `steps`.
|
|
742
|
+
*/
|
|
743
|
+
i18n?: I18nConfig;
|
|
744
|
+
/**
|
|
745
|
+
* Local-mode declaration of your billing behavior: does cancellation take
|
|
746
|
+
* effect at the end of the billing period (`true`) or immediately
|
|
747
|
+
* (`false`)? Drives timing-aware messages and the confirm step's
|
|
748
|
+
* access-until notice. In token mode this is ignored: the server-resolved
|
|
749
|
+
* value is authoritative, and if the server omits it the timing is treated
|
|
750
|
+
* as unknown rather than falling back to this declaration.
|
|
751
|
+
*/
|
|
752
|
+
cancelAtPeriodEnd?: boolean;
|
|
575
753
|
}
|
|
576
754
|
type OfferCallback = (offer: AcceptedOffer, customer: DirectCustomer | null) => Promise<void> | void;
|
|
577
755
|
type CancelCallback = (customer: DirectCustomer | null) => Promise<void> | void;
|
|
@@ -615,6 +793,10 @@ interface CancelFlowProps extends FlowCallbacks {
|
|
|
615
793
|
apiBaseUrl?: string;
|
|
616
794
|
/** See FlowConfig.mode. Ignored in token mode (the token is authoritative). */
|
|
617
795
|
mode?: Mode;
|
|
796
|
+
/** See FlowConfig.i18n. */
|
|
797
|
+
i18n?: I18nConfig;
|
|
798
|
+
/** See FlowConfig.cancelAtPeriodEnd. Local mode only; the token config wins. */
|
|
799
|
+
cancelAtPeriodEnd?: boolean;
|
|
618
800
|
appearance?: Appearance;
|
|
619
801
|
classNames?: StructuralClassNames;
|
|
620
802
|
components?: Partial<ComponentOverrides>;
|
|
@@ -647,4 +829,4 @@ interface ResolvedStep {
|
|
|
647
829
|
classNames?: unknown;
|
|
648
830
|
}
|
|
649
831
|
|
|
650
|
-
export type
|
|
832
|
+
export { type Step as $, type AcceptedOffer as A, type BackButtonProps as B, type CancelFlowProps as C, type DirectAddress as D, type FlowConfig as E, type FeedbackStepProps as F, type FlowState as G, type MessagesPatch as H, type I18nConfig as I, type Mode as J, type OfferClassNames as K, type OfferConfig as L, type ModalProps as M, type OfferCopy as N, type OfferStepProps as O, type OfferDecision as P, type OfferStep as Q, type ReasonButtonProps as R, type SuccessStepProps as S, type PauseOffer as T, type PlanChangeOffer as U, type PlanOption as V, type ReasonConfig as W, type RebateOffer as X, type RedirectOffer as Y, type ResolvedStep as Z, type SavedOfferCopy as _, type ConfirmStepProps as a, type StructuralClassNames as a0, type SubscriptionStatus as a1, type SuccessClassNames as a2, type SuccessStep as a3, type SurveyClassNames as a4, type SurveyStep as a5, type TimingAware as a6, type TimingVariants as a7, type TrialExtensionOffer as a8, buildMessages as a9, defaultMessages as aa, formatMessage as ab, selectTiming as ac, type SurveyStepProps as b, type CloseButtonProps as c, type Appearance as d, type AppearanceVariables as e, type BuiltInOfferConfig as f, type BuiltInStep as g, type BuiltInStepType as h, type CancelFlowMessages as i, type ComponentOverrides as j, type ConfirmClassNames as k, type ConfirmStep as l, type ContactOffer as m, type CustomComponents as n, type CustomOfferConfig as o, type CustomOfferProps as p, type CustomStepConfig as q, type CustomStepProps as r, type DirectCoupon as s, type DirectCustomer as t, type DirectPrice as u, type DirectSubscription as v, type DirectSubscriptionItem as w, type DiscountOffer as x, type FeedbackClassNames as y, type FeedbackStep as z };
|
package/dist/styles.css
CHANGED
|
@@ -275,6 +275,19 @@
|
|
|
275
275
|
margin: 0 0 20px;
|
|
276
276
|
}
|
|
277
277
|
|
|
278
|
+
/* Embedded video (YouTube/Vimeo/Wistia/Brightcove iframes) authored into a
|
|
279
|
+
step description. Overrides the inline height="480" the TipTap YouTube node
|
|
280
|
+
emits and makes the player fill the width at a 16:9 box. */
|
|
281
|
+
.ck-cancel-flow .ck-step-description :is(iframe, video),
|
|
282
|
+
.ck-cancel-flow .ck-step-description [data-youtube-video] {
|
|
283
|
+
display: block;
|
|
284
|
+
width: 100%;
|
|
285
|
+
height: auto;
|
|
286
|
+
aspect-ratio: 16 / 9;
|
|
287
|
+
border: 0;
|
|
288
|
+
border-radius: 10px;
|
|
289
|
+
}
|
|
290
|
+
|
|
278
291
|
/* --- Buttons --- */
|
|
279
292
|
.ck-cancel-flow .ck-button {
|
|
280
293
|
display: inline-flex;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@churnkey/react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Production-ready cancel flow for React. Drop-in component, headless hook, or full customization. Works standalone or with Churnkey for AI-powered retention.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|