@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.
Files changed (36) hide show
  1. package/dist/{chunk-WTFB6T53.cjs → chunk-4ZAJYXG2.cjs} +7 -6
  2. package/dist/chunk-4ZAJYXG2.cjs.map +1 -0
  3. package/dist/{chunk-4WYZSTRI.js → chunk-BODEVZ3K.js} +189 -10
  4. package/dist/chunk-BODEVZ3K.js.map +1 -0
  5. package/dist/{chunk-EYIGZLVT.js → chunk-GH5EKETT.js} +6 -3
  6. package/dist/chunk-GH5EKETT.js.map +1 -0
  7. package/dist/{chunk-OC5H64F4.js → chunk-KS3RWFRH.js} +4 -3
  8. package/dist/chunk-KS3RWFRH.js.map +1 -0
  9. package/dist/{chunk-WVSNF3TN.cjs → chunk-M6Z3BHEH.cjs} +6 -2
  10. package/dist/chunk-M6Z3BHEH.cjs.map +1 -0
  11. package/dist/{chunk-WGS432VT.cjs → chunk-SWEPUHIN.cjs} +192 -9
  12. package/dist/chunk-SWEPUHIN.cjs.map +1 -0
  13. package/dist/core.cjs +30 -14
  14. package/dist/core.d.cts +22 -6
  15. package/dist/core.d.ts +22 -6
  16. package/dist/core.js +2 -2
  17. package/dist/headless.cjs +3 -3
  18. package/dist/headless.d.cts +4 -1
  19. package/dist/headless.d.ts +4 -1
  20. package/dist/headless.js +2 -2
  21. package/dist/index.cjs +235 -161
  22. package/dist/index.cjs.map +1 -1
  23. package/dist/index.d.cts +14 -14
  24. package/dist/index.d.ts +14 -14
  25. package/dist/index.js +136 -78
  26. package/dist/index.js.map +1 -1
  27. package/dist/{step-graph-BpoAhd0d.d.cts → step-graph-s1L1wV-P.d.cts} +184 -2
  28. package/dist/{step-graph-BpoAhd0d.d.ts → step-graph-s1L1wV-P.d.ts} +184 -2
  29. package/dist/styles.css +13 -0
  30. package/package.json +1 -1
  31. package/dist/chunk-4WYZSTRI.js.map +0 -1
  32. package/dist/chunk-EYIGZLVT.js.map +0 -1
  33. package/dist/chunk-OC5H64F4.js.map +0 -1
  34. package/dist/chunk-WGS432VT.cjs.map +0 -1
  35. package/dist/chunk-WTFB6T53.cjs.map +0 -1
  36. package/dist/chunk-WVSNF3TN.cjs.map +0 -1
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
- import { useCancelFlowMachine } from './chunk-OC5H64F4.js';
2
- export { useCancelFlow } from './chunk-OC5H64F4.js';
3
- import { discountPhrase, formatMonthDayLong, formatPriceFromMinor, formatMonthDay } from './chunk-EYIGZLVT.js';
4
- export { calculateDiscountedPrice, formatPeriodEnd, formatPrice } from './chunk-EYIGZLVT.js';
5
- import { cn, appearanceToStyle, defaultTitles, BUILT_IN_OFFER_TYPES } from './chunk-4WYZSTRI.js';
6
- export { AnalyticsClient, BUILT_IN_OFFER_TYPES, BUILT_IN_STEP_TYPES, CancelFlowMachine, ChurnkeyApi, appearanceToStyle, cn, decodeSessionToken, defaultTitles } from './chunk-4WYZSTRI.js';
1
+ import { useCancelFlowMachine } from './chunk-KS3RWFRH.js';
2
+ export { useCancelFlow } from './chunk-KS3RWFRH.js';
3
+ import { discountPhrase, formatMonthDayLong, formatPriceFromMinor, formatMonthDay, formatShortDate, formatPeriodEnd } from './chunk-GH5EKETT.js';
4
+ export { calculateDiscountedPrice, formatPeriodEnd, formatPrice } from './chunk-GH5EKETT.js';
5
+ import { cn, defaultMessages, formatMessage, appearanceToStyle, selectTiming, BUILT_IN_OFFER_TYPES } from './chunk-BODEVZ3K.js';
6
+ export { AnalyticsClient, BUILT_IN_OFFER_TYPES, BUILT_IN_STEP_TYPES, CancelFlowMachine, ChurnkeyApi, appearanceToStyle, buildMessages, cn, decodeSessionToken, defaultMessages, defaultTitles, formatMessage, selectTiming } from './chunk-BODEVZ3K.js';
7
7
  import { useState, useRef, useEffect, useCallback } from 'react';
8
8
  import { jsx, jsxs } from 'react/jsx-runtime';
9
9
 
