@mrrocket/sdk 0.13.0 → 0.15.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 (45) hide show
  1. package/CHANGELOG.md +8 -1
  2. package/README.md +43 -3
  3. package/dist/api-client.d.ts +62 -1
  4. package/dist/api-client.js +28 -1
  5. package/dist/api-client.js.map +1 -1
  6. package/dist/browser/mrrocket-flows.js +11 -2
  7. package/dist/browser/mrrocket-messenger.js +304 -70
  8. package/dist/browser/mrrocket.js +1 -1
  9. package/dist/browser.d.ts +1 -1
  10. package/dist/browser.js +2 -1
  11. package/dist/browser.js.map +1 -1
  12. package/dist/client.d.ts +12 -0
  13. package/dist/client.js +22 -0
  14. package/dist/client.js.map +1 -1
  15. package/dist/context.d.ts +3 -0
  16. package/dist/context.js.map +1 -1
  17. package/dist/core.d.ts +4 -1
  18. package/dist/core.js +1 -1
  19. package/dist/core.js.map +1 -1
  20. package/dist/flows-ui.d.ts +6 -4
  21. package/dist/flows-ui.js +147 -5
  22. package/dist/flows-ui.js.map +1 -1
  23. package/dist/flows.d.ts +51 -1
  24. package/dist/flows.js +62 -21
  25. package/dist/flows.js.map +1 -1
  26. package/dist/index.d.ts +2 -1
  27. package/dist/index.js +4 -0
  28. package/dist/index.js.map +1 -1
  29. package/dist/messenger-ui.d.ts +37 -2
  30. package/dist/messenger-ui.js +597 -109
  31. package/dist/messenger-ui.js.map +1 -1
  32. package/dist/messenger.d.ts +11 -3
  33. package/dist/messenger.js +6 -3
  34. package/dist/messenger.js.map +1 -1
  35. package/dist/paywall.d.ts +123 -0
  36. package/dist/paywall.js +131 -0
  37. package/dist/paywall.js.map +1 -0
  38. package/dist/react-native/index.d.cts +235 -8
  39. package/dist/react-native/index.d.ts +235 -8
  40. package/dist/react-native/index.js +650 -184
  41. package/dist/react-native/index.js.map +1 -1
  42. package/dist/styles-css.d.ts +1 -1
  43. package/dist/styles-css.js +302 -68
  44. package/dist/styles-css.js.map +1 -1
  45. package/package.json +5 -1