@@ -25,22 +25,26 @@ function DefaultConfirm({
25
25
  lossesLabel,
26
26
  confirmLabel,
27
27
  goBackLabel,
28
+ periodEndNotice,
28
29
  onConfirm,
29
30
  onGoBack,
30
31
  isProcessing,
31
- classNames
32
+ classNames,
33
+ messages
32
34
  }) {
35
+ const m = messages ?? defaultMessages;
33
36
  const hasLosses = Array.isArray(losses) && losses.length > 0;
34
37
  return /* @__PURE__ */ jsxs("div", { className: cn("ck-step ck-step-confirm", classNames?.root), children: [
35
38
  /* @__PURE__ */ jsx("h2", { className: cn("ck-step-title", classNames?.title), children: title }),
36
- description && /* @__PURE__ */ jsx(RichText, { html: description, className: cn("ck-step-description", classNames?.description) }),
39
+ description && /* @__PURE__ */ jsx(RichText, { as: "div", html: description, className: cn("ck-step-description", classNames?.description) }),
37
40
  hasLosses && /* @__PURE__ */ jsxs("div", { className: cn("ck-loss-block", classNames?.lossList), children: [
38
- /* @__PURE__ */ jsx("div", { className: cn("ck-loss-label", classNames?.lossLabel), children: lossesLabel ?? "You'll lose access to:" }),
41
+ /* @__PURE__ */ jsx("div", { className: cn("ck-loss-label", classNames?.lossLabel), children: lossesLabel ?? m.confirm.lossesLabel }),
39
42
  /* @__PURE__ */ jsx("ul", { className: "ck-loss-list", children: losses.map((item) => /* @__PURE__ */ jsxs("li", { className: cn("ck-loss-item", classNames?.lossItem), children: [
40
43
  /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: cn("ck-loss-bullet", classNames?.lossBullet), children: "\u25CF" }),
41
44
  /* @__PURE__ */ jsx("span", { children: item })
42
45
  ] }, item)) })
43
46
  ] }),
47
+ periodEndNotice && /* @__PURE__ */ jsx("p", { className: cn("ck-period-end", classNames?.periodEndNotice), children: periodEndNotice }),
44
48
  /* @__PURE__ */ jsx(
45
49
  "button",
46
50
  {
@@ -48,7 +52,7 @@ function DefaultConfirm({
48
52
  className: cn("ck-button ck-button-danger", classNames?.confirmButton),
49
53
  onClick: onConfirm,
50
54
  disabled: isProcessing,
51
- children: isProcessing ? "Processing..." : confirmLabel
55
+ children: isProcessing ? m.common.processing : confirmLabel
52
56
  }
53
57
  ),
54
58
  /* @__PURE__ */ jsx("button", { type: "button", className: cn("ck-button-link", classNames?.goBackButton), onClick: onGoBack, children: goBackLabel })
@@ -63,16 +67,18 @@ function DefaultFeedback({
63
67
  value,
64
68
  onChange,
65
69
  onSubmit,
66
- classNames
70
+ classNames,
71
+ messages
67
72
  }) {
73
+ const m = messages ?? defaultMessages;
68
74
  const [focused, setFocused] = useState(false);
69
75
  const hasMin = minLength > 0;
70
76
  const isUnderMin = hasMin && value.length > 0 && value.length < minLength;
71
77
  const isValid = !required || value.length >= minLength;
72
- const placeholderText = placeholder ?? (hasMin ? `At least ${minLength} characters\u2026` : "Type your thoughts\u2026");
78
+ const placeholderText = placeholder ?? (hasMin ? formatMessage(m.feedback.placeholderWithMin, { minLength }) : m.feedback.placeholder);
73
79
  return /* @__PURE__ */ jsxs("div", { className: cn("ck-step ck-step-feedback", classNames?.root), children: [
74
80
  /* @__PURE__ */ jsx("h2", { className: cn("ck-step-title", classNames?.title), children: title }),
75
- description && /* @__PURE__ */ jsx(RichText, { html: description, className: cn("ck-step-description", classNames?.description) }),
81
+ description && /* @__PURE__ */ jsx(RichText, { as: "div", html: description, className: cn("ck-step-description", classNames?.description) }),
76
82
  /* @__PURE__ */ jsxs(
77
83
  "div",
78
84
  {
@@ -119,7 +125,7 @@ function DefaultFeedback({
119
125
  className: cn("ck-button ck-button-primary", classNames?.submitButton),
120
126
  onClick: onSubmit,
121
127
  disabled: !isValid,
122
- children: "Continue"
128
+ children: m.common.continue
123
129
  }
124
130
  )
125
131
  ] });
@@ -131,13 +137,15 @@ function DefaultContactOffer({
131
137
  onAccept,
132
138
  onDecline,
133
139
  isProcessing,
134
- classNames
140
+ classNames,
141
+ messages
135
142
  }) {
143
+ const msg = messages ?? defaultMessages;
136
144
  const headline = title ?? offer.copy.headline;
137
145
  const body = description ?? offer.copy.body;
138
146
  return /* @__PURE__ */ jsxs("div", { className: cn("ck-step ck-step-offer", classNames?.root), children: [
139
147
  headline && /* @__PURE__ */ jsx("h2", { className: cn("ck-step-title", classNames?.title), children: headline }),
140
- body && /* @__PURE__ */ jsx(RichText, { html: body, className: cn("ck-step-description", classNames?.description) }),
148
+ body && /* @__PURE__ */ jsx(RichText, { as: "div", html: body, className: cn("ck-step-description", classNames?.description) }),
141
149
  /* @__PURE__ */ jsxs("div", { className: cn("ck-offer-card", classNames?.card), children: [
142
150
  /* @__PURE__ */ jsx(
143
151
  "button",
@@ -146,7 +154,7 @@ function DefaultContactOffer({
146
154
  className: cn("ck-button ck-button-primary", classNames?.acceptButton),
147
155
  onClick: () => onAccept(),
148
156
  disabled: isProcessing,
149
- children: isProcessing ? "Processing..." : offer.copy.cta
157
+ children: isProcessing ? msg.common.processing : offer.copy.cta
150
158
  }
151
159
  ),
152
160
  /* @__PURE__ */ jsx("button", { type: "button", className: cn("ck-button-link", classNames?.declineButton), onClick: onDecline, children: offer.copy.declineCta })
@@ -160,18 +168,20 @@ function DefaultDiscountOffer({
160
168
  onAccept,
161
169
  onDecline,
162
170
  isProcessing,
163
- classNames
171
+ classNames,
172
+ messages
164
173
  }) {
174
+ const msg = messages ?? defaultMessages;
165
175
  const o = offer;
166
176
  const headline = title ?? offer.copy.headline;
167
177
  const body = description ?? offer.copy.body;
168
178
  const phrase = discountPhrase(o);
169
179
  return /* @__PURE__ */ jsxs("div", { className: cn("ck-step ck-step-offer", classNames?.root), children: [
170
180
  headline && /* @__PURE__ */ jsx("h2", { className: cn("ck-step-title", classNames?.title), children: headline }),
171
- body && /* @__PURE__ */ jsx(RichText, { html: body, className: cn("ck-step-description", classNames?.description) }),
181
+ body && /* @__PURE__ */ jsx(RichText, { as: "div", html: body, className: cn("ck-step-description", classNames?.description) }),
172
182
  /* @__PURE__ */ jsxs("div", { className: cn("ck-offer-card", classNames?.card), children: [
173
183
  /* @__PURE__ */ jsxs("div", { className: "ck-offer-details ck-offer-discount", children: [
174
- /* @__PURE__ */ jsx("div", { className: "ck-offer-discount-eyebrow", children: "Limited-time offer" }),
184
+ /* @__PURE__ */ jsx("div", { className: "ck-offer-discount-eyebrow", children: msg.offer.limitedTimeEyebrow }),
175
185
  /* @__PURE__ */ jsx("div", { className: "ck-offer-discount-phrase", children: phrase })
176
186
  ] }),
177
187
  /* @__PURE__ */ jsx(
@@ -181,7 +191,7 @@ function DefaultDiscountOffer({
181
191
  className: cn("ck-button ck-button-primary", classNames?.acceptButton),
182
192
  onClick: () => onAccept(),
183
193
  disabled: isProcessing,
184
- children: isProcessing ? "Processing..." : offer.copy.cta
194
+ children: isProcessing ? msg.common.processing : offer.copy.cta
185
195
  }
186
196
  ),
187
197
  /* @__PURE__ */ jsx("button", { type: "button", className: cn("ck-button-link", classNames?.declineButton), onClick: onDecline, children: offer.copy.declineCta })
@@ -195,8 +205,10 @@ function DefaultPauseOffer({
195
205
  onAccept,
196
206
  onDecline,
197
207
  isProcessing,
198
- classNames
208
+ classNames,
209
+ messages
199
210
  }) {
211
+ const msg = messages ?? defaultMessages;
200
212
  const o = offer;
201
213
  const max = Math.max(1, o.months);
202
214
  const [months, setMonths] = useState(1);
@@ -207,9 +219,9 @@ function DefaultPauseOffer({
207
219
  const resumeDate = formatMonthDayLong(resume);
208
220
  return /* @__PURE__ */ jsxs("div", { className: cn("ck-step ck-step-offer", classNames?.root), children: [
209
221
  headline && /* @__PURE__ */ jsx("h2", { className: cn("ck-step-title", classNames?.title), children: headline }),
210
- body && /* @__PURE__ */ jsx(RichText, { html: body, className: cn("ck-step-description", classNames?.description) }),
222
+ body && /* @__PURE__ */ jsx(RichText, { as: "div", html: body, className: cn("ck-step-description", classNames?.description) }),
211
223
  /* @__PURE__ */ jsxs("div", { className: cn("ck-offer-card ck-pause-card", classNames?.card), children: [
212
- /* @__PURE__ */ jsx("div", { className: "ck-pause-eyebrow", children: "We'll see you back on" }),
224
+ /* @__PURE__ */ jsx("div", { className: "ck-pause-eyebrow", children: msg.offer.pauseEyebrow }),
213
225
  /* @__PURE__ */ jsx("div", { className: "ck-pause-date", children: resumeDate }),
214
226
  max > 1 && /* @__PURE__ */ jsx("div", { className: cn("ck-pause-chips", classNames?.pauseSlider), children: Array.from({ length: max }, (_, i) => i + 1).map((m) => /* @__PURE__ */ jsxs(
215
227
  "button",
@@ -221,7 +233,7 @@ function DefaultPauseOffer({
221
233
  children: [
222
234
  m,
223
235
  " ",
224
- m === 1 ? "month" : "months"
236
+ m === 1 ? msg.common.month : msg.common.months
225
237
  ]
226
238
  },
227
239
  m
@@ -234,7 +246,7 @@ function DefaultPauseOffer({
234
246
  className: cn("ck-button ck-button-primary", classNames?.acceptButton),
235
247
  onClick: () => onAccept({ months }),
236
248
  disabled: isProcessing,
237
- children: isProcessing ? "Processing..." : offer.copy.cta
249
+ children: isProcessing ? msg.common.processing : offer.copy.cta
238
250
  }
239
251
  ),
240
252
  /* @__PURE__ */ jsx("button", { type: "button", className: cn("ck-button-link", classNames?.declineButton), onClick: onDecline, children: offer.copy.declineCta })
@@ -251,8 +263,10 @@ function DefaultPlanChangeOffer({
251
263
  onAccept,
252
264
  onDecline,
253
265
  isProcessing,
254
- classNames
266
+ classNames,
267
+ messages
255
268
  }) {
269
+ const msg = messages ?? defaultMessages;
256
270
  const o = offer;
257
271
  const plans = o.plans ?? [];
258
272
  const currentPlanId = subscriptions[0]?.items[0]?.price.id;
@@ -261,10 +275,10 @@ function DefaultPlanChangeOffer({
261
275
  const selectedPlan = plans.find((p) => p.id === selectedPlanId) ?? null;
262
276
  const headline = title ?? offer.copy.headline;
263
277
  const body = description ?? offer.copy.body;
264
- const ctaLabel = isProcessing ? "Processing..." : selectedPlan?.name ? `Switch to ${selectedPlan.name}` : offer.copy.cta;
278
+ const ctaLabel = isProcessing ? msg.common.processing : selectedPlan?.name ? formatMessage(msg.offer.switchToCta, { planName: selectedPlan.name }) : offer.copy.cta;
265
279
  return /* @__PURE__ */ jsxs("div", { className: cn("ck-step ck-step-offer", classNames?.root), children: [
266
280
  headline && /* @__PURE__ */ jsx("h2", { className: cn("ck-step-title", classNames?.title), children: headline }),
267
- body && /* @__PURE__ */ jsx(RichText, { html: body, className: cn("ck-step-description", classNames?.description) }),
281
+ body && /* @__PURE__ */ jsx(RichText, { as: "div", html: body, className: cn("ck-step-description", classNames?.description) }),
268
282
  /* @__PURE__ */ jsxs("div", { className: cn("ck-offer-card", classNames?.card), children: [
269
283
  /* @__PURE__ */ jsx("div", { className: "ck-offer-details ck-plan-grid", children: plans.map((plan) => {
270
284
  const interval = plan.duration?.interval ?? "month";
@@ -286,7 +300,7 @@ function DefaultPlanChangeOffer({
286
300
  children: [
287
301
  /* @__PURE__ */ jsxs("div", { className: "ck-plan-name", children: [
288
302
  plan.name ?? plan.id,
289
- isCurrent && /* @__PURE__ */ jsx("span", { className: "ck-plan-current-badge", children: "Current" })
303
+ isCurrent && /* @__PURE__ */ jsx("span", { className: "ck-plan-current-badge", children: msg.offer.currentPlanBadge })
290
304
  ] }),
291
305
  plan.tagline && /* @__PURE__ */ jsx("div", { className: "ck-plan-tagline", children: plan.tagline }),
292
306
  /* @__PURE__ */ jsxs("div", { className: "ck-plan-price-row", children: [
@@ -327,8 +341,10 @@ function DefaultRebateOffer({
327
341
  onAccept,
328
342
  onDecline,
329
343
  isProcessing,
330
- classNames
344
+ classNames,
345
+ messages
331
346
  }) {
347
+ const msg = messages ?? defaultMessages;
332
348
  const o = offer;
333
349
  const headline = title ?? offer.copy.headline;
334
350
  const body = description ?? offer.copy.body;
@@ -338,20 +354,19 @@ function DefaultRebateOffer({
338
354
  const taxRefunded = refund - amount;
339
355
  return /* @__PURE__ */ jsxs("div", { className: cn("ck-step ck-step-offer", classNames?.root), children: [
340
356
  headline && /* @__PURE__ */ jsx("h2", { className: cn("ck-step-title", classNames?.title), children: headline }),
341
- body && /* @__PURE__ */ jsx(RichText, { html: body, className: cn("ck-step-description", classNames?.description) }),
357
+ body && /* @__PURE__ */ jsx(RichText, { as: "div", html: body, className: cn("ck-step-description", classNames?.description) }),
342
358
  /* @__PURE__ */ jsxs("div", { className: cn("ck-offer-card", classNames?.card), children: [
343
359
  /* @__PURE__ */ jsxs("div", { className: "ck-offer-rebate", children: [
344
360
  o.amountPaidMinor != null && /* @__PURE__ */ jsxs("div", { className: "ck-offer-rebate-row", children: [
345
- /* @__PURE__ */ jsx("span", { children: "You paid this period" }),
361
+ /* @__PURE__ */ jsx("span", { children: msg.offer.rebate.paidLabel }),
346
362
  /* @__PURE__ */ jsx("span", { children: formatPriceFromMinor(o.amountPaidMinor, currency) })
347
363
  ] }),
348
364
  /* @__PURE__ */ jsxs("div", { className: "ck-offer-rebate-row ck-offer-rebate-credit", children: [
349
365
  /* @__PURE__ */ jsxs("span", { children: [
350
- "Money back",
366
+ msg.offer.rebate.moneyBackLabel,
351
367
  taxRefunded > 0 && /* @__PURE__ */ jsxs("span", { className: "ck-offer-rebate-tax", children: [
352
- " (incl. ",
353
- formatPriceFromMinor(taxRefunded, currency),
354
- " tax)"
368
+ " ",
369
+ formatMessage(msg.offer.rebate.inclTax, { amount: formatPriceFromMinor(taxRefunded, currency) })
355
370
  ] })
356
371
  ] }),
357
372
  /* @__PURE__ */ jsxs("span", { children: [
@@ -360,7 +375,7 @@ function DefaultRebateOffer({
360
375
  ] })
361
376
  ] }),
362
377
  o.netAfterRebateMinor != null && /* @__PURE__ */ jsxs("div", { className: "ck-offer-rebate-row ck-offer-rebate-total", children: [
363
- /* @__PURE__ */ jsx("span", { children: "Your net for this period" }),
378
+ /* @__PURE__ */ jsx("span", { children: msg.offer.rebate.netLabel }),
364
379
  /* @__PURE__ */ jsx("span", { children: formatPriceFromMinor(o.netAfterRebateMinor, currency) })
365
380
  ] })
366
381
  ] }),
@@ -371,7 +386,7 @@ function DefaultRebateOffer({
371
386
  className: cn("ck-button ck-button-primary", classNames?.acceptButton),
372
387
  onClick: () => onAccept(),
373
388
  disabled: isProcessing,
374
- children: isProcessing ? "Processing..." : offer.copy.cta
389
+ children: isProcessing ? msg.common.processing : offer.copy.cta
375
390
  }
376
391
  ),
377
392
  /* @__PURE__ */ jsx("button", { type: "button", className: cn("ck-button-link", classNames?.declineButton), onClick: onDecline, children: offer.copy.declineCta })
@@ -384,7 +399,7 @@ function DefaultRedirectOffer({ title, description, offer, onDecline, classNames
384
399
  const body = description ?? offer.copy.body;
385
400
  return /* @__PURE__ */ jsxs("div", { className: cn("ck-step ck-step-offer", classNames?.root), children: [
386
401
  headline && /* @__PURE__ */ jsx("h2", { className: cn("ck-step-title", classNames?.title), children: headline }),
387
- body && /* @__PURE__ */ jsx(RichText, { html: body, className: cn("ck-step-description", classNames?.description) }),
402
+ body && /* @__PURE__ */ jsx(RichText, { as: "div", html: body, className: cn("ck-step-description", classNames?.description) }),
388
403
  /* @__PURE__ */ jsxs("div", { className: cn("ck-offer-card", classNames?.card), children: [
389
404
  /* @__PURE__ */ jsx("div", { className: "ck-offer-details", children: /* @__PURE__ */ jsxs("a", { href: url, target: "_blank", rel: "noopener noreferrer", className: "ck-redirect-link", children: [
390
405
  /* @__PURE__ */ jsx("span", { children: offer.copy.cta }),
@@ -423,8 +438,10 @@ function DefaultTrialExtensionOffer({
423
438
  onAccept,
424
439
  onDecline,
425
440
  isProcessing,
426
- classNames
441
+ classNames,
442
+ messages
427
443
  }) {
444
+ const msg = messages ?? defaultMessages;
428
445
  const o = offer;
429
446
  const headline = title ?? offer.copy.headline;
430
447
  const body = description ?? offer.copy.body;
@@ -433,7 +450,7 @@ function DefaultTrialExtensionOffer({
433
450
  const newEnd = formatMonthDay(end);
434
451
  return /* @__PURE__ */ jsxs("div", { className: cn("ck-step ck-step-offer", classNames?.root), children: [
435
452
  headline && /* @__PURE__ */ jsx("h2", { className: cn("ck-step-title", classNames?.title), children: headline }),
436
- body && /* @__PURE__ */ jsx(RichText, { html: body, className: cn("ck-step-description", classNames?.description) }),
453
+ body && /* @__PURE__ */ jsx(RichText, { as: "div", html: body, className: cn("ck-step-description", classNames?.description) }),
437
454
  /* @__PURE__ */ jsxs("div", { className: cn("ck-offer-card", classNames?.card), children: [
438
455
  /* @__PURE__ */ jsxs("div", { className: "ck-offer-details ck-trial-block", children: [
439
456
  /* @__PURE__ */ jsxs("div", { className: "ck-trial-badge", children: [
@@ -441,10 +458,10 @@ function DefaultTrialExtensionOffer({
441
458
  "+",
442
459
  o.days
443
460
  ] }),
444
- /* @__PURE__ */ jsx("div", { className: "ck-trial-unit", children: o.days === 1 ? "day" : "days" })
461
+ /* @__PURE__ */ jsx("div", { className: "ck-trial-unit", children: o.days === 1 ? msg.common.day : msg.common.days })
445
462
  ] }),
446
463
  /* @__PURE__ */ jsxs("div", { children: [
447
- /* @__PURE__ */ jsx("div", { className: "ck-trial-end-label", children: "New end date" }),
464
+ /* @__PURE__ */ jsx("div", { className: "ck-trial-end-label", children: msg.offer.newEndDateLabel }),
448
465
  /* @__PURE__ */ jsx("div", { className: "ck-trial-end-date", children: newEnd })
449
466
  ] })
450
467
  ] }),
@@ -455,7 +472,7 @@ function DefaultTrialExtensionOffer({
455
472
  className: cn("ck-button ck-button-primary", classNames?.acceptButton),
456
473
  onClick: () => onAccept(),
457
474
  disabled: isProcessing,
458
- children: isProcessing ? "Processing..." : offer.copy.cta
475
+ children: isProcessing ? msg.common.processing : offer.copy.cta
459
476
  }
460
477
  ),
461
478
  /* @__PURE__ */ jsx("button", { type: "button", className: cn("ck-button-link", classNames?.declineButton), onClick: onDecline, children: offer.copy.declineCta })
@@ -494,12 +511,13 @@ function pickOfferComponent(type, components) {
494
511
  return null;
495
512
  }
496
513
  }
497
- function DefaultSuccess({ title, description, onClose, classNames }) {
514
+ function DefaultSuccess({ title, description, onClose, classNames, messages }) {
515
+ const m = messages ?? defaultMessages;
498
516
  return /* @__PURE__ */ jsxs("div", { className: cn("ck-step ck-step-success", classNames?.root), children: [
499
517
  /* @__PURE__ */ jsx("div", { className: cn("ck-success-icon", classNames?.icon), children: /* @__PURE__ */ jsx(Checkmark, { color: "currentColor", size: 26 }) }),
500
518
  /* @__PURE__ */ jsx("h2", { className: cn("ck-step-title", classNames?.title), children: title }),
501
- description && /* @__PURE__ */ jsx(RichText, { html: description, className: cn("ck-step-description", classNames?.description) }),
502
- /* @__PURE__ */ jsx("div", { className: "ck-success-actions", children: /* @__PURE__ */ jsx("button", { type: "button", className: cn("ck-button ck-button-primary", classNames?.closeButton), onClick: onClose, children: "Done" }) })
519
+ description && /* @__PURE__ */ jsx(RichText, { as: "div", html: description, className: cn("ck-step-description", classNames?.description) }),
520
+ /* @__PURE__ */ jsx("div", { className: "ck-success-actions", children: /* @__PURE__ */ jsx("button", { type: "button", className: cn("ck-button ck-button-primary", classNames?.closeButton), onClick: onClose, children: m.common.done }) })
503
521
  ] });
504
522
  }
505
523
  function DefaultReasonButton({ reason, index, isSelected, onSelect }) {
@@ -529,14 +547,16 @@ function DefaultSurvey({
529
547
  onFollowupResponseChange,
530
548
  onNext,
531
549
  classNames,
532
- components
550
+ components,
551
+ messages
533
552
  }) {
553
+ const m = messages ?? defaultMessages;
534
554
  const ReasonButton = components?.ReasonButton ?? DefaultReasonButton;
535
555
  const selected = reasons.find((r) => r.id === selectedReason);
536
556
  const showFollowup = selected?.freeform === true;
537
557
  return /* @__PURE__ */ jsxs("div", { className: cn("ck-step ck-step-survey", classNames?.root), children: [
538
558
  /* @__PURE__ */ jsx("h2", { className: cn("ck-step-title", classNames?.title), children: title }),
539
- description && /* @__PURE__ */ jsx(RichText, { html: description, className: cn("ck-step-description", classNames?.description) }),
559
+ description && /* @__PURE__ */ jsx(RichText, { as: "div", html: description, className: cn("ck-step-description", classNames?.description) }),
540
560
  /* @__PURE__ */ jsx("div", { className: cn("ck-reason-list", classNames?.reasonList), role: "radiogroup", "aria-label": title, children: reasons.map((reason, i) => /* @__PURE__ */ jsx(
541
561
  ReasonButton,
542
562
  {
@@ -551,11 +571,11 @@ function DefaultSurvey({
551
571
  "textarea",
552
572
  {
553
573
  className: cn("ck-reason-followup", classNames?.followupInput),
554
- placeholder: "Tell us more (optional)",
574
+ placeholder: m.survey.followupPlaceholder,
555
575
  rows: 3,
556
576
  value: followupResponse,
557
577
  onChange: (e) => onFollowupResponseChange(e.target.value),
558
- "aria-label": "Additional detail"
578
+ "aria-label": m.survey.followupAriaLabel
559
579
  }
560
580
  ),
561
581
  /* @__PURE__ */ jsx(
@@ -565,12 +585,12 @@ function DefaultSurvey({
565
585
  className: cn("ck-button ck-button-primary", classNames?.continueButton),
566
586
  onClick: onNext,
567
587
  disabled: !selectedReason,
568
- children: "Continue"
588
+ children: m.common.continue
569
589
  }
570
590
  )
571
591
  ] });
572
592
  }
573
- function DefaultBackButton({ onBack, className }) {
593
+ function DefaultBackButton({ onBack, className, label }) {
574
594
  return /* @__PURE__ */ jsxs("button", { type: "button", className: cn("ck-back-button", className), onClick: onBack, children: [
575
595
  /* @__PURE__ */ jsx("svg", { width: "14", height: "14", viewBox: "0 0 20 20", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx(
576
596
  "path",
@@ -582,11 +602,11 @@ function DefaultBackButton({ onBack, className }) {
582
602
  strokeLinejoin: "round"
583
603
  }
584
604
  ) }),
585
- /* @__PURE__ */ jsx("span", { children: "Back" })
605
+ /* @__PURE__ */ jsx("span", { children: label ?? "Back" })
586
606
  ] });
587
607
  }
588
- function DefaultCloseButton({ onClose, className }) {
589
- return /* @__PURE__ */ jsx("button", { type: "button", className: cn("ck-close-button", className), onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsx("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M15 5L5 15M5 5l10 10", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }) }) });
608
+ function DefaultCloseButton({ onClose, className, label }) {
609
+ return /* @__PURE__ */ jsx("button", { type: "button", className: cn("ck-close-button", className), onClick: onClose, "aria-label": label ?? "Close", children: /* @__PURE__ */ jsx("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M15 5L5 15M5 5l10 10", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }) }) });
590
610
  }
591
611
  function trapFocus(container) {
592
612
  const focusableSelector = [
@@ -695,7 +715,8 @@ function CancelFlow(props) {
695
715
  onClose: props.onClose,
696
716
  isLoading,
697
717
  loadError,
698
- onRetry: retry
718
+ onRetry: retry,
719
+ messages: machine.messages
699
720
  }
700
721
  );
701
722
  }
@@ -718,7 +739,8 @@ function LoadStatus({
718
739
  onClose,
719
740
  isLoading,
720
741
  loadError,
721
- onRetry
742
+ onRetry,
743
+ messages
722
744
  }) {
723
745
  const scheme = useColorScheme(appearance?.colorScheme);
724
746
  const appearanceStyle = appearanceToStyle(appearance);
@@ -727,7 +749,7 @@ function LoadStatus({
727
749
  const handleClose = onClose ?? (() => {
728
750
  });
729
751
  return /* @__PURE__ */ jsx("div", { className: "ck-cancel-flow", "data-color-scheme": scheme, style: appearanceStyle, children: /* @__PURE__ */ jsxs(Modal, { open: true, onClose: handleClose, className: classNames?.modal, overlayClassName: classNames?.overlay, children: [
730
- /* @__PURE__ */ jsx(CloseButton, { onClose: handleClose, className: classNames?.closeButton }),
752
+ /* @__PURE__ */ jsx(CloseButton, { onClose: handleClose, className: classNames?.closeButton, label: messages.common.close }),
731
753
  /* @__PURE__ */ jsxs("div", { className: "ck-content", children: [
732
754
  isLoading && /* @__PURE__ */ jsxs("div", { className: "ck-loading", style: { padding: "32px", textAlign: "center" }, children: [
733
755
  /* @__PURE__ */ jsx(
@@ -745,10 +767,10 @@ function LoadStatus({
745
767
  }
746
768
  }
747
769
  ),
748
- /* @__PURE__ */ jsx("p", { style: { color: "var(--ck-color-text-secondary, #6b7280)" }, children: "Loading your options..." })
770
+ /* @__PURE__ */ jsx("p", { style: { color: "var(--ck-color-text-secondary, #6b7280)" }, children: messages.common.loading })
749
771
  ] }),
750
772
  loadError && /* @__PURE__ */ jsxs("div", { className: "ck-error", role: "alert", style: { padding: "32px", textAlign: "center" }, children: [
751
- /* @__PURE__ */ jsx("p", { className: "ck-error-message", style: { marginBottom: 16 }, children: "We couldn't load your cancellation options. Please try again." }),
773
+ /* @__PURE__ */ jsx("p", { className: "ck-error-message", style: { marginBottom: 16 }, children: messages.common.loadError }),
752
774
  /* @__PURE__ */ jsx(
753
775
  "button",
754
776
  {
@@ -765,7 +787,7 @@ function LoadStatus({
765
787
  borderRadius: "var(--ck-border-radius, 8px)",
766
788
  cursor: "pointer"
767
789
  },
768
- children: "Try again"
790
+ children: messages.common.tryAgain
769
791
  }
770
792
  )
771
793
  ] })
@@ -775,14 +797,15 @@ function LoadStatus({
775
797
  function FlowShell({ machine, state, appearance, classNames, components, customComponents }) {
776
798
  const scheme = useColorScheme(appearance?.colorScheme);
777
799
  const appearanceStyle = appearanceToStyle(appearance);
800
+ const messages = machine.messages;
778
801
  const Modal = components?.Modal ?? DefaultModal;
779
802
  const CloseButton = components?.CloseButton ?? DefaultCloseButton;
780
803
  const BackButton = components?.BackButton ?? DefaultBackButton;
781
804
  return /* @__PURE__ */ jsx("div", { className: "ck-cancel-flow", "data-color-scheme": scheme, style: appearanceStyle, children: /* @__PURE__ */ jsxs(Modal, { open: true, onClose: machine.close, className: classNames?.modal, overlayClassName: classNames?.overlay, children: [
782
- /* @__PURE__ */ jsx(CloseButton, { onClose: machine.close, className: classNames?.closeButton }),
805
+ /* @__PURE__ */ jsx(CloseButton, { onClose: machine.close, className: classNames?.closeButton, label: messages.common.close }),
783
806
  /* @__PURE__ */ jsxs("div", { className: "ck-content", children: [
784
- machine.canGoBack && /* @__PURE__ */ jsx(BackButton, { onBack: machine.back, className: classNames?.backButton }),
785
- state.error && /* @__PURE__ */ jsx("div", { className: "ck-error", role: "alert", children: /* @__PURE__ */ jsx("p", { className: "ck-error-message", children: "Something went wrong. Please try again." }) }),
807
+ machine.canGoBack && /* @__PURE__ */ jsx(BackButton, { onBack: machine.back, className: classNames?.backButton, label: messages.common.back }),
808
+ state.error && /* @__PURE__ */ jsx("div", { className: "ck-error", role: "alert", children: /* @__PURE__ */ jsx("p", { className: "ck-error-message", children: messages.common.error }) }),
786
809
  /* @__PURE__ */ jsx(StepRenderer, { state, machine, components, customComponents })
787
810
  ] })
788
811
  ] }) });
@@ -794,6 +817,7 @@ function StepRenderer({
794
817
  customComponents
795
818
  }) {
796
819
  const stepConfig = machine.currentStep;
820
+ const messages = machine.messages;
797
821
  switch (state.step) {
798
822
  case "survey": {
799
823
  const Survey = components?.Survey ?? DefaultSurvey;
@@ -801,7 +825,7 @@ function StepRenderer({
801
825
  return /* @__PURE__ */ jsx(
802
826
  Survey,
803
827
  {
804
- title: config?.title ?? defaultTitles.survey,
828
+ title: config?.title ?? messages.survey.title,
805
829
  description: config?.description,
806
830
  customer: state.customer,
807
831
  subscriptions: state.subscriptions,
@@ -812,7 +836,8 @@ function StepRenderer({
812
836
  onFollowupResponseChange: machine.setFollowupResponse,
813
837
  onNext: machine.next,
814
838
  classNames: config?.classNames,
815
- components
839
+ components,
840
+ messages
816
841
  }
817
842
  );
818
843
  }
@@ -850,7 +875,8 @@ function StepRenderer({
850
875
  onDecline: machine.decline,
851
876
  isProcessing: state.isProcessing,
852
877
  classNames: config?.classNames,
853
- components
878
+ components,
879
+ messages
854
880
  }
855
881
  );
856
882
  }
@@ -860,7 +886,7 @@ function StepRenderer({
860
886
  return /* @__PURE__ */ jsx(
861
887
  Feedback,
862
888
  {
863
- title: config?.title ?? defaultTitles.feedback,
889
+ title: config?.title ?? messages.feedback.title,
864
890
  description: config?.description,
865
891
  customer: state.customer,
866
892
  subscriptions: state.subscriptions,
@@ -870,7 +896,8 @@ function StepRenderer({
870
896
  value: state.feedback,
871
897
  onChange: machine.setFeedback,
872
898
  onSubmit: machine.next,
873
- classNames: config?.classNames
899
+ classNames: config?.classNames,
900
+ messages
874
901
  }
875
902
  );
876
903
  }
@@ -880,18 +907,20 @@ function StepRenderer({
880
907
  return /* @__PURE__ */ jsx(
881
908
  Confirm,
882
909
  {
883
- title: config?.title ?? defaultTitles.confirm,
910
+ title: config?.title ?? messages.confirm.title,
884
911
  description: config?.description,
885
912
  customer: state.customer,
886
913
  subscriptions: state.subscriptions,
887
914
  losses: config?.losses,
888
915
  lossesLabel: config?.lossesLabel,
889
- confirmLabel: config?.confirmLabel ?? "Cancel subscription",
890
- goBackLabel: config?.goBackLabel ?? "Go back",
916
+ confirmLabel: config?.confirmLabel ?? selectTiming(messages.confirm.cta, state.cancelAtPeriodEnd),
917
+ goBackLabel: config?.goBackLabel ?? messages.confirm.goBack,
918
+ periodEndNotice: resolvePeriodEndNotice(state, messages),
891
919
  onConfirm: machine.cancel,
892
920
  onGoBack: machine.back,
893
921
  isProcessing: state.isProcessing,
894
- classNames: config?.classNames
922
+ classNames: config?.classNames,
923
+ messages
895
924
  }
896
925
  );
897
926
  }
@@ -899,17 +928,20 @@ function StepRenderer({
899
928
  const Success = components?.Success ?? DefaultSuccess;
900
929
  const config = stepConfig;
901
930
  const isSaved = state.outcome === "saved";
931
+ const perOffer = isSaved ? savedOfferCopy(messages, state.acceptedOffer) : void 0;
902
932
  return /* @__PURE__ */ jsx(
903
933
  Success,
904
934
  {
905
935
  outcome: state.outcome ?? "cancelled",
906
936
  offer: machine.currentOffer ?? void 0,
907
- title: isSaved ? config?.savedTitle ?? "Welcome back!" : config?.cancelledTitle ?? "Subscription cancelled",
908
- description: isSaved ? config?.savedDescription ?? "Your offer has been applied." : config?.cancelledDescription ?? "We're sorry to see you go.",
937
+ acceptedOffer: state.acceptedOffer ?? void 0,
938
+ title: isSaved ? config?.savedTitle ?? perOffer?.title ?? messages.success.saved.title : config?.cancelledTitle ?? selectTiming(messages.success.cancelled.title, state.cancelAtPeriodEnd),
939
+ description: isSaved ? config?.savedDescription ?? pauseResumeDescription(messages, state.acceptedOffer) ?? perOffer?.description ?? messages.success.saved.description : config?.cancelledDescription ?? selectTiming(messages.success.cancelled.description, state.cancelAtPeriodEnd),
909
940
  customer: state.customer,
910
941
  subscriptions: state.subscriptions,
911
942
  onClose: machine.close,
912
- classNames: config?.classNames
943
+ classNames: config?.classNames,
944
+ messages
913
945
  }
914
946
  );
915
947
  }
@@ -937,6 +969,32 @@ function StepRenderer({
937
969
  }
938
970
  }
939
971
  }
972
+ var SAVED_OFFER_COPY_TYPES = ["discount", "pause", "trial_extension", "plan_change", "rebate"];
973
+ function savedOfferCopy(messages, offer) {
974
+ if (!offer) return void 0;
975
+ if (!SAVED_OFFER_COPY_TYPES.includes(offer.type)) return void 0;
976
+ return messages.success.saved[offer.type];
977
+ }
978
+ function pauseResumeDescription(messages, offer) {
979
+ if (offer?.type !== "pause") return void 0;
980
+ const months = offer.result?.months;
981
+ if (typeof months !== "number" || months < 1) return void 0;
982
+ const resume = /* @__PURE__ */ new Date();
983
+ if (offer.interval === "week") {
984
+ resume.setDate(resume.getDate() + months * 7);
985
+ } else {
986
+ resume.setMonth(resume.getMonth() + months);
987
+ }
988
+ return formatMessage(messages.success.saved.pause.resumeDescription, { resumeDate: formatShortDate(resume) });
989
+ }
990
+ function resolvePeriodEndNotice(state, messages) {
991
+ if (state.cancelAtPeriodEnd !== true) return void 0;
992
+ const template = selectTiming(messages.confirm.periodEndNotice, state.cancelAtPeriodEnd);
993
+ if (!template) return void 0;
994
+ const periodEnd = formatPeriodEnd(state.subscriptions);
995
+ if (!periodEnd) return void 0;
996
+ return formatMessage(template, { periodEnd });
997
+ }
940
998
  function UnregisteredStepFallback({ step, onSkip }) {
941
999
  useEffect(() => {
942
1000
  console.warn(`[churnkey] No component registered for step type "${step}". Skipping.`);