package/dist/flows-ui.js CHANGED
@@ -59,6 +59,15 @@ var FLOWS_CSS = `
59
59
  .ckf-banner{position:fixed;z-index:2147483001;left:50%;top:12px;transform:translateX(-50%);width:min(640px,calc(100vw - 24px));background:var(--ckf-bg);border-radius:12px;box-shadow:var(--ckf-shadow);padding:10px 10px 10px 16px;display:flex;align-items:center;gap:12px;animation:ckf-in .18s ease-out}
60
60
  .ckf-banner>span{flex:1;min-width:0;display:flex;flex-direction:column}
61
61
  .ckf-banner .ckf-btn{height:32px}
62
+ .ckf-pw{max-width:420px;gap:14px}
63
+ .ckf-root[data-still] :is(.ckf-scrim,.ckf-card){animation:none}
64
+ .ckf-li{display:flex;gap:10px;align-items:flex-start;margin:0}
65
+ .ckf-li::before{content:"\u2713";flex:none;width:20px;height:20px;border-radius:50%;background:color-mix(in srgb,var(--ckf-accent) 14%,var(--ckf-bg));color:var(--ckf-accent);font-size:12px;font-weight:700;display:inline-flex;align-items:center;justify-content:center}
66
+ .ckf-quote{margin:0;padding:12px 14px;border-radius:12px;background:var(--ckf-soft)}
67
+ .ckf-quote cite{display:block;margin-top:6px;font-style:normal;font-size:12px;color:var(--ckf-muted)}
68
+ .ckf-plan{justify-content:space-between;min-height:52px}
69
+ .ckf-plan span:nth-child(2){display:flex;flex-direction:column;margin-right:auto}
70
+ .ckf-plan b{font-variant-numeric:tabular-nums}
62
71
  @keyframes ckf-fade{from{opacity:0}}
63
72
  @keyframes ckf-in{from{opacity:0;transform:translateY(6px)}}
64
73
  .ckf-banner{animation-name:ckf-fade}
@@ -93,10 +102,8 @@ var dots = (step, steps) => steps > 1 ? h(
93
102
  { className: "ckf-dots", role: "img", "aria-label": `Step ${step} of ${steps}` },
94
103
  ...Array.from({ length: steps }, (_, i) => h("span", { className: "ckf-dot", "data-on": i < step }))
95
104
  ) : h("span");
96
- var screenKey = (v) => v.type === "cancel" ? `c:${v.screen}` : v.type === "step" ? `s:${v.step.id}` : v.type === "checklist" ? `l:${v.flow.id}` : `m:${v.message.id}`;
97
- var mountFlows = (ctl, theme = {}) => {
98
- if (typeof document === "undefined") return () => {
99
- };
105
+ var screenKey = (v) => v.type === "cancel" ? `c:${v.screen}:${v.round ?? 1}` : v.type === "step" ? `s:${v.step.id}` : v.type === "checklist" ? `l:${v.flow.id}` : `m:${v.message.id}`;
106
+ var rootFor = (theme) => {
100
107
  if (!document.getElementById(STYLE_ID)) {
101
108
  const style = document.createElement("style");
102
109
  style.id = STYLE_ID;
@@ -106,6 +113,12 @@ var mountFlows = (ctl, theme = {}) => {
106
113
  const root = h("div", { className: `ckf-root${theme.className ? ` ${theme.className}` : ""}`, "data-theme": theme.theme ?? "auto" });
107
114
  if (theme.accent) root.style.setProperty("--ckf-accent", theme.accent);
108
115
  document.body.appendChild(root);
116
+ return root;
117
+ };
118
+ var mountFlows = (ctl, theme = {}) => {
119
+ if (typeof document === "undefined") return () => {
120
+ };
121
+ const root = rootFor(theme);
109
122
  let lastKey = "";
110
123
  let returnFocus = null;
111
124
  let comment = "";
@@ -143,6 +156,53 @@ var mountFlows = (ctl, theme = {}) => {
143
156
  button("Never mind, keep my plan", "quiet", ctl.cancel.close)
144
157
  )
145
158
  );
159
+ } else if (v.screen === "followup" && v.followUp) {
160
+ const f = v.followUp;
161
+ let field;
162
+ if (f.answers.length)
163
+ field = h(
164
+ "div",
165
+ { className: "ckf-opts", role: "radiogroup", "aria-labelledby": titleId },
166
+ ...f.answers.map(
167
+ (a, i) => h(
168
+ "button",
169
+ {
170
+ type: "button",
171
+ role: "radio",
172
+ className: "ckf-opt",
173
+ "aria-checked": String(v.answer === a),
174
+ "data-ckf-id": `a:${i}`,
175
+ onClick: () => ctl.cancel.answer(a)
176
+ },
177
+ h("span", { className: "ckf-radio" }),
178
+ a
179
+ )
180
+ )
181
+ );
182
+ else {
183
+ const text = h("textarea", {
184
+ className: "ckf-text",
185
+ placeholder: "Your answer (optional)",
186
+ "aria-labelledby": titleId,
187
+ maxlength: "500",
188
+ "data-ckf-id": "answer",
189
+ onInput: (e) => ctl.cancel.answer(e.target.value)
190
+ });
191
+ text.value = v.answer ?? "";
192
+ field = text;
193
+ }
194
+ const label = v.flow.reasons.find((r) => r.id === v.reason)?.label;
195
+ body.push(
196
+ label ? h("p", { className: "ckf-small" }, `You said: ${label}`) : false,
197
+ h("h2", { className: "ckf-h", id: titleId }, f.question),
198
+ field,
199
+ h(
200
+ "div",
201
+ { className: "ckf-btns" },
202
+ button("Continue", "primary", ctl.cancel.next, { id: "go" }),
203
+ button("Never mind, keep my plan", "quiet", ctl.cancel.close)
204
+ )
205
+ );
146
206
  } else if (v.screen === "offer" && v.offer) {
147
207
  const o = v.offer;
148
208
  const label = v.flow.reasons.find((r) => r.id === v.reason)?.label;
@@ -389,8 +449,90 @@ var mountFlows = (ctl, theme = {}) => {
389
449
  root.remove();
390
450
  };
391
451
  };
452
+ var mountPaywall = (host, theme = {}) => {
453
+ if (typeof document === "undefined") return () => {
454
+ };
455
+ const root = rootFor(theme);
456
+ let lastKey = "";
457
+ let returnFocus = null;
458
+ const draw = () => {
459
+ const v = host.view();
460
+ const key = v ? `${v.paywall.id}:${v.index}` : "";
461
+ if (key && !lastKey && document.activeElement instanceof HTMLElement) returnFocus = document.activeElement;
462
+ if (!v) {
463
+ root.replaceChildren();
464
+ root.removeAttribute("data-still");
465
+ if (lastKey) returnFocus?.focus();
466
+ lastKey = "";
467
+ return;
468
+ }
469
+ if (lastKey) root.setAttribute("data-still", "");
470
+ const p = v.page;
471
+ const plans = p.kind === "plans" ? h(
472
+ "div",
473
+ { className: "ckf-opts", role: "radiogroup", "aria-label": "Plans" },
474
+ ...v.plans.map(
475
+ (plan) => h(
476
+ "button",
477
+ {
478
+ type: "button",
479
+ role: "radio",
480
+ className: "ckf-opt ckf-plan",
481
+ "aria-checked": String(v.plan === plan.id),
482
+ "data-ckf-id": `p:${plan.id}`,
483
+ onClick: () => host.pick(plan.id)
484
+ },
485
+ h("span", { className: "ckf-radio" }),
486
+ h("span", {}, plan.name, plan.note ? h("span", { className: "ckf-small" }, plan.note) : false),
487
+ h("b", {}, plan.price)
488
+ )
489
+ )
490
+ ) : false;
491
+ root.replaceChildren(
492
+ h(
493
+ "div",
494
+ { className: "ckf-scrim", onClick: (e) => e.target === e.currentTarget && host.close() },
495
+ h(
496
+ "div",
497
+ { className: "ckf-card ckf-pw", role: "dialog", "aria-modal": "true", "aria-labelledby": "ckf-title" },
498
+ h("div", { className: "ckf-top" }, dots(v.index + 1, v.total), closeButton("Close", host.close)),
499
+ h("h2", { className: "ckf-h", id: "ckf-title" }, p.title),
500
+ p.body ? h("p", { className: "ckf-p" }, p.body) : false,
501
+ p.kind === "benefits" && p.items?.length ? h("div", { className: "ckf-opts" }, ...p.items.map((i) => h("p", { className: "ckf-li" }, i))) : false,
502
+ p.kind === "proof" && p.quotes?.length ? h("div", { className: "ckf-opts" }, ...p.quotes.map((q) => h("blockquote", { className: "ckf-quote" }, q.text, q.by ? h("cite", {}, q.by) : false))) : false,
503
+ plans,
504
+ v.error ? h("p", { className: "ckf-err", role: "alert" }, v.error) : false,
505
+ h(
506
+ "div",
507
+ { className: "ckf-btns" },
508
+ button(v.busy ? "One moment\u2026" : p.button || "Continue", "primary", host.next, {
509
+ disabled: v.busy || p.kind === "plans" && !v.plan,
510
+ id: "go"
511
+ }),
512
+ v.index ? button("Back", "quiet", host.back, { disabled: v.busy }) : false
513
+ )
514
+ )
515
+ )
516
+ );
517
+ if (key !== lastKey) root.querySelector("[data-ckf-id=go]")?.focus();
518
+ lastKey = key;
519
+ };
520
+ const onKey = (e) => {
521
+ if (!host.view()) return;
522
+ if (e.key === "Escape") host.close();
523
+ };
524
+ document.addEventListener("keydown", onKey);
525
+ const off = host.subscribe(draw);
526
+ draw();
527
+ return () => {
528
+ off();
529
+ document.removeEventListener("keydown", onKey);
530
+ root.remove();
531
+ };
532
+ };
392
533
  export {
393
534
  FLOWS_CSS,
394
- mountFlows
535
+ mountFlows,
536
+ mountPaywall
395
537
  };
396
538
  //# sourceMappingURL=flows-ui.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/flows-ui.ts"],"sourcesContent":["/**\n * The web renderer of the cancel flow, onboarding and in-app messages (flows.ts): plain DOM, no React, so the Next.js\n * provider and the script tag share it. Both load it only when something is about to show (a dynamic import, and\n * `sdk-flows.js` for the script tag), so pages that show nothing never download it.\n *\n * Themeable three ways: `theme` (\"light\", \"dark\", \"auto\") and `accent` from the SDK's options, and CSS variables on\n * `.ckf-root` in the site's own stylesheet (`--ckf-accent`, `--ckf-bg`, `--ckf-fg`, `--ckf-muted`, `--ckf-line`,\n * `--ckf-radius`, `--ckf-font`). Every text is set with textContent; nothing from the server is parsed as HTML.\n */\nimport { type FlowView, type FlowsController, discounted, money, offerButton } from \"./flows\";\n\nexport interface FlowsTheme {\n theme?: \"light\" | \"dark\" | \"auto\";\n /** The main color of buttons and highlights, like \"#3f76ff\". */\n accent?: string;\n /** An extra class on the root, for the site's own CSS. */\n className?: string;\n}\n\nconst STYLE_ID = \"ckf-styles\";\n\nexport const FLOWS_CSS = `\n.ckf-root{--ckf-accent:#3f76ff;--ckf-accent-ink:#fff;--ckf-bg:#fff;--ckf-fg:#1c1d20;--ckf-muted:#5d6068;--ckf-soft:#f4f5f7;--ckf-line:#e4e5e8;--ckf-danger:#d92d20;--ckf-radius:14px;--ckf-font:inherit;--ckf-shadow:0 16px 40px -12px rgba(16,18,24,.28),0 0 0 1px rgba(16,18,24,.07)}\n.ckf-root[data-theme=dark]{--ckf-bg:#17181b;--ckf-fg:#f1f2f4;--ckf-muted:#a9acb3;--ckf-soft:#232428;--ckf-line:#2e2f35;--ckf-shadow:0 16px 40px -12px rgba(0,0,0,.6),0 0 0 1px rgba(255,255,255,.08)}\n@media (prefers-color-scheme:dark){.ckf-root[data-theme=auto]{--ckf-bg:#17181b;--ckf-fg:#f1f2f4;--ckf-muted:#a9acb3;--ckf-soft:#232428;--ckf-line:#2e2f35;--ckf-shadow:0 16px 40px -12px rgba(0,0,0,.6),0 0 0 1px rgba(255,255,255,.08)}}\n.ckf-root,.ckf-root *{box-sizing:border-box}\n.ckf-root{font-family:var(--ckf-font);font-size:14px;line-height:1.45;color:var(--ckf-fg);-webkit-font-smoothing:antialiased}\n.ckf-root :where(button,textarea){font:inherit;color:inherit;margin:0}\n.ckf-root :focus-visible{outline:2px solid var(--ckf-accent);outline-offset:2px}\n.ckf-scrim{position:fixed;inset:0;z-index:2147483002;background:rgba(0,0,0,.4);display:flex;align-items:center;justify-content:center;padding:16px;animation:ckf-fade .15s ease-out}\n.ckf-card{position:relative;width:100%;max-width:400px;max-height:calc(100dvh - 32px);overflow-y:auto;background:var(--ckf-bg);color:var(--ckf-fg);border-radius:var(--ckf-radius);box-shadow:var(--ckf-shadow);padding:18px 20px 20px;display:flex;flex-direction:column;gap:12px;animation:ckf-in .18s ease-out}\n.ckf-float{position:fixed;z-index:2147483001;right:20px;bottom:20px;width:340px;max-width:calc(100vw - 24px)}\n.ckf-top{display:flex;align-items:center;gap:8px;min-height:20px}\n.ckf-dots{display:flex;gap:4px}\n.ckf-dot{width:16px;height:4px;border-radius:4px;background:var(--ckf-line)}\n.ckf-dot[data-on]{background:var(--ckf-accent)}\n.ckf-x{margin-left:auto;width:28px;height:28px;border:0;border-radius:8px;background:none;color:var(--ckf-muted);cursor:pointer;display:inline-flex;align-items:center;justify-content:center;font-size:18px;line-height:1}\n.ckf-x:hover{background:var(--ckf-soft);color:var(--ckf-fg)}\n.ckf-h{margin:0;font-size:17px;font-weight:650;letter-spacing:-.01em;text-wrap:balance}\n.ckf-p{margin:0;color:var(--ckf-muted);text-wrap:pretty;white-space:pre-wrap}\n.ckf-small{margin:0;font-size:12px;color:var(--ckf-muted)}\n.ckf-opts{display:flex;flex-direction:column;gap:6px;margin:0;padding:0;list-style:none}\n.ckf-opt{display:flex;align-items:center;gap:10px;width:100%;min-height:40px;padding:8px 12px;border:1px solid var(--ckf-line);border-radius:10px;background:var(--ckf-bg);text-align:left;cursor:pointer;font-weight:500}\n.ckf-opt:hover{background:var(--ckf-soft)}\n.ckf-opt[aria-checked=true]{border-color:var(--ckf-accent);background:color-mix(in srgb,var(--ckf-accent) 8%,var(--ckf-bg))}\n.ckf-radio{width:16px;height:16px;flex:none;border-radius:50%;border:1.5px solid var(--ckf-line);display:inline-flex;align-items:center;justify-content:center}\n.ckf-opt[aria-checked=true] .ckf-radio{border-color:var(--ckf-accent)}\n.ckf-opt[aria-checked=true] .ckf-radio::after{content:\"\";width:8px;height:8px;border-radius:50%;background:var(--ckf-accent)}\n.ckf-box{border-radius:10px;background:var(--ckf-soft);padding:12px;display:flex;align-items:baseline;gap:8px;flex-wrap:wrap}\n.ckf-big{font-size:22px;font-weight:650;font-variant-numeric:tabular-nums}\n.ckf-strike{color:var(--ckf-muted);text-decoration:line-through;font-variant-numeric:tabular-nums}\n.ckf-text{width:100%;min-height:64px;resize:vertical;border:1px solid var(--ckf-line);border-radius:10px;background:var(--ckf-bg);padding:8px 10px}\n.ckf-text::placeholder{color:var(--ckf-muted)}\n.ckf-btns{display:flex;flex-direction:column;gap:6px}\n.ckf-btn{height:38px;padding:0 14px;border-radius:10px;border:1px solid var(--ckf-line);background:var(--ckf-bg);font-weight:600;cursor:pointer;transition:filter .1s linear,background-color .1s linear}\n.ckf-btn:hover{background:var(--ckf-soft)}\n.ckf-btn[disabled]{opacity:.55;cursor:not-allowed}\n.ckf-primary{background:var(--ckf-accent);border-color:var(--ckf-accent);color:var(--ckf-accent-ink)}\n.ckf-primary:hover{background:var(--ckf-accent);filter:brightness(1.07)}\n.ckf-danger{background:var(--ckf-danger);border-color:var(--ckf-danger);color:#fff}\n.ckf-danger:hover{background:var(--ckf-danger);filter:brightness(1.07)}\n.ckf-quiet{border-color:transparent;background:none;color:var(--ckf-muted);height:30px;font-weight:500}\n.ckf-quiet:hover{background:none;color:var(--ckf-fg)}\n.ckf-err{margin:0;color:var(--ckf-danger);font-size:13px;font-weight:500}\n.ckf-row{display:flex;justify-content:flex-end;gap:8px;align-items:center}\n.ckf-row .ckf-small{margin-right:auto}\n.ckf-ring{position:fixed;z-index:2147483000;pointer-events:none;border-radius:10px;box-shadow:0 0 0 2px var(--ckf-accent),0 0 0 6px color-mix(in srgb,var(--ckf-accent) 25%,transparent);transition:all .15s ease-out}\n.ckf-check{display:flex;align-items:flex-start;gap:10px;width:100%;padding:8px 6px;border:0;border-radius:8px;background:none;text-align:left}\n.ckf-check:where(button){cursor:pointer}\n.ckf-check:where(button):hover{background:var(--ckf-soft)}\n.ckf-tick{width:18px;height:18px;flex:none;margin-top:1px;border-radius:50%;border:1.5px solid var(--ckf-line);display:inline-flex;align-items:center;justify-content:center;color:var(--ckf-accent-ink);font-size:11px;font-weight:700}\n.ckf-check[data-done] .ckf-tick{background:var(--ckf-accent);border-color:var(--ckf-accent)}\n.ckf-check[data-done] b{color:var(--ckf-muted);text-decoration:line-through}\n.ckf-check span{display:flex;flex-direction:column;gap:2px;min-width:0}\n.ckf-bar{height:4px;border-radius:4px;background:var(--ckf-soft);overflow:hidden}\n.ckf-bar i{display:block;height:100%;background:var(--ckf-accent)}\n.ckf-banner{position:fixed;z-index:2147483001;left:50%;top:12px;transform:translateX(-50%);width:min(640px,calc(100vw - 24px));background:var(--ckf-bg);border-radius:12px;box-shadow:var(--ckf-shadow);padding:10px 10px 10px 16px;display:flex;align-items:center;gap:12px;animation:ckf-in .18s ease-out}\n.ckf-banner>span{flex:1;min-width:0;display:flex;flex-direction:column}\n.ckf-banner .ckf-btn{height:32px}\n@keyframes ckf-fade{from{opacity:0}}\n@keyframes ckf-in{from{opacity:0;transform:translateY(6px)}}\n.ckf-banner{animation-name:ckf-fade}\n@media (max-width:520px){.ckf-scrim{align-items:flex-end;padding:0}.ckf-scrim .ckf-card{max-width:none;border-radius:var(--ckf-radius) var(--ckf-radius) 0 0;max-height:92dvh}.ckf-float{right:12px;bottom:12px}}\n@media (prefers-reduced-motion:reduce){.ckf-root *{animation:none!important;transition:none!important}}\n`;\n\ntype Attrs = Record<string, string | boolean | undefined | ((e: Event) => void)>;\n\n/** A small element builder: attributes, `on*` handlers and children (strings become text, never HTML). */\nconst h = (tag: string, attrs: Attrs = {}, ...children: (Node | string | false | null | undefined)[]) => {\n const el = document.createElement(tag);\n for (const [k, v] of Object.entries(attrs)) {\n if (v === undefined || v === false) continue;\n if (typeof v === \"function\") el.addEventListener(k.slice(2).toLowerCase(), v);\n else if (k === \"className\") el.className = String(v);\n else el.setAttribute(k, v === true ? \"\" : v);\n }\n for (const c of children) if (c || c === \"\") el.append(typeof c === \"string\" ? document.createTextNode(c) : c);\n return el;\n};\n\nconst button = (label: string, kind: \"primary\" | \"danger\" | \"secondary\" | \"quiet\", onClick: () => void, opts: { disabled?: boolean; id?: string } = {}) =>\n h(\n \"button\",\n {\n type: \"button\",\n className: `ckf-btn${kind === \"secondary\" ? \"\" : ` ckf-${kind}`}`,\n disabled: !!opts.disabled,\n \"data-ckf-id\": opts.id,\n onClick: () => onClick(),\n },\n label,\n );\n\nconst closeButton = (label: string, onClick: () => void) =>\n h(\"button\", { type: \"button\", className: \"ckf-x\", \"aria-label\": label, \"data-ckf-id\": \"x\", onClick: () => onClick() }, \"×\");\n\nconst dots = (step: number, steps: number) =>\n steps > 1\n ? h(\n \"span\",\n { className: \"ckf-dots\", role: \"img\", \"aria-label\": `Step ${step} of ${steps}` },\n ...Array.from({ length: steps }, (_, i) => h(\"span\", { className: \"ckf-dot\", \"data-on\": i < step })),\n )\n : h(\"span\");\n\n/** A key that changes when the screen (not just its state) changes: then focus moves to the new screen. */\nconst screenKey = (v: FlowView) =>\n v.type === \"cancel\" ? `c:${v.screen}` : v.type === \"step\" ? `s:${v.step.id}` : v.type === \"checklist\" ? `l:${v.flow.id}` : `m:${v.message.id}`;\n\n/**\n * Draws the controller's view into the page until the returned function is called. One root element under <body>;\n * nothing is drawn while there is nothing to show.\n */\nexport const mountFlows = (ctl: FlowsController, theme: FlowsTheme = {}): (() => void) => {\n if (typeof document === \"undefined\") return () => {};\n if (!document.getElementById(STYLE_ID)) {\n const style = document.createElement(\"style\");\n style.id = STYLE_ID;\n style.textContent = FLOWS_CSS;\n document.head.appendChild(style);\n }\n const root = h(\"div\", { className: `ckf-root${theme.className ? ` ${theme.className}` : \"\"}`, \"data-theme\": theme.theme ?? \"auto\" });\n if (theme.accent) root.style.setProperty(\"--ckf-accent\", theme.accent);\n document.body.appendChild(root);\n\n let lastKey = \"\";\n let returnFocus: HTMLElement | null = null;\n let comment = \"\";\n let placed: { float: HTMLElement; ring: HTMLElement; target: HTMLElement } | null = null;\n\n // ---------- Cancel flow ----------\n\n const cancelCard = (v: Extract<FlowView, { type: \"cancel\" }>) => {\n const titleId = \"ckf-title\";\n const busy = v.busy;\n const body: (Node | false)[] = [];\n if (v.screen === \"survey\") {\n body.push(\n h(\"h2\", { className: \"ckf-h\", id: titleId }, v.flow.question || \"Why are you leaving?\"),\n h(\n \"div\",\n { className: \"ckf-opts\", role: \"radiogroup\", \"aria-labelledby\": titleId },\n ...v.flow.reasons.map((r) =>\n h(\n \"button\",\n {\n type: \"button\",\n role: \"radio\",\n className: \"ckf-opt\",\n \"aria-checked\": String(v.reason === r.id),\n \"data-ckf-id\": `r:${r.id}`,\n onClick: () => ctl.cancel.pick(r.id),\n },\n h(\"span\", { className: \"ckf-radio\" }),\n r.label,\n ),\n ),\n ),\n h(\n \"div\",\n { className: \"ckf-btns\" },\n button(\"Continue\", \"primary\", ctl.cancel.next, { disabled: !v.reason, id: \"go\" }),\n button(\"Never mind, keep my plan\", \"quiet\", ctl.cancel.close),\n ),\n );\n } else if (v.screen === \"offer\" && v.offer) {\n const o = v.offer;\n const label = v.flow.reasons.find((r) => r.id === v.reason)?.label;\n let detail: Node | false = false;\n if (o.kind === \"discount\" && o.percent)\n detail = v.price\n ? h(\n \"div\",\n { className: \"ckf-box\" },\n h(\"span\", { className: \"ckf-big\" }, money(discounted(v.price.amount, o.percent), v.price.currency)),\n h(\"span\", { className: \"ckf-strike\" }, money(v.price.amount, v.price.currency)),\n h(\n \"span\",\n { className: \"ckf-small\" },\n `/${v.price.interval ?? \"month\"}${o.months ? ` for ${o.months} ${o.months === 1 ? \"month\" : \"months\"}` : \"\"}`,\n ),\n )\n : h(\n \"div\",\n { className: \"ckf-box\" },\n h(\"span\", { className: \"ckf-big\" }, `${o.percent}% off`),\n o.months ? h(\"span\", { className: \"ckf-small\" }, `for ${o.months} ${o.months === 1 ? \"month\" : \"months\"}`) : false,\n );\n if (o.kind === \"pause\" && o.months)\n detail = h(\n \"div\",\n { className: \"ckf-box\" },\n h(\"span\", {}, `Pause for up to ${o.months} ${o.months === 1 ? \"month\" : \"months\"}. Your data stays.`),\n );\n if (o.kind === \"downgrade\" && o.planName) detail = h(\"div\", { className: \"ckf-box\" }, h(\"b\", {}, o.planName));\n const text =\n o.kind === \"contact\"\n ? (h(\"textarea\", {\n className: \"ckf-text\",\n placeholder: \"What would make you stay?\",\n \"aria-label\": \"Your message\",\n maxlength: \"500\",\n \"data-ckf-id\": \"comment\",\n onInput: (e) => (comment = (e.target as HTMLTextAreaElement).value),\n }) as HTMLTextAreaElement)\n : null;\n if (text) text.value = comment;\n body.push(\n label ? h(\"p\", { className: \"ckf-small\" }, `You said: ${label}`) : false,\n h(\"h2\", { className: \"ckf-h\", id: titleId }, o.headline),\n detail,\n text ?? false,\n h(\n \"div\",\n { className: \"ckf-btns\" },\n button(busy ? \"One moment…\" : offerButton(o), \"primary\", () => ctl.cancel.accept(comment), { disabled: busy, id: \"go\" }),\n button(\"No thanks, continue cancelling\", \"quiet\", ctl.cancel.decline, { disabled: busy }),\n ),\n );\n } else {\n const plain = v.steps === 1;\n const text = plain\n ? null\n : (h(\"textarea\", {\n className: \"ckf-text\",\n placeholder: \"Anything else we should know? (optional)\",\n \"aria-label\": \"Anything else we should know? (optional)\",\n maxlength: \"500\",\n \"data-ckf-id\": \"comment\",\n onInput: (e) => (comment = (e.target as HTMLTextAreaElement).value),\n }) as HTMLTextAreaElement);\n if (text) text.value = comment;\n body.push(\n h(\"h2\", { className: \"ckf-h\", id: titleId }, v.flow.confirm.title),\n v.flow.confirm.body ? h(\"p\", { className: \"ckf-p\" }, v.flow.confirm.body) : false,\n text ?? false,\n h(\n \"div\",\n { className: \"ckf-btns\" },\n button(busy ? \"One moment…\" : v.flow.confirm.button, \"danger\", () => ctl.cancel.confirm(comment), { disabled: busy, id: \"go\" }),\n button(\"Keep my plan\", \"secondary\", ctl.cancel.close, { disabled: busy }),\n ),\n );\n }\n return h(\n \"div\",\n { className: \"ckf-scrim\", onClick: (e) => e.target === e.currentTarget && !busy && ctl.cancel.close() },\n h(\n \"div\",\n { className: \"ckf-card\", role: \"dialog\", \"aria-modal\": \"true\", \"aria-labelledby\": titleId },\n h(\"div\", { className: \"ckf-top\" }, dots(v.step, v.steps), closeButton(\"Close\", ctl.cancel.close)),\n ...body,\n v.error ? h(\"p\", { className: \"ckf-err\", role: \"alert\" }, v.error) : false,\n ),\n );\n };\n\n // ---------- Onboarding ----------\n\n const stepCard = (v: Extract<FlowView, { type: \"step\" }>) => {\n const s = v.step;\n const card = h(\n \"div\",\n { className: \"ckf-card\", role: \"dialog\", \"aria-modal\": s.kind === \"modal\" ? \"true\" : undefined, \"aria-labelledby\": \"ckf-title\" },\n h(\n \"div\",\n { className: \"ckf-top\" },\n v.total > 1 ? h(\"p\", { className: \"ckf-small\" }, `Step ${v.index + 1} of ${v.total}`) : h(\"span\"),\n closeButton(\"Close the tour\", ctl.onboarding.dismiss),\n ),\n h(\"h2\", { className: \"ckf-h\", id: \"ckf-title\" }, s.title),\n s.body ? h(\"p\", { className: \"ckf-p\" }, s.body) : false,\n h(\"div\", { className: \"ckf-row\" }, button(s.button || (v.last ? \"Done\" : \"Next\"), \"primary\", ctl.onboarding.next, { id: \"go\" })),\n );\n if (s.kind === \"modal\") return h(\"div\", { className: \"ckf-scrim\" }, card);\n const target = s.target ? findTarget(s.target) : null;\n const float = h(\"div\", { className: \"ckf-float\" }, card);\n if (target) {\n const ring = h(\"div\", { className: \"ckf-ring\", \"aria-hidden\": \"true\" });\n placed = { float, ring, target };\n place(float, ring, target);\n return h(\"div\", {}, ring, float);\n }\n return float;\n };\n\n /**\n * The element a tour step points at: a CSS selector (`#new-project`, `.toolbar button`, picked on the site in\n * MRRocket), or a plain name for `data-ck-target=\"…\"`. A selector that does not parse falls back to the name.\n */\n const findTarget = (t: string): HTMLElement | null => {\n const named = () => document.querySelector<HTMLElement>(`[data-ck-target=\"${CSS.escape(t)}\"]`);\n if (!/^[#.[]|[\\s>:=]/.test(t)) return named();\n return safeQuery(t) ?? named();\n };\n const safeQuery = (sel: string) => {\n try {\n return document.querySelector<HTMLElement>(sel);\n } catch {\n return null;\n }\n };\n\n /** Puts a tour card next to the element it points at (below it, or above when there is no room). */\n const place = (float: HTMLElement, ring: HTMLElement, target: HTMLElement) => {\n const r = target.getBoundingClientRect();\n Object.assign(ring.style, { left: `${r.left - 4}px`, top: `${r.top - 4}px`, width: `${r.width + 8}px`, height: `${r.height + 8}px` });\n const w = Math.min(340, window.innerWidth - 24);\n const left = Math.max(12, Math.min(r.left, window.innerWidth - w - 12));\n const below = r.bottom + 12;\n Object.assign(float.style, { left: `${left}px`, right: \"auto\", width: `${w}px` });\n if (below + 200 < window.innerHeight) Object.assign(float.style, { top: `${below}px`, bottom: \"auto\" });\n else Object.assign(float.style, { top: \"auto\", bottom: `${Math.max(12, window.innerHeight - r.top + 12)}px` });\n };\n\n const checklist = (v: Extract<FlowView, { type: \"checklist\" }>) => {\n const done = v.items.filter((i) => i.done).length;\n return h(\n \"div\",\n { className: \"ckf-float\" },\n h(\n \"div\",\n { className: \"ckf-card\", role: \"region\", \"aria-labelledby\": \"ckf-title\" },\n h(\n \"div\",\n { className: \"ckf-top\" },\n h(\"h2\", { className: \"ckf-h\", id: \"ckf-title\" }, v.flow.name),\n closeButton(\"Hide the checklist\", ctl.onboarding.dismiss),\n ),\n h(\"p\", { className: \"ckf-small\" }, `${done} of ${v.items.length} done`),\n h(\n \"div\",\n { className: \"ckf-bar\", role: \"progressbar\", \"aria-valuemin\": \"0\", \"aria-valuemax\": String(v.items.length), \"aria-valuenow\": String(done) },\n h(\"i\", { style: `width:${(done / v.items.length) * 100}%` } as Attrs),\n ),\n h(\n \"div\",\n { className: \"ckf-opts\" },\n ...v.items.map(({ step, done: d }) => {\n const inner = [\n h(\"span\", { className: \"ckf-tick\", \"aria-hidden\": \"true\" }, d ? \"✓\" : \"\"),\n h(\"span\", {}, h(\"b\", {}, step.title), step.body ? h(\"span\", { className: \"ckf-small\" }, step.body) : false),\n ];\n // An item with an event ticks itself when the app records it; the others are ticked by hand.\n return d || step.event\n ? h(\"div\", { className: \"ckf-check\", \"data-done\": d, role: \"listitem\" }, ...inner)\n : h(\n \"button\",\n {\n type: \"button\",\n className: \"ckf-check\",\n \"aria-label\": `Mark “${step.title}” done`,\n \"data-ckf-id\": `i:${step.id}`,\n onClick: () => ctl.onboarding.check(step.id),\n },\n ...inner,\n );\n }),\n ),\n ),\n );\n };\n\n // ---------- Messages ----------\n\n const message = (v: Extract<FlowView, { type: \"message\" }>) => {\n const m = v.message;\n const action = m.button ? button(m.button, \"primary\", ctl.message.click, { id: \"go\" }) : false;\n if (m.style === \"banner\")\n return h(\n \"div\",\n { className: \"ckf-banner\", role: \"status\" },\n h(\"span\", {}, h(\"b\", {}, m.title), m.body ? h(\"span\", { className: \"ckf-small\" }, m.body) : false),\n action,\n closeButton(\"Dismiss\", ctl.message.dismiss),\n );\n return h(\n \"div\",\n { className: \"ckf-scrim\", onClick: (e) => e.target === e.currentTarget && ctl.message.dismiss() },\n h(\n \"div\",\n { className: \"ckf-card\", role: \"dialog\", \"aria-modal\": \"true\", \"aria-labelledby\": \"ckf-title\" },\n h(\"div\", { className: \"ckf-top\" }, h(\"span\"), closeButton(\"Dismiss\", ctl.message.dismiss)),\n h(\"h2\", { className: \"ckf-h\", id: \"ckf-title\" }, m.title),\n m.body ? h(\"p\", { className: \"ckf-p\" }, m.body) : false,\n action ? h(\"div\", { className: \"ckf-row\" }, action) : false,\n ),\n );\n };\n\n // ---------- Drawing ----------\n\n const draw = () => {\n const v = ctl.view();\n const key = v ? screenKey(v) : \"\";\n const focused = (document.activeElement as HTMLElement | null)?.closest?.(\".ckf-root\") ? document.activeElement?.getAttribute(\"data-ckf-id\") : null;\n if (key && !lastKey && document.activeElement instanceof HTMLElement && !document.activeElement.closest(\".ckf-root\"))\n returnFocus = document.activeElement;\n if (key !== lastKey && !key.startsWith(\"c:\")) comment = \"\";\n if (!key) comment = \"\";\n placed = null;\n root.replaceChildren(\n ...(v ? [v.type === \"cancel\" ? cancelCard(v) : v.type === \"step\" ? stepCard(v) : v.type === \"checklist\" ? checklist(v) : message(v)] : []),\n );\n if (key !== lastKey) {\n // A new screen: focus its first control (a dialog's), so keyboard and screen reader users land in it.\n const modal = root.querySelector<HTMLElement>(\"[aria-modal=true]\");\n const first = modal?.querySelector<HTMLElement>(\"[role=radio], [data-ckf-id=go], textarea\") ?? null;\n first?.focus();\n if (!key && returnFocus) {\n returnFocus.focus();\n returnFocus = null;\n }\n } else if (focused) root.querySelector<HTMLElement>(`[data-ckf-id=\"${CSS.escape(focused)}\"]`)?.focus();\n lastKey = key;\n };\n\n const onKey = (e: KeyboardEvent) => {\n if (e.key !== \"Escape\") return;\n const v = ctl.view();\n if (v?.type === \"cancel\") ctl.cancel.close();\n else if (v?.type === \"message\") ctl.message.dismiss();\n else if (v?.type === \"step\" && v.step.kind === \"modal\") ctl.onboarding.dismiss();\n };\n // A tour card follows its element when the page scrolls or resizes.\n const follow = () => {\n if (placed) place(placed.float, placed.ring, placed.target);\n };\n document.addEventListener(\"keydown\", onKey);\n window.addEventListener(\"resize\", follow);\n window.addEventListener(\"scroll\", follow, { passive: true, capture: true });\n const off = ctl.subscribe(draw);\n draw();\n return () => {\n off();\n document.removeEventListener(\"keydown\", onKey);\n window.removeEventListener(\"resize\", follow);\n window.removeEventListener(\"scroll\", follow, { capture: true });\n root.remove();\n };\n};\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/flows-ui.ts"],"sourcesContent":["/**\n * The web renderer of the cancel flow, onboarding and in-app messages (flows.ts): plain DOM, no React, so the Next.js\n * provider and the script tag share it. Both load it only when something is about to show (a dynamic import, and\n * `sdk-flows.js` for the script tag), so pages that show nothing never download it.\n *\n * Themeable three ways: `theme` (\"light\", \"dark\", \"auto\") and `accent` from the SDK's options, and CSS variables on\n * `.ckf-root` in the site's own stylesheet (`--ckf-accent`, `--ckf-bg`, `--ckf-fg`, `--ckf-muted`, `--ckf-line`,\n * `--ckf-radius`, `--ckf-font`). Every text is set with textContent; nothing from the server is parsed as HTML.\n */\nimport { type FlowView, type FlowsController, discounted, money, offerButton } from \"./flows\";\nimport type { PaywallHost } from \"./paywall\";\n\nexport interface FlowsTheme {\n theme?: \"light\" | \"dark\" | \"auto\";\n /** The main color of buttons and highlights, like \"#3f76ff\". */\n accent?: string;\n /** An extra class on the root, for the site's own CSS. */\n className?: string;\n}\n\nconst STYLE_ID = \"ckf-styles\";\n\nexport const FLOWS_CSS = `\n.ckf-root{--ckf-accent:#3f76ff;--ckf-accent-ink:#fff;--ckf-bg:#fff;--ckf-fg:#1c1d20;--ckf-muted:#5d6068;--ckf-soft:#f4f5f7;--ckf-line:#e4e5e8;--ckf-danger:#d92d20;--ckf-radius:14px;--ckf-font:inherit;--ckf-shadow:0 16px 40px -12px rgba(16,18,24,.28),0 0 0 1px rgba(16,18,24,.07)}\n.ckf-root[data-theme=dark]{--ckf-bg:#17181b;--ckf-fg:#f1f2f4;--ckf-muted:#a9acb3;--ckf-soft:#232428;--ckf-line:#2e2f35;--ckf-shadow:0 16px 40px -12px rgba(0,0,0,.6),0 0 0 1px rgba(255,255,255,.08)}\n@media (prefers-color-scheme:dark){.ckf-root[data-theme=auto]{--ckf-bg:#17181b;--ckf-fg:#f1f2f4;--ckf-muted:#a9acb3;--ckf-soft:#232428;--ckf-line:#2e2f35;--ckf-shadow:0 16px 40px -12px rgba(0,0,0,.6),0 0 0 1px rgba(255,255,255,.08)}}\n.ckf-root,.ckf-root *{box-sizing:border-box}\n.ckf-root{font-family:var(--ckf-font);font-size:14px;line-height:1.45;color:var(--ckf-fg);-webkit-font-smoothing:antialiased}\n.ckf-root :where(button,textarea){font:inherit;color:inherit;margin:0}\n.ckf-root :focus-visible{outline:2px solid var(--ckf-accent);outline-offset:2px}\n.ckf-scrim{position:fixed;inset:0;z-index:2147483002;background:rgba(0,0,0,.4);display:flex;align-items:center;justify-content:center;padding:16px;animation:ckf-fade .15s ease-out}\n.ckf-card{position:relative;width:100%;max-width:400px;max-height:calc(100dvh - 32px);overflow-y:auto;background:var(--ckf-bg);color:var(--ckf-fg);border-radius:var(--ckf-radius);box-shadow:var(--ckf-shadow);padding:18px 20px 20px;display:flex;flex-direction:column;gap:12px;animation:ckf-in .18s ease-out}\n.ckf-float{position:fixed;z-index:2147483001;right:20px;bottom:20px;width:340px;max-width:calc(100vw - 24px)}\n.ckf-top{display:flex;align-items:center;gap:8px;min-height:20px}\n.ckf-dots{display:flex;gap:4px}\n.ckf-dot{width:16px;height:4px;border-radius:4px;background:var(--ckf-line)}\n.ckf-dot[data-on]{background:var(--ckf-accent)}\n.ckf-x{margin-left:auto;width:28px;height:28px;border:0;border-radius:8px;background:none;color:var(--ckf-muted);cursor:pointer;display:inline-flex;align-items:center;justify-content:center;font-size:18px;line-height:1}\n.ckf-x:hover{background:var(--ckf-soft);color:var(--ckf-fg)}\n.ckf-h{margin:0;font-size:17px;font-weight:650;letter-spacing:-.01em;text-wrap:balance}\n.ckf-p{margin:0;color:var(--ckf-muted);text-wrap:pretty;white-space:pre-wrap}\n.ckf-small{margin:0;font-size:12px;color:var(--ckf-muted)}\n.ckf-opts{display:flex;flex-direction:column;gap:6px;margin:0;padding:0;list-style:none}\n.ckf-opt{display:flex;align-items:center;gap:10px;width:100%;min-height:40px;padding:8px 12px;border:1px solid var(--ckf-line);border-radius:10px;background:var(--ckf-bg);text-align:left;cursor:pointer;font-weight:500}\n.ckf-opt:hover{background:var(--ckf-soft)}\n.ckf-opt[aria-checked=true]{border-color:var(--ckf-accent);background:color-mix(in srgb,var(--ckf-accent) 8%,var(--ckf-bg))}\n.ckf-radio{width:16px;height:16px;flex:none;border-radius:50%;border:1.5px solid var(--ckf-line);display:inline-flex;align-items:center;justify-content:center}\n.ckf-opt[aria-checked=true] .ckf-radio{border-color:var(--ckf-accent)}\n.ckf-opt[aria-checked=true] .ckf-radio::after{content:\"\";width:8px;height:8px;border-radius:50%;background:var(--ckf-accent)}\n.ckf-box{border-radius:10px;background:var(--ckf-soft);padding:12px;display:flex;align-items:baseline;gap:8px;flex-wrap:wrap}\n.ckf-big{font-size:22px;font-weight:650;font-variant-numeric:tabular-nums}\n.ckf-strike{color:var(--ckf-muted);text-decoration:line-through;font-variant-numeric:tabular-nums}\n.ckf-text{width:100%;min-height:64px;resize:vertical;border:1px solid var(--ckf-line);border-radius:10px;background:var(--ckf-bg);padding:8px 10px}\n.ckf-text::placeholder{color:var(--ckf-muted)}\n.ckf-btns{display:flex;flex-direction:column;gap:6px}\n.ckf-btn{height:38px;padding:0 14px;border-radius:10px;border:1px solid var(--ckf-line);background:var(--ckf-bg);font-weight:600;cursor:pointer;transition:filter .1s linear,background-color .1s linear}\n.ckf-btn:hover{background:var(--ckf-soft)}\n.ckf-btn[disabled]{opacity:.55;cursor:not-allowed}\n.ckf-primary{background:var(--ckf-accent);border-color:var(--ckf-accent);color:var(--ckf-accent-ink)}\n.ckf-primary:hover{background:var(--ckf-accent);filter:brightness(1.07)}\n.ckf-danger{background:var(--ckf-danger);border-color:var(--ckf-danger);color:#fff}\n.ckf-danger:hover{background:var(--ckf-danger);filter:brightness(1.07)}\n.ckf-quiet{border-color:transparent;background:none;color:var(--ckf-muted);height:30px;font-weight:500}\n.ckf-quiet:hover{background:none;color:var(--ckf-fg)}\n.ckf-err{margin:0;color:var(--ckf-danger);font-size:13px;font-weight:500}\n.ckf-row{display:flex;justify-content:flex-end;gap:8px;align-items:center}\n.ckf-row .ckf-small{margin-right:auto}\n.ckf-ring{position:fixed;z-index:2147483000;pointer-events:none;border-radius:10px;box-shadow:0 0 0 2px var(--ckf-accent),0 0 0 6px color-mix(in srgb,var(--ckf-accent) 25%,transparent);transition:all .15s ease-out}\n.ckf-check{display:flex;align-items:flex-start;gap:10px;width:100%;padding:8px 6px;border:0;border-radius:8px;background:none;text-align:left}\n.ckf-check:where(button){cursor:pointer}\n.ckf-check:where(button):hover{background:var(--ckf-soft)}\n.ckf-tick{width:18px;height:18px;flex:none;margin-top:1px;border-radius:50%;border:1.5px solid var(--ckf-line);display:inline-flex;align-items:center;justify-content:center;color:var(--ckf-accent-ink);font-size:11px;font-weight:700}\n.ckf-check[data-done] .ckf-tick{background:var(--ckf-accent);border-color:var(--ckf-accent)}\n.ckf-check[data-done] b{color:var(--ckf-muted);text-decoration:line-through}\n.ckf-check span{display:flex;flex-direction:column;gap:2px;min-width:0}\n.ckf-bar{height:4px;border-radius:4px;background:var(--ckf-soft);overflow:hidden}\n.ckf-bar i{display:block;height:100%;background:var(--ckf-accent)}\n.ckf-banner{position:fixed;z-index:2147483001;left:50%;top:12px;transform:translateX(-50%);width:min(640px,calc(100vw - 24px));background:var(--ckf-bg);border-radius:12px;box-shadow:var(--ckf-shadow);padding:10px 10px 10px 16px;display:flex;align-items:center;gap:12px;animation:ckf-in .18s ease-out}\n.ckf-banner>span{flex:1;min-width:0;display:flex;flex-direction:column}\n.ckf-banner .ckf-btn{height:32px}\n.ckf-pw{max-width:420px;gap:14px}\n.ckf-root[data-still] :is(.ckf-scrim,.ckf-card){animation:none}\n.ckf-li{display:flex;gap:10px;align-items:flex-start;margin:0}\n.ckf-li::before{content:\"✓\";flex:none;width:20px;height:20px;border-radius:50%;background:color-mix(in srgb,var(--ckf-accent) 14%,var(--ckf-bg));color:var(--ckf-accent);font-size:12px;font-weight:700;display:inline-flex;align-items:center;justify-content:center}\n.ckf-quote{margin:0;padding:12px 14px;border-radius:12px;background:var(--ckf-soft)}\n.ckf-quote cite{display:block;margin-top:6px;font-style:normal;font-size:12px;color:var(--ckf-muted)}\n.ckf-plan{justify-content:space-between;min-height:52px}\n.ckf-plan span:nth-child(2){display:flex;flex-direction:column;margin-right:auto}\n.ckf-plan b{font-variant-numeric:tabular-nums}\n@keyframes ckf-fade{from{opacity:0}}\n@keyframes ckf-in{from{opacity:0;transform:translateY(6px)}}\n.ckf-banner{animation-name:ckf-fade}\n@media (max-width:520px){.ckf-scrim{align-items:flex-end;padding:0}.ckf-scrim .ckf-card{max-width:none;border-radius:var(--ckf-radius) var(--ckf-radius) 0 0;max-height:92dvh}.ckf-float{right:12px;bottom:12px}}\n@media (prefers-reduced-motion:reduce){.ckf-root *{animation:none!important;transition:none!important}}\n`;\n\ntype Attrs = Record<string, string | boolean | undefined | ((e: Event) => void)>;\n\n/** A small element builder: attributes, `on*` handlers and children (strings become text, never HTML). */\nconst h = (tag: string, attrs: Attrs = {}, ...children: (Node | string | false | null | undefined)[]) => {\n const el = document.createElement(tag);\n for (const [k, v] of Object.entries(attrs)) {\n if (v === undefined || v === false) continue;\n if (typeof v === \"function\") el.addEventListener(k.slice(2).toLowerCase(), v);\n else if (k === \"className\") el.className = String(v);\n else el.setAttribute(k, v === true ? \"\" : v);\n }\n for (const c of children) if (c || c === \"\") el.append(typeof c === \"string\" ? document.createTextNode(c) : c);\n return el;\n};\n\nconst button = (label: string, kind: \"primary\" | \"danger\" | \"secondary\" | \"quiet\", onClick: () => void, opts: { disabled?: boolean; id?: string } = {}) =>\n h(\n \"button\",\n {\n type: \"button\",\n className: `ckf-btn${kind === \"secondary\" ? \"\" : ` ckf-${kind}`}`,\n disabled: !!opts.disabled,\n \"data-ckf-id\": opts.id,\n onClick: () => onClick(),\n },\n label,\n );\n\nconst closeButton = (label: string, onClick: () => void) =>\n h(\"button\", { type: \"button\", className: \"ckf-x\", \"aria-label\": label, \"data-ckf-id\": \"x\", onClick: () => onClick() }, \"×\");\n\nconst dots = (step: number, steps: number) =>\n steps > 1\n ? h(\n \"span\",\n { className: \"ckf-dots\", role: \"img\", \"aria-label\": `Step ${step} of ${steps}` },\n ...Array.from({ length: steps }, (_, i) => h(\"span\", { className: \"ckf-dot\", \"data-on\": i < step })),\n )\n : h(\"span\");\n\n/** A key that changes when the screen (not just its state) changes: then focus moves to the new screen. */\nconst screenKey = (v: FlowView) =>\n v.type === \"cancel\" ? `c:${v.screen}:${v.round ?? 1}` : v.type === \"step\" ? `s:${v.step.id}` : v.type === \"checklist\" ? `l:${v.flow.id}` : `m:${v.message.id}`;\n\n/**\n * Draws the controller's view into the page until the returned function is called. One root element under <body>;\n * nothing is drawn while there is nothing to show.\n */\n/** The stylesheet once, and a themed root element under <body>. */\nconst rootFor = (theme: FlowsTheme) => {\n if (!document.getElementById(STYLE_ID)) {\n const style = document.createElement(\"style\");\n style.id = STYLE_ID;\n style.textContent = FLOWS_CSS;\n document.head.appendChild(style);\n }\n const root = h(\"div\", { className: `ckf-root${theme.className ? ` ${theme.className}` : \"\"}`, \"data-theme\": theme.theme ?? \"auto\" });\n if (theme.accent) root.style.setProperty(\"--ckf-accent\", theme.accent);\n document.body.appendChild(root);\n return root;\n};\n\nexport const mountFlows = (ctl: FlowsController, theme: FlowsTheme = {}): (() => void) => {\n if (typeof document === \"undefined\") return () => {};\n const root = rootFor(theme);\n\n let lastKey = \"\";\n let returnFocus: HTMLElement | null = null;\n let comment = \"\";\n let placed: { float: HTMLElement; ring: HTMLElement; target: HTMLElement } | null = null;\n\n // ---------- Cancel flow ----------\n\n const cancelCard = (v: Extract<FlowView, { type: \"cancel\" }>) => {\n const titleId = \"ckf-title\";\n const busy = v.busy;\n const body: (Node | false)[] = [];\n if (v.screen === \"survey\") {\n body.push(\n h(\"h2\", { className: \"ckf-h\", id: titleId }, v.flow.question || \"Why are you leaving?\"),\n h(\n \"div\",\n { className: \"ckf-opts\", role: \"radiogroup\", \"aria-labelledby\": titleId },\n ...v.flow.reasons.map((r) =>\n h(\n \"button\",\n {\n type: \"button\",\n role: \"radio\",\n className: \"ckf-opt\",\n \"aria-checked\": String(v.reason === r.id),\n \"data-ckf-id\": `r:${r.id}`,\n onClick: () => ctl.cancel.pick(r.id),\n },\n h(\"span\", { className: \"ckf-radio\" }),\n r.label,\n ),\n ),\n ),\n h(\n \"div\",\n { className: \"ckf-btns\" },\n button(\"Continue\", \"primary\", ctl.cancel.next, { disabled: !v.reason, id: \"go\" }),\n button(\"Never mind, keep my plan\", \"quiet\", ctl.cancel.close),\n ),\n );\n } else if (v.screen === \"followup\" && v.followUp) {\n const f = v.followUp;\n let field: Node;\n if (f.answers.length)\n field = h(\n \"div\",\n { className: \"ckf-opts\", role: \"radiogroup\", \"aria-labelledby\": titleId },\n ...f.answers.map((a, i) =>\n h(\n \"button\",\n {\n type: \"button\",\n role: \"radio\",\n className: \"ckf-opt\",\n \"aria-checked\": String(v.answer === a),\n \"data-ckf-id\": `a:${i}`,\n onClick: () => ctl.cancel.answer(a),\n },\n h(\"span\", { className: \"ckf-radio\" }),\n a,\n ),\n ),\n );\n else {\n const text = h(\"textarea\", {\n className: \"ckf-text\",\n placeholder: \"Your answer (optional)\",\n \"aria-labelledby\": titleId,\n maxlength: \"500\",\n \"data-ckf-id\": \"answer\",\n onInput: (e) => ctl.cancel.answer((e.target as HTMLTextAreaElement).value),\n }) as HTMLTextAreaElement;\n text.value = v.answer ?? \"\";\n field = text;\n }\n const label = v.flow.reasons.find((r) => r.id === v.reason)?.label;\n body.push(\n label ? h(\"p\", { className: \"ckf-small\" }, `You said: ${label}`) : false,\n h(\"h2\", { className: \"ckf-h\", id: titleId }, f.question),\n field,\n h(\n \"div\",\n { className: \"ckf-btns\" },\n button(\"Continue\", \"primary\", ctl.cancel.next, { id: \"go\" }),\n button(\"Never mind, keep my plan\", \"quiet\", ctl.cancel.close),\n ),\n );\n } else if (v.screen === \"offer\" && v.offer) {\n const o = v.offer;\n const label = v.flow.reasons.find((r) => r.id === v.reason)?.label;\n let detail: Node | false = false;\n if (o.kind === \"discount\" && o.percent)\n detail = v.price\n ? h(\n \"div\",\n { className: \"ckf-box\" },\n h(\"span\", { className: \"ckf-big\" }, money(discounted(v.price.amount, o.percent), v.price.currency)),\n h(\"span\", { className: \"ckf-strike\" }, money(v.price.amount, v.price.currency)),\n h(\n \"span\",\n { className: \"ckf-small\" },\n `/${v.price.interval ?? \"month\"}${o.months ? ` for ${o.months} ${o.months === 1 ? \"month\" : \"months\"}` : \"\"}`,\n ),\n )\n : h(\n \"div\",\n { className: \"ckf-box\" },\n h(\"span\", { className: \"ckf-big\" }, `${o.percent}% off`),\n o.months ? h(\"span\", { className: \"ckf-small\" }, `for ${o.months} ${o.months === 1 ? \"month\" : \"months\"}`) : false,\n );\n if (o.kind === \"pause\" && o.months)\n detail = h(\n \"div\",\n { className: \"ckf-box\" },\n h(\"span\", {}, `Pause for up to ${o.months} ${o.months === 1 ? \"month\" : \"months\"}. Your data stays.`),\n );\n if (o.kind === \"downgrade\" && o.planName) detail = h(\"div\", { className: \"ckf-box\" }, h(\"b\", {}, o.planName));\n const text =\n o.kind === \"contact\"\n ? (h(\"textarea\", {\n className: \"ckf-text\",\n placeholder: \"What would make you stay?\",\n \"aria-label\": \"Your message\",\n maxlength: \"500\",\n \"data-ckf-id\": \"comment\",\n onInput: (e) => (comment = (e.target as HTMLTextAreaElement).value),\n }) as HTMLTextAreaElement)\n : null;\n if (text) text.value = comment;\n body.push(\n label ? h(\"p\", { className: \"ckf-small\" }, `You said: ${label}`) : false,\n h(\"h2\", { className: \"ckf-h\", id: titleId }, o.headline),\n detail,\n text ?? false,\n h(\n \"div\",\n { className: \"ckf-btns\" },\n button(busy ? \"One moment…\" : offerButton(o), \"primary\", () => ctl.cancel.accept(comment), { disabled: busy, id: \"go\" }),\n button(\"No thanks, continue cancelling\", \"quiet\", ctl.cancel.decline, { disabled: busy }),\n ),\n );\n } else {\n const plain = v.steps === 1;\n const text = plain\n ? null\n : (h(\"textarea\", {\n className: \"ckf-text\",\n placeholder: \"Anything else we should know? (optional)\",\n \"aria-label\": \"Anything else we should know? (optional)\",\n maxlength: \"500\",\n \"data-ckf-id\": \"comment\",\n onInput: (e) => (comment = (e.target as HTMLTextAreaElement).value),\n }) as HTMLTextAreaElement);\n if (text) text.value = comment;\n body.push(\n h(\"h2\", { className: \"ckf-h\", id: titleId }, v.flow.confirm.title),\n v.flow.confirm.body ? h(\"p\", { className: \"ckf-p\" }, v.flow.confirm.body) : false,\n text ?? false,\n h(\n \"div\",\n { className: \"ckf-btns\" },\n button(busy ? \"One moment…\" : v.flow.confirm.button, \"danger\", () => ctl.cancel.confirm(comment), { disabled: busy, id: \"go\" }),\n button(\"Keep my plan\", \"secondary\", ctl.cancel.close, { disabled: busy }),\n ),\n );\n }\n return h(\n \"div\",\n { className: \"ckf-scrim\", onClick: (e) => e.target === e.currentTarget && !busy && ctl.cancel.close() },\n h(\n \"div\",\n { className: \"ckf-card\", role: \"dialog\", \"aria-modal\": \"true\", \"aria-labelledby\": titleId },\n h(\"div\", { className: \"ckf-top\" }, dots(v.step, v.steps), closeButton(\"Close\", ctl.cancel.close)),\n ...body,\n v.error ? h(\"p\", { className: \"ckf-err\", role: \"alert\" }, v.error) : false,\n ),\n );\n };\n\n // ---------- Onboarding ----------\n\n const stepCard = (v: Extract<FlowView, { type: \"step\" }>) => {\n const s = v.step;\n const card = h(\n \"div\",\n { className: \"ckf-card\", role: \"dialog\", \"aria-modal\": s.kind === \"modal\" ? \"true\" : undefined, \"aria-labelledby\": \"ckf-title\" },\n h(\n \"div\",\n { className: \"ckf-top\" },\n v.total > 1 ? h(\"p\", { className: \"ckf-small\" }, `Step ${v.index + 1} of ${v.total}`) : h(\"span\"),\n closeButton(\"Close the tour\", ctl.onboarding.dismiss),\n ),\n h(\"h2\", { className: \"ckf-h\", id: \"ckf-title\" }, s.title),\n s.body ? h(\"p\", { className: \"ckf-p\" }, s.body) : false,\n h(\"div\", { className: \"ckf-row\" }, button(s.button || (v.last ? \"Done\" : \"Next\"), \"primary\", ctl.onboarding.next, { id: \"go\" })),\n );\n if (s.kind === \"modal\") return h(\"div\", { className: \"ckf-scrim\" }, card);\n const target = s.target ? findTarget(s.target) : null;\n const float = h(\"div\", { className: \"ckf-float\" }, card);\n if (target) {\n const ring = h(\"div\", { className: \"ckf-ring\", \"aria-hidden\": \"true\" });\n placed = { float, ring, target };\n place(float, ring, target);\n return h(\"div\", {}, ring, float);\n }\n return float;\n };\n\n /**\n * The element a tour step points at: a CSS selector (`#new-project`, `.toolbar button`, picked on the site in\n * MRRocket), or a plain name for `data-ck-target=\"…\"`. A selector that does not parse falls back to the name.\n */\n const findTarget = (t: string): HTMLElement | null => {\n const named = () => document.querySelector<HTMLElement>(`[data-ck-target=\"${CSS.escape(t)}\"]`);\n if (!/^[#.[]|[\\s>:=]/.test(t)) return named();\n return safeQuery(t) ?? named();\n };\n const safeQuery = (sel: string) => {\n try {\n return document.querySelector<HTMLElement>(sel);\n } catch {\n return null;\n }\n };\n\n /** Puts a tour card next to the element it points at (below it, or above when there is no room). */\n const place = (float: HTMLElement, ring: HTMLElement, target: HTMLElement) => {\n const r = target.getBoundingClientRect();\n Object.assign(ring.style, { left: `${r.left - 4}px`, top: `${r.top - 4}px`, width: `${r.width + 8}px`, height: `${r.height + 8}px` });\n const w = Math.min(340, window.innerWidth - 24);\n const left = Math.max(12, Math.min(r.left, window.innerWidth - w - 12));\n const below = r.bottom + 12;\n Object.assign(float.style, { left: `${left}px`, right: \"auto\", width: `${w}px` });\n if (below + 200 < window.innerHeight) Object.assign(float.style, { top: `${below}px`, bottom: \"auto\" });\n else Object.assign(float.style, { top: \"auto\", bottom: `${Math.max(12, window.innerHeight - r.top + 12)}px` });\n };\n\n const checklist = (v: Extract<FlowView, { type: \"checklist\" }>) => {\n const done = v.items.filter((i) => i.done).length;\n return h(\n \"div\",\n { className: \"ckf-float\" },\n h(\n \"div\",\n { className: \"ckf-card\", role: \"region\", \"aria-labelledby\": \"ckf-title\" },\n h(\n \"div\",\n { className: \"ckf-top\" },\n h(\"h2\", { className: \"ckf-h\", id: \"ckf-title\" }, v.flow.name),\n closeButton(\"Hide the checklist\", ctl.onboarding.dismiss),\n ),\n h(\"p\", { className: \"ckf-small\" }, `${done} of ${v.items.length} done`),\n h(\n \"div\",\n { className: \"ckf-bar\", role: \"progressbar\", \"aria-valuemin\": \"0\", \"aria-valuemax\": String(v.items.length), \"aria-valuenow\": String(done) },\n h(\"i\", { style: `width:${(done / v.items.length) * 100}%` } as Attrs),\n ),\n h(\n \"div\",\n { className: \"ckf-opts\" },\n ...v.items.map(({ step, done: d }) => {\n const inner = [\n h(\"span\", { className: \"ckf-tick\", \"aria-hidden\": \"true\" }, d ? \"✓\" : \"\"),\n h(\"span\", {}, h(\"b\", {}, step.title), step.body ? h(\"span\", { className: \"ckf-small\" }, step.body) : false),\n ];\n // An item with an event ticks itself when the app records it; the others are ticked by hand.\n return d || step.event\n ? h(\"div\", { className: \"ckf-check\", \"data-done\": d, role: \"listitem\" }, ...inner)\n : h(\n \"button\",\n {\n type: \"button\",\n className: \"ckf-check\",\n \"aria-label\": `Mark “${step.title}” done`,\n \"data-ckf-id\": `i:${step.id}`,\n onClick: () => ctl.onboarding.check(step.id),\n },\n ...inner,\n );\n }),\n ),\n ),\n );\n };\n\n // ---------- Messages ----------\n\n const message = (v: Extract<FlowView, { type: \"message\" }>) => {\n const m = v.message;\n const action = m.button ? button(m.button, \"primary\", ctl.message.click, { id: \"go\" }) : false;\n if (m.style === \"banner\")\n return h(\n \"div\",\n { className: \"ckf-banner\", role: \"status\" },\n h(\"span\", {}, h(\"b\", {}, m.title), m.body ? h(\"span\", { className: \"ckf-small\" }, m.body) : false),\n action,\n closeButton(\"Dismiss\", ctl.message.dismiss),\n );\n return h(\n \"div\",\n { className: \"ckf-scrim\", onClick: (e) => e.target === e.currentTarget && ctl.message.dismiss() },\n h(\n \"div\",\n { className: \"ckf-card\", role: \"dialog\", \"aria-modal\": \"true\", \"aria-labelledby\": \"ckf-title\" },\n h(\"div\", { className: \"ckf-top\" }, h(\"span\"), closeButton(\"Dismiss\", ctl.message.dismiss)),\n h(\"h2\", { className: \"ckf-h\", id: \"ckf-title\" }, m.title),\n m.body ? h(\"p\", { className: \"ckf-p\" }, m.body) : false,\n action ? h(\"div\", { className: \"ckf-row\" }, action) : false,\n ),\n );\n };\n\n // ---------- Drawing ----------\n\n const draw = () => {\n const v = ctl.view();\n const key = v ? screenKey(v) : \"\";\n const focused = (document.activeElement as HTMLElement | null)?.closest?.(\".ckf-root\") ? document.activeElement?.getAttribute(\"data-ckf-id\") : null;\n if (key && !lastKey && document.activeElement instanceof HTMLElement && !document.activeElement.closest(\".ckf-root\"))\n returnFocus = document.activeElement;\n if (key !== lastKey && !key.startsWith(\"c:\")) comment = \"\";\n if (!key) comment = \"\";\n placed = null;\n root.replaceChildren(\n ...(v ? [v.type === \"cancel\" ? cancelCard(v) : v.type === \"step\" ? stepCard(v) : v.type === \"checklist\" ? checklist(v) : message(v)] : []),\n );\n if (key !== lastKey) {\n // A new screen: focus its first control (a dialog's), so keyboard and screen reader users land in it.\n const modal = root.querySelector<HTMLElement>(\"[aria-modal=true]\");\n const first = modal?.querySelector<HTMLElement>(\"[role=radio], [data-ckf-id=go], textarea\") ?? null;\n first?.focus();\n if (!key && returnFocus) {\n returnFocus.focus();\n returnFocus = null;\n }\n } else if (focused) root.querySelector<HTMLElement>(`[data-ckf-id=\"${CSS.escape(focused)}\"]`)?.focus();\n lastKey = key;\n };\n\n const onKey = (e: KeyboardEvent) => {\n if (e.key !== \"Escape\") return;\n const v = ctl.view();\n if (v?.type === \"cancel\") ctl.cancel.close();\n else if (v?.type === \"message\") ctl.message.dismiss();\n else if (v?.type === \"step\" && v.step.kind === \"modal\") ctl.onboarding.dismiss();\n };\n // A tour card follows its element when the page scrolls or resizes.\n const follow = () => {\n if (placed) place(placed.float, placed.ring, placed.target);\n };\n document.addEventListener(\"keydown\", onKey);\n window.addEventListener(\"resize\", follow);\n window.addEventListener(\"scroll\", follow, { passive: true, capture: true });\n const off = ctl.subscribe(draw);\n draw();\n return () => {\n off();\n document.removeEventListener(\"keydown\", onKey);\n window.removeEventListener(\"resize\", follow);\n window.removeEventListener(\"scroll\", follow, { capture: true });\n root.remove();\n };\n};\n\n/**\n * Draws an open paywall (paywall.ts) page by page until the returned function is called: the dots, the page's title and\n * text, its benefits, quotes or plans, Back and the button. Closes on Escape and on a click beside the card.\n */\nexport const mountPaywall = (host: PaywallHost, theme: FlowsTheme = {}): (() => void) => {\n if (typeof document === \"undefined\") return () => {};\n const root = rootFor(theme);\n let lastKey = \"\";\n let returnFocus: HTMLElement | null = null;\n const draw = () => {\n const v = host.view();\n const key = v ? `${v.paywall.id}:${v.index}` : \"\";\n if (key && !lastKey && document.activeElement instanceof HTMLElement) returnFocus = document.activeElement;\n if (!v) {\n root.replaceChildren();\n root.removeAttribute(\"data-still\");\n if (lastKey) returnFocus?.focus();\n lastKey = \"\";\n return;\n }\n // Fades in when it opens, not again on every page or plan.\n if (lastKey) root.setAttribute(\"data-still\", \"\");\n const p = v.page;\n const plans =\n p.kind === \"plans\"\n ? h(\n \"div\",\n { className: \"ckf-opts\", role: \"radiogroup\", \"aria-label\": \"Plans\" },\n ...v.plans.map((plan) =>\n h(\n \"button\",\n {\n type: \"button\",\n role: \"radio\",\n className: \"ckf-opt ckf-plan\",\n \"aria-checked\": String(v.plan === plan.id),\n \"data-ckf-id\": `p:${plan.id}`,\n onClick: () => host.pick(plan.id),\n },\n h(\"span\", { className: \"ckf-radio\" }),\n h(\"span\", {}, plan.name, plan.note ? h(\"span\", { className: \"ckf-small\" }, plan.note) : false),\n h(\"b\", {}, plan.price),\n ),\n ),\n )\n : false;\n root.replaceChildren(\n h(\n \"div\",\n { className: \"ckf-scrim\", onClick: (e) => e.target === e.currentTarget && host.close() },\n h(\n \"div\",\n { className: \"ckf-card ckf-pw\", role: \"dialog\", \"aria-modal\": \"true\", \"aria-labelledby\": \"ckf-title\" },\n h(\"div\", { className: \"ckf-top\" }, dots(v.index + 1, v.total), closeButton(\"Close\", host.close)),\n h(\"h2\", { className: \"ckf-h\", id: \"ckf-title\" }, p.title),\n p.body ? h(\"p\", { className: \"ckf-p\" }, p.body) : false,\n p.kind === \"benefits\" && p.items?.length ? h(\"div\", { className: \"ckf-opts\" }, ...p.items.map((i) => h(\"p\", { className: \"ckf-li\" }, i))) : false,\n p.kind === \"proof\" && p.quotes?.length\n ? h(\"div\", { className: \"ckf-opts\" }, ...p.quotes.map((q) => h(\"blockquote\", { className: \"ckf-quote\" }, q.text, q.by ? h(\"cite\", {}, q.by) : false)))\n : false,\n plans,\n v.error ? h(\"p\", { className: \"ckf-err\", role: \"alert\" }, v.error) : false,\n h(\n \"div\",\n { className: \"ckf-btns\" },\n button(v.busy ? \"One moment…\" : p.button || \"Continue\", \"primary\", host.next, {\n disabled: v.busy || (p.kind === \"plans\" && !v.plan),\n id: \"go\",\n }),\n v.index ? button(\"Back\", \"quiet\", host.back, { disabled: v.busy }) : false,\n ),\n ),\n ),\n );\n if (key !== lastKey) root.querySelector<HTMLElement>(\"[data-ckf-id=go]\")?.focus();\n lastKey = key;\n };\n const onKey = (e: KeyboardEvent) => {\n if (!host.view()) return;\n if (e.key === \"Escape\") host.close();\n };\n document.addEventListener(\"keydown\", onKey);\n const off = host.subscribe(draw);\n draw();\n return () => {\n off();\n document.removeEventListener(\"keydown\", onKey);\n root.remove();\n };\n};\n"],"mappings":"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","names":[]}
package/dist/flows.d.ts CHANGED
@@ -31,6 +31,29 @@ export interface CancelOffer {
31
31
  plan?: string;
32
32
  planName?: string;
33
33
  }
34
+ /** 0.15.0: a second question after an answer ("What would be a fair price?"); no answers means a free text. */
35
+ export interface CancelFollowUp {
36
+ question: string;
37
+ /** The choices, in order; empty for a free text. */
38
+ answers: string[];
39
+ }
40
+ /**
41
+ * 0.15.0: a different offer for some customers of one answer. Every condition that is set must hold; the first rule
42
+ * that matches wins, else the answer's own offer. `offer: null` means no offer (straight to the confirmation).
43
+ */
44
+ export interface CancelRule {
45
+ when: {
46
+ /** The follow-up's answer, as written. */
47
+ answer?: string;
48
+ /** The customer's plan: its id or name as MRRocket knows it from the payment provider (any of a list). */
49
+ plan?: string | string[];
50
+ /** A customer for fewer than this many months. */
51
+ tenureUnder?: number;
52
+ /** A customer for at least this many months. */
53
+ tenureOver?: number;
54
+ };
55
+ offer: CancelOffer | null;
56
+ }
34
57
  /** The product's live cancel flow (MRRocket › Churn › Cancel flow). */
35
58
  export interface CancelFlowConfig {
36
59
  /** Grows with every publish. */
@@ -43,6 +66,12 @@ export interface CancelFlowConfig {
43
66
  }[];
44
67
  /** The offer per answer id. */
45
68
  offers: Record<string, CancelOffer>;
69
+ /** 0.15.0: the follow-up question per answer id. */
70
+ followUps?: Record<string, CancelFollowUp>;
71
+ /** 0.15.0: the rules per answer id, checked in order before its offer. */
72
+ rules?: Record<string, CancelRule[]>;
73
+ /** 0.15.0: what one answer offers after its first offer was declined. */
74
+ second?: Record<string, CancelOffer>;
46
75
  confirm: {
47
76
  title: string;
48
77
  body: string;
@@ -132,6 +161,8 @@ export interface CancelFlowResult {
132
161
  outcome: "cancelled" | "saved" | "kept";
133
162
  reason?: string;
134
163
  reasonLabel?: string;
164
+ /** 0.15.0: the answer to the follow-up question. */
165
+ answer?: string;
135
166
  offer?: CancelOffer;
136
167
  /** What the customer wrote on the confirmation, if anything. */
137
168
  comment?: string;
@@ -150,6 +181,10 @@ export interface CancelFlowOptions {
150
181
  currency: string;
151
182
  interval?: "month" | "year";
152
183
  };
184
+ /** 0.15.0: the customer's plan (its id or name in your billing), for offers that depend on it. */
185
+ plan?: string;
186
+ /** 0.15.0: since when they pay (a date, ISO string or ms), for offers that depend on how long. */
187
+ customerSince?: Date | string | number;
153
188
  /** The customer confirmed: cancel the subscription in your code. Throwing keeps the dialog open with an error. */
154
189
  onCancel?: (result: CancelFlowResult) => unknown;
155
190
  /** The customer took an offer: apply it in your code (coupon, pause, plan change). Throwing keeps the dialog open. */
@@ -160,11 +195,16 @@ export interface CancelFlowOptions {
160
195
  /** What a renderer draws. */
161
196
  export type FlowView = {
162
197
  type: "cancel";
163
- screen: "survey" | "offer" | "confirm";
198
+ screen: "survey" | "followup" | "offer" | "confirm";
164
199
  flow: CancelFlowConfig;
165
200
  /** The answer picked on the survey. */
166
201
  reason?: string;
202
+ /** 0.15.0: the follow-up question on its screen, and its answer so far. */
203
+ followUp?: CancelFollowUp;
204
+ answer?: string;
167
205
  offer?: CancelOffer;
206
+ /** 0.15.0: 2 for the offer after a declined one. */
207
+ round?: 1 | 2;
168
208
  price?: CancelFlowOptions["price"];
169
209
  busy: boolean;
170
210
  error?: string;
@@ -222,8 +262,10 @@ export interface FlowsController {
222
262
  view(): FlowView | null;
223
263
  subscribe(listener: () => void): () => void;
224
264
  /** `accept(comment)`: a "Talk to us" offer sends what the customer wrote with it. */
265
+ /** `answer(text)`: the follow-up's choice or free text (0.15.0); `next()` moves on from the survey and the follow-up. */
225
266
  cancel: {
226
267
  pick(reason: string): void;
268
+ answer(text: string): void;
227
269
  next(): void;
228
270
  accept(comment?: string): void;
229
271
  decline(): void;
@@ -264,4 +306,12 @@ export declare const nodesOf: (f: OnboardingFlow) => OnboardingNode[];
264
306
  export declare const waitMs: (w: Pick<OnboardingWait, "amount" | "unit">) => number;
265
307
  /** Whether a path matches a condition's page: the same path, or below it for a pattern ending in `*`. */
266
308
  export declare const pageMatches: (pattern: string, path: string) => boolean;
309
+ /** Whole months since a date the app passed (NaN without one, which no tenure condition holds for). */
310
+ export declare const monthsSince: (since: CancelFlowOptions["customerSince"], now?: number) => number;
311
+ /** Whether a rule holds for this customer; a condition about something the app did not pass does not hold. */
312
+ export declare const ruleMatches: ({ when: w }: CancelRule, c: {
313
+ answer?: string;
314
+ plan?: string;
315
+ months: number;
316
+ }) => boolean;
267
317
  export declare const createFlows: (env: FlowsEnv) => FlowsController;