@mrrocket/sdk 0.17.0 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +30 -1
- package/README.md +24 -1
- package/dist/api-client.d.ts +28 -4
- package/dist/api-client.js +30 -13
- package/dist/api-client.js.map +1 -1
- package/dist/browser/mrrocket-flows.js +15 -3
- package/dist/browser/mrrocket-messenger.js +38 -5
- package/dist/browser/mrrocket.js +1 -1
- package/dist/browser.d.ts +2 -2
- package/dist/browser.js +2 -1
- package/dist/browser.js.map +1 -1
- package/dist/client.d.ts +28 -3
- package/dist/client.js +37 -5
- package/dist/client.js.map +1 -1
- package/dist/context.d.ts +3 -1
- package/dist/context.js.map +1 -1
- package/dist/core.d.ts +7 -1
- package/dist/core.js +1 -1
- package/dist/core.js.map +1 -1
- package/dist/feedback.js +161 -119
- package/dist/feedback.js.map +1 -1
- package/dist/finish-web.d.ts +18 -0
- package/dist/finish-web.js +139 -0
- package/dist/finish-web.js.map +1 -0
- package/dist/flows-ui.d.ts +1 -10
- package/dist/flows-ui.js +175 -48
- package/dist/flows-ui.js.map +1 -1
- package/dist/flows-web.d.ts +8 -1
- package/dist/flows-web.js +10 -1
- package/dist/flows-web.js.map +1 -1
- package/dist/flows.d.ts +193 -9
- package/dist/flows.js +284 -38
- package/dist/flows.js.map +1 -1
- package/dist/i18n.d.ts +22 -0
- package/dist/i18n.js +74 -0
- package/dist/i18n.js.map +1 -0
- package/dist/icons.d.ts +24 -0
- package/dist/icons.js +61 -0
- package/dist/icons.js.map +1 -0
- package/dist/index.d.ts +6 -2
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/messenger-ui.d.ts +9 -3
- package/dist/messenger-ui.js +388 -191
- package/dist/messenger-ui.js.map +1 -1
- package/dist/messenger.d.ts +3 -1
- package/dist/messenger.js +3 -2
- package/dist/messenger.js.map +1 -1
- package/dist/onboarding-state.d.ts +23 -0
- package/dist/onboarding-state.js +43 -0
- package/dist/onboarding-state.js.map +1 -0
- package/dist/paywall.js +2 -1
- package/dist/paywall.js.map +1 -1
- package/dist/react-native/index.d.cts +239 -19
- package/dist/react-native/index.d.ts +239 -19
- package/dist/react-native/index.js +1305 -518
- package/dist/react-native/index.js.map +1 -1
- package/dist/server.d.ts +4 -2
- package/dist/server.js +2 -2
- package/dist/server.js.map +1 -1
- package/dist/styles-css.d.ts +1 -1
- package/dist/styles-css.js +35 -2
- package/dist/styles-css.js.map +1 -1
- package/dist/survey.js +18 -14
- package/dist/survey.js.map +1 -1
- package/dist/ui.d.ts +2 -0
- package/dist/ui.js +15 -12
- package/dist/ui.js.map +1 -1
- package/package.json +6 -3
package/dist/flows-ui.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
// src/flows-ui.ts
|
|
2
|
-
import {
|
|
2
|
+
import { playFinish } from "./finish-web.js";
|
|
3
|
+
import { OTHER_PREFIX, answerOk, forMonths, monthsWord, scaleOf, discounted, mediaOf, money, offerButton, videoEmbed } from "./flows.js";
|
|
4
|
+
import { onWords, t, tf, wordsLang } from "./i18n.js";
|
|
5
|
+
import { FLOW_ICONS, iconOf } from "./icons.js";
|
|
3
6
|
var STYLE_ID = "ckf-styles";
|
|
4
7
|
var FLOWS_CSS = `
|
|
5
8
|
.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)}
|
|
@@ -28,6 +31,11 @@ var FLOWS_CSS = `
|
|
|
28
31
|
.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}
|
|
29
32
|
.ckf-opt[aria-checked=true] .ckf-radio{border-color:var(--ckf-accent)}
|
|
30
33
|
.ckf-opt[aria-checked=true] .ckf-radio::after{content:"";width:8px;height:8px;border-radius:50%;background:var(--ckf-accent)}
|
|
34
|
+
.ckf-scale{display:flex;gap:4px}
|
|
35
|
+
.ckf-num{flex:1;min-width:0;height:38px;padding:0;border:1px solid var(--ckf-line);border-radius:8px;background:var(--ckf-bg);cursor:pointer;font-weight:500}
|
|
36
|
+
.ckf-num:hover{background:var(--ckf-soft)}
|
|
37
|
+
.ckf-num[aria-checked=true]{border-color:var(--ckf-accent);background:var(--ckf-accent);color:var(--ckf-accent-ink)}
|
|
38
|
+
.ckf-ends{display:flex;justify-content:space-between;gap:12px;margin-top:6px}
|
|
31
39
|
.ckf-box{border-radius:10px;background:var(--ckf-soft);padding:12px;display:flex;align-items:baseline;gap:8px;flex-wrap:wrap}
|
|
32
40
|
.ckf-big{font-size:22px;font-weight:650;font-variant-numeric:tabular-nums}
|
|
33
41
|
.ckf-strike{color:var(--ckf-muted);text-decoration:line-through;font-variant-numeric:tabular-nums}
|
|
@@ -53,6 +61,12 @@ var FLOWS_CSS = `
|
|
|
53
61
|
[data-side=bottom]>.ckf-arrow{top:0;left:var(--ckf-ap)}[data-side=top]>.ckf-arrow{top:100%;left:var(--ckf-ap)}
|
|
54
62
|
[data-side=right]>.ckf-arrow{left:0;top:var(--ckf-ap)}[data-side=left]>.ckf-arrow{left:100%;top:var(--ckf-ap)}
|
|
55
63
|
.ckf-check .ckf-go{margin-left:auto;align-self:center;color:var(--ckf-accent);font-weight:700}
|
|
64
|
+
.ckf-ic{width:20px;height:20px;flex:none;display:inline-flex;align-items:center;justify-content:center;color:var(--ckf-accent);font-size:16px;line-height:1}
|
|
65
|
+
.ckf-ic svg{width:20px;height:20px}.ckf-ic img{width:20px;height:20px;border-radius:5px;object-fit:cover}
|
|
66
|
+
.ckf-hic{width:40px;height:40px;border-radius:12px;background:color-mix(in srgb,var(--ckf-accent) 12%,var(--ckf-bg));display:inline-flex;align-items:center;justify-content:center;color:var(--ckf-accent);font-size:22px;line-height:1}
|
|
67
|
+
.ckf-hic svg{width:22px;height:22px}.ckf-hic img{width:40px;height:40px;border-radius:12px;object-fit:cover}
|
|
68
|
+
.ckf-input{width:100%;height:40px;border:1px solid var(--ckf-line);border-radius:10px;background:var(--ckf-bg);padding:0 12px}
|
|
69
|
+
.ckf-input::placeholder{color:var(--ckf-muted)}
|
|
56
70
|
.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}
|
|
57
71
|
.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}
|
|
58
72
|
.ckf-check:where(button){cursor:pointer}
|
|
@@ -67,7 +81,7 @@ var FLOWS_CSS = `
|
|
|
67
81
|
.ckf-banner>span{flex:1;min-width:0;display:flex;flex-direction:column}
|
|
68
82
|
.ckf-banner .ckf-btn{height:32px}
|
|
69
83
|
.ckf-pw{max-width:420px;gap:14px}
|
|
70
|
-
.ckf-root[data-still] :is(.ckf-scrim,.ckf-card){animation:none}
|
|
84
|
+
.ckf-root[data-still] :is(.ckf-scrim,.ckf-card,.ckf-banner){animation:none}
|
|
71
85
|
.ckf-li{display:flex;gap:10px;align-items:flex-start;margin:0}
|
|
72
86
|
.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}
|
|
73
87
|
.ckf-quote{margin:0;padding:12px 14px;border-radius:12px;background:var(--ckf-soft)}
|
|
@@ -106,10 +120,11 @@ var button = (label, kind, onClick, opts = {}) => h(
|
|
|
106
120
|
var closeButton = (label, onClick) => h("button", { type: "button", className: "ckf-x", "aria-label": label, "data-ckf-id": "x", onClick: () => onClick() }, "\xD7");
|
|
107
121
|
var dots = (step, steps) => steps > 1 ? h(
|
|
108
122
|
"span",
|
|
109
|
-
{ className: "ckf-dots", role: "img", "aria-label":
|
|
123
|
+
{ className: "ckf-dots", role: "img", "aria-label": tf("Step {n} of {total}", { n: step, total: steps }) },
|
|
110
124
|
...Array.from({ length: steps }, (_, i) => h("span", { className: "ckf-dot", "data-on": i < step }))
|
|
111
125
|
) : h("span");
|
|
112
126
|
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}`;
|
|
127
|
+
var surfaceKey = (v) => v.type === "cancel" ? "c" : v.type === "step" && (v.step.kind === "question" || v.step.kind === "modal") ? `d:${v.flow.id}` : screenKey(v);
|
|
113
128
|
var rootFor = (theme) => {
|
|
114
129
|
if (!document.getElementById(STYLE_ID)) {
|
|
115
130
|
const style = document.createElement("style");
|
|
@@ -122,11 +137,38 @@ var rootFor = (theme) => {
|
|
|
122
137
|
document.body.appendChild(root);
|
|
123
138
|
return root;
|
|
124
139
|
};
|
|
140
|
+
var setRootLang = (root) => {
|
|
141
|
+
const lang = wordsLang();
|
|
142
|
+
if (lang) root.setAttribute("lang", lang);
|
|
143
|
+
else root.removeAttribute("lang");
|
|
144
|
+
};
|
|
145
|
+
var iconEl = (icon, className) => {
|
|
146
|
+
const i = iconOf(icon);
|
|
147
|
+
if (!i) return false;
|
|
148
|
+
const box = h("span", { className, "aria-hidden": "true" });
|
|
149
|
+
if (i.kind === "emoji") box.textContent = i.value;
|
|
150
|
+
else if (i.kind === "image") box.append(h("img", { src: i.url, alt: "" }));
|
|
151
|
+
else {
|
|
152
|
+
const ns = "http://www.w3.org/2000/svg";
|
|
153
|
+
const svg = document.createElementNS(ns, "svg");
|
|
154
|
+
for (const [k, v] of Object.entries({ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }))
|
|
155
|
+
svg.setAttribute(k, v);
|
|
156
|
+
for (const d of FLOW_ICONS[i.name].paths) {
|
|
157
|
+
const p = document.createElementNS(ns, "path");
|
|
158
|
+
p.setAttribute("d", d);
|
|
159
|
+
svg.append(p);
|
|
160
|
+
}
|
|
161
|
+
box.append(svg);
|
|
162
|
+
}
|
|
163
|
+
return box;
|
|
164
|
+
};
|
|
125
165
|
var mountFlows = (ctl, theme = {}) => {
|
|
126
166
|
if (typeof document === "undefined") return () => {
|
|
127
167
|
};
|
|
128
168
|
const root = rootFor(theme);
|
|
169
|
+
let played = "";
|
|
129
170
|
let lastKey = "";
|
|
171
|
+
let lastSurface = "";
|
|
130
172
|
let returnFocus = null;
|
|
131
173
|
let comment = "";
|
|
132
174
|
let placed = null;
|
|
@@ -136,7 +178,7 @@ var mountFlows = (ctl, theme = {}) => {
|
|
|
136
178
|
const body = [];
|
|
137
179
|
if (v.screen === "survey") {
|
|
138
180
|
body.push(
|
|
139
|
-
h("h2", { className: "ckf-h", id: titleId }, v.flow.question || "Why are you leaving?"),
|
|
181
|
+
h("h2", { className: "ckf-h", id: titleId }, v.flow.question || t("Why are you leaving?")),
|
|
140
182
|
h(
|
|
141
183
|
"div",
|
|
142
184
|
{ className: "ckf-opts", role: "radiogroup", "aria-labelledby": titleId },
|
|
@@ -159,8 +201,8 @@ var mountFlows = (ctl, theme = {}) => {
|
|
|
159
201
|
h(
|
|
160
202
|
"div",
|
|
161
203
|
{ className: "ckf-btns" },
|
|
162
|
-
button("Continue", "primary", ctl.cancel.next, { disabled: !v.reason, id: "go" }),
|
|
163
|
-
button("Never mind, keep my plan", "quiet", ctl.cancel.close)
|
|
204
|
+
button(t("Continue"), "primary", ctl.cancel.next, { disabled: !v.reason, id: "go" }),
|
|
205
|
+
button(t("Never mind, keep my plan"), "quiet", ctl.cancel.close)
|
|
164
206
|
)
|
|
165
207
|
);
|
|
166
208
|
} else if (v.screen === "followup" && v.followUp) {
|
|
@@ -182,14 +224,15 @@ var mountFlows = (ctl, theme = {}) => {
|
|
|
182
224
|
onClick: () => ctl.cancel.answer(a)
|
|
183
225
|
},
|
|
184
226
|
h("span", { className: "ckf-radio" }),
|
|
185
|
-
a
|
|
227
|
+
// The label in the visitor's language; what is recorded stays `a`.
|
|
228
|
+
f.answerLabels?.[i] || a
|
|
186
229
|
)
|
|
187
230
|
)
|
|
188
231
|
);
|
|
189
232
|
else {
|
|
190
233
|
const text = h("textarea", {
|
|
191
234
|
className: "ckf-text",
|
|
192
|
-
placeholder: "Your answer (optional)",
|
|
235
|
+
placeholder: t("Your answer (optional)"),
|
|
193
236
|
"aria-labelledby": titleId,
|
|
194
237
|
maxlength: "500",
|
|
195
238
|
"data-ckf-id": "answer",
|
|
@@ -200,14 +243,14 @@ var mountFlows = (ctl, theme = {}) => {
|
|
|
200
243
|
}
|
|
201
244
|
const label = v.flow.reasons.find((r) => r.id === v.reason)?.label;
|
|
202
245
|
body.push(
|
|
203
|
-
label ? h("p", { className: "ckf-small" },
|
|
246
|
+
label ? h("p", { className: "ckf-small" }, tf("You said: {label}", { label })) : false,
|
|
204
247
|
h("h2", { className: "ckf-h", id: titleId }, f.question),
|
|
205
248
|
field,
|
|
206
249
|
h(
|
|
207
250
|
"div",
|
|
208
251
|
{ className: "ckf-btns" },
|
|
209
|
-
button("Continue", "primary", ctl.cancel.next, { id: "go" }),
|
|
210
|
-
button("Never mind, keep my plan", "quiet", ctl.cancel.close)
|
|
252
|
+
button(t("Continue"), "primary", ctl.cancel.next, { id: "go" }),
|
|
253
|
+
button(t("Never mind, keep my plan"), "quiet", ctl.cancel.close)
|
|
211
254
|
)
|
|
212
255
|
);
|
|
213
256
|
} else if (v.screen === "offer" && v.offer) {
|
|
@@ -223,48 +266,48 @@ var mountFlows = (ctl, theme = {}) => {
|
|
|
223
266
|
h(
|
|
224
267
|
"span",
|
|
225
268
|
{ className: "ckf-small" },
|
|
226
|
-
`/${v.price.interval
|
|
269
|
+
`/${v.price.interval === "year" ? t("year") : t("month")}${o.months ? ` ${forMonths(o.months)}` : ""}`
|
|
227
270
|
)
|
|
228
271
|
) : h(
|
|
229
272
|
"div",
|
|
230
273
|
{ className: "ckf-box" },
|
|
231
|
-
h("span", { className: "ckf-big" },
|
|
232
|
-
o.months ? h("span", { className: "ckf-small" },
|
|
274
|
+
h("span", { className: "ckf-big" }, tf("{percent}% off", { percent: o.percent })),
|
|
275
|
+
o.months ? h("span", { className: "ckf-small" }, forMonths(o.months)) : false
|
|
233
276
|
);
|
|
234
277
|
if (o.kind === "pause" && o.months)
|
|
235
278
|
detail = h(
|
|
236
279
|
"div",
|
|
237
280
|
{ className: "ckf-box" },
|
|
238
|
-
h("span", {},
|
|
281
|
+
h("span", {}, tf("Pause for up to {months}. Your data stays.", { months: monthsWord(o.months) }))
|
|
239
282
|
);
|
|
240
283
|
if (o.kind === "downgrade" && o.planName) detail = h("div", { className: "ckf-box" }, h("b", {}, o.planName));
|
|
241
284
|
const text = o.kind === "contact" ? h("textarea", {
|
|
242
285
|
className: "ckf-text",
|
|
243
|
-
placeholder: "What would make you stay?",
|
|
244
|
-
"aria-label": "Your message",
|
|
286
|
+
placeholder: t("What would make you stay?"),
|
|
287
|
+
"aria-label": t("Your message"),
|
|
245
288
|
maxlength: "500",
|
|
246
289
|
"data-ckf-id": "comment",
|
|
247
290
|
onInput: (e) => comment = e.target.value
|
|
248
291
|
}) : null;
|
|
249
292
|
if (text) text.value = comment;
|
|
250
293
|
body.push(
|
|
251
|
-
label ? h("p", { className: "ckf-small" },
|
|
294
|
+
label ? h("p", { className: "ckf-small" }, tf("You said: {label}", { label })) : false,
|
|
252
295
|
h("h2", { className: "ckf-h", id: titleId }, o.headline),
|
|
253
296
|
detail,
|
|
254
297
|
text ?? false,
|
|
255
298
|
h(
|
|
256
299
|
"div",
|
|
257
300
|
{ className: "ckf-btns" },
|
|
258
|
-
button(busy ? "One moment\u2026" : offerButton(o), "primary", () => ctl.cancel.accept(comment), { disabled: busy, id: "go" }),
|
|
259
|
-
button("No thanks, continue cancelling", "quiet", ctl.cancel.decline, { disabled: busy })
|
|
301
|
+
button(busy ? t("One moment\u2026") : offerButton(o), "primary", () => ctl.cancel.accept(comment), { disabled: busy, id: "go" }),
|
|
302
|
+
button(t("No thanks, continue cancelling"), "quiet", ctl.cancel.decline, { disabled: busy })
|
|
260
303
|
)
|
|
261
304
|
);
|
|
262
305
|
} else {
|
|
263
306
|
const plain = v.steps === 1;
|
|
264
307
|
const text = plain ? null : h("textarea", {
|
|
265
308
|
className: "ckf-text",
|
|
266
|
-
placeholder: "Anything else we should know? (optional)",
|
|
267
|
-
"aria-label": "Anything else we should know? (optional)",
|
|
309
|
+
placeholder: t("Anything else we should know? (optional)"),
|
|
310
|
+
"aria-label": t("Anything else we should know? (optional)"),
|
|
268
311
|
maxlength: "500",
|
|
269
312
|
"data-ckf-id": "comment",
|
|
270
313
|
onInput: (e) => comment = e.target.value
|
|
@@ -277,8 +320,8 @@ var mountFlows = (ctl, theme = {}) => {
|
|
|
277
320
|
h(
|
|
278
321
|
"div",
|
|
279
322
|
{ className: "ckf-btns" },
|
|
280
|
-
button(busy ? "One moment\u2026" : v.flow.confirm.button, "danger", () => ctl.cancel.confirm(comment), { disabled: busy, id: "go" }),
|
|
281
|
-
button("Keep my plan", "secondary", ctl.cancel.close, { disabled: busy })
|
|
323
|
+
button(busy ? t("One moment\u2026") : v.flow.confirm.button, "danger", () => ctl.cancel.confirm(comment), { disabled: busy, id: "go" }),
|
|
324
|
+
button(t("Keep my plan"), "secondary", ctl.cancel.close, { disabled: busy })
|
|
282
325
|
)
|
|
283
326
|
);
|
|
284
327
|
}
|
|
@@ -288,7 +331,7 @@ var mountFlows = (ctl, theme = {}) => {
|
|
|
288
331
|
h(
|
|
289
332
|
"div",
|
|
290
333
|
{ className: "ckf-card", role: "dialog", "aria-modal": "true", "aria-labelledby": titleId },
|
|
291
|
-
h("div", { className: "ckf-top" }, dots(v.step, v.steps), closeButton("Close", ctl.cancel.close)),
|
|
334
|
+
h("div", { className: "ckf-top" }, dots(v.step, v.steps), closeButton(t("Close"), ctl.cancel.close)),
|
|
292
335
|
...body,
|
|
293
336
|
v.error ? h("p", { className: "ckf-err", role: "alert" }, v.error) : false
|
|
294
337
|
)
|
|
@@ -302,24 +345,91 @@ var mountFlows = (ctl, theme = {}) => {
|
|
|
302
345
|
return v.kind === "iframe" ? h(
|
|
303
346
|
"div",
|
|
304
347
|
{ className: "ckf-video" },
|
|
305
|
-
h("iframe", { src: v.src, title: m.alt || "Video", allow: "fullscreen; picture-in-picture", allowfullscreen: true, loading: "lazy", referrerpolicy: "strict-origin-when-cross-origin" })
|
|
348
|
+
h("iframe", { src: v.src, title: m.alt || t("Video"), allow: "fullscreen; picture-in-picture", allowfullscreen: true, loading: "lazy", referrerpolicy: "strict-origin-when-cross-origin" })
|
|
306
349
|
) : h("video", { className: "ckf-media", src: v.src, controls: true, playsinline: true, preload: "metadata", "aria-label": m.alt || void 0, onLoadedmetadata: () => follow() });
|
|
307
350
|
};
|
|
351
|
+
const questionCard = (v) => {
|
|
352
|
+
const s = v.step;
|
|
353
|
+
const q = s.question;
|
|
354
|
+
const titleId = "ckf-title";
|
|
355
|
+
const ok = answerOk(q, v.answer);
|
|
356
|
+
let field = false;
|
|
357
|
+
const textField = (hint, value, write) => {
|
|
358
|
+
const input = h("input", {
|
|
359
|
+
type: "text",
|
|
360
|
+
className: "ckf-input",
|
|
361
|
+
placeholder: hint,
|
|
362
|
+
"aria-labelledby": titleId,
|
|
363
|
+
maxlength: "500",
|
|
364
|
+
"data-ckf-id": "answer",
|
|
365
|
+
onInput: (e) => write(e.target.value),
|
|
366
|
+
onKeydown: (e) => e.key === "Enter" && ctl.onboarding.next()
|
|
367
|
+
});
|
|
368
|
+
input.value = value;
|
|
369
|
+
return input;
|
|
370
|
+
};
|
|
371
|
+
const picked = (on, id, label, press) => h("button", { type: "button", role: "radio", className: "ckf-opt", "aria-checked": String(on), "data-ckf-id": `q:${id}`, onClick: press }, h("span", { className: "ckf-radio" }), label);
|
|
372
|
+
if (q?.type === "choice") {
|
|
373
|
+
const other = !!q.other && !!v.answer?.startsWith(OTHER_PREFIX);
|
|
374
|
+
field = h(
|
|
375
|
+
"div",
|
|
376
|
+
{ className: "ckf-opts", role: "radiogroup", "aria-labelledby": titleId },
|
|
377
|
+
...(q.options ?? []).map((o) => picked(v.answer === o.id, o.id, o.label, () => ctl.onboarding.answer(o.id))),
|
|
378
|
+
q.other ? picked(other, "other", q.otherLabel || t("Other"), () => other || ctl.onboarding.answer(OTHER_PREFIX)) : false,
|
|
379
|
+
other ? textField(t("Your answer"), v.answer.slice(OTHER_PREFIX.length), (t2) => ctl.onboarding.answer(OTHER_PREFIX + t2)) : false
|
|
380
|
+
);
|
|
381
|
+
} else if (q?.type === "scale")
|
|
382
|
+
field = h(
|
|
383
|
+
"div",
|
|
384
|
+
{},
|
|
385
|
+
h(
|
|
386
|
+
"div",
|
|
387
|
+
{ className: "ckf-scale", role: "radiogroup", "aria-labelledby": titleId },
|
|
388
|
+
...scaleOf(q).map(
|
|
389
|
+
(n) => h("button", { type: "button", role: "radio", className: "ckf-num", "aria-checked": String(v.answer === String(n)), "data-ckf-id": `q:${n}`, onClick: () => ctl.onboarding.answer(String(n)) }, String(n))
|
|
390
|
+
)
|
|
391
|
+
),
|
|
392
|
+
q.lowLabel || q.highLabel ? h("div", { className: "ckf-ends ckf-small" }, h("span", {}, q.lowLabel || ""), h("span", {}, q.highLabel || "")) : false
|
|
393
|
+
);
|
|
394
|
+
else if (q) field = textField(q.placeholder || t("Your answer"), v.answer ?? "", (t2) => ctl.onboarding.answer(t2));
|
|
395
|
+
return h(
|
|
396
|
+
"div",
|
|
397
|
+
{ className: "ckf-scrim" },
|
|
398
|
+
h(
|
|
399
|
+
"div",
|
|
400
|
+
{ className: "ckf-card", role: "dialog", "aria-modal": "true", "aria-labelledby": titleId },
|
|
401
|
+
h("div", { className: "ckf-top" }, dots(v.index + 1, v.total), closeButton(t("Close"), ctl.onboarding.dismiss)),
|
|
402
|
+
v.index === 0 ? iconEl(v.flow.icon, "ckf-hic") : false,
|
|
403
|
+
h("h2", { className: "ckf-h", id: titleId }, s.title),
|
|
404
|
+
s.body ? h("p", { className: "ckf-p" }, s.body) : false,
|
|
405
|
+
field,
|
|
406
|
+
h(
|
|
407
|
+
"div",
|
|
408
|
+
{ className: "ckf-row" },
|
|
409
|
+
v.back ? button(t("Back"), "quiet", ctl.onboarding.back) : false,
|
|
410
|
+
q?.optional ? button(q.skipLabel || t("Skip"), "quiet", ctl.onboarding.skip) : false,
|
|
411
|
+
button(s.button || (v.last ? t("Done") : t("Continue")), "primary", ctl.onboarding.next, { disabled: !ok, id: "go" })
|
|
412
|
+
)
|
|
413
|
+
)
|
|
414
|
+
);
|
|
415
|
+
};
|
|
308
416
|
const stepCard = (v) => {
|
|
309
417
|
const s = v.step;
|
|
418
|
+
if (s.kind === "question") return questionCard(v);
|
|
310
419
|
const card = h(
|
|
311
420
|
"div",
|
|
312
421
|
{ className: "ckf-card", role: "dialog", "aria-modal": s.kind === "modal" ? "true" : void 0, "aria-labelledby": "ckf-title" },
|
|
313
422
|
h(
|
|
314
423
|
"div",
|
|
315
424
|
{ className: "ckf-top" },
|
|
316
|
-
v.total > 1 ? h("p", { className: "ckf-small" },
|
|
317
|
-
closeButton("Close the tour", ctl.onboarding.dismiss)
|
|
425
|
+
v.total > 1 ? h("p", { className: "ckf-small" }, tf("Step {n} of {total}", { n: v.index + 1, total: v.total })) : h("span"),
|
|
426
|
+
closeButton(t("Close the tour"), ctl.onboarding.dismiss)
|
|
318
427
|
),
|
|
428
|
+
s.kind === "modal" ? iconEl(v.flow.icon, "ckf-hic") : false,
|
|
319
429
|
media(s),
|
|
320
430
|
h("h2", { className: "ckf-h", id: "ckf-title" }, s.title),
|
|
321
431
|
s.body ? h("p", { className: "ckf-p" }, s.body) : false,
|
|
322
|
-
h("div", { className: "ckf-row" }, button(s.button || (v.last ? "Done" : "Next"), "primary", ctl.onboarding.next, { id: "go" }))
|
|
432
|
+
h("div", { className: "ckf-row" }, button(s.button || (v.last ? t("Done") : t("Next")), "primary", ctl.onboarding.next, { id: "go" }))
|
|
323
433
|
);
|
|
324
434
|
if (s.kind === "modal") return h("div", { className: "ckf-scrim" }, card);
|
|
325
435
|
const target = s.target ? findTarget(s.target) : null;
|
|
@@ -333,10 +443,10 @@ var mountFlows = (ctl, theme = {}) => {
|
|
|
333
443
|
}
|
|
334
444
|
return float;
|
|
335
445
|
};
|
|
336
|
-
const findTarget = (
|
|
337
|
-
const named = () => document.querySelector(`[data-ck-target="${CSS.escape(
|
|
338
|
-
if (!/^[#.[]|[\s>:=]/.test(
|
|
339
|
-
return safeQuery(
|
|
446
|
+
const findTarget = (t2) => {
|
|
447
|
+
const named = () => document.querySelector(`[data-ck-target="${CSS.escape(t2)}"]`);
|
|
448
|
+
if (!/^[#.[]|[\s>:=]/.test(t2)) return named();
|
|
449
|
+
return safeQuery(t2) ?? named();
|
|
340
450
|
};
|
|
341
451
|
const safeQuery = (sel) => {
|
|
342
452
|
try {
|
|
@@ -383,9 +493,9 @@ var mountFlows = (ctl, theme = {}) => {
|
|
|
383
493
|
"div",
|
|
384
494
|
{ className: "ckf-top" },
|
|
385
495
|
h("h2", { className: "ckf-h", id: "ckf-title" }, v.flow.name),
|
|
386
|
-
closeButton("Hide the checklist", ctl.onboarding.dismiss)
|
|
496
|
+
closeButton(t("Hide the checklist"), ctl.onboarding.dismiss)
|
|
387
497
|
),
|
|
388
|
-
h("p", { className: "ckf-small" },
|
|
498
|
+
h("p", { className: "ckf-small" }, tf("{done} of {total} done", { done, total: v.items.length })),
|
|
389
499
|
h(
|
|
390
500
|
"div",
|
|
391
501
|
{ className: "ckf-bar", role: "progressbar", "aria-valuemin": "0", "aria-valuemax": String(v.items.length), "aria-valuenow": String(done) },
|
|
@@ -394,20 +504,21 @@ var mountFlows = (ctl, theme = {}) => {
|
|
|
394
504
|
h(
|
|
395
505
|
"div",
|
|
396
506
|
{ className: "ckf-opts" },
|
|
397
|
-
...v.items.map(({ step, done: d,
|
|
507
|
+
...v.items.map(({ step, done: d, opens }) => {
|
|
398
508
|
const inner = [
|
|
399
509
|
h("span", { className: "ckf-tick", "aria-hidden": "true" }, d ? "\u2713" : ""),
|
|
510
|
+
iconEl(step.icon, "ckf-ic"),
|
|
400
511
|
h("span", {}, h("b", {}, step.title), step.body ? h("span", { className: "ckf-small" }, step.body) : false)
|
|
401
512
|
];
|
|
402
|
-
if (!d &&
|
|
513
|
+
if (!d && opens)
|
|
403
514
|
return h(
|
|
404
515
|
"button",
|
|
405
516
|
{
|
|
406
517
|
type: "button",
|
|
407
518
|
className: "ckf-check",
|
|
408
|
-
"aria-label":
|
|
519
|
+
"aria-label": tf("Start \u201C{title}\u201D", { title: step.title }),
|
|
409
520
|
"data-ckf-id": `t:${step.id}`,
|
|
410
|
-
onClick: () => ctl.onboarding.
|
|
521
|
+
onClick: () => ctl.onboarding.open(step.id)
|
|
411
522
|
},
|
|
412
523
|
...inner,
|
|
413
524
|
h("span", { className: "ckf-go", "aria-hidden": "true" }, "\u203A")
|
|
@@ -417,7 +528,7 @@ var mountFlows = (ctl, theme = {}) => {
|
|
|
417
528
|
{
|
|
418
529
|
type: "button",
|
|
419
530
|
className: "ckf-check",
|
|
420
|
-
"aria-label":
|
|
531
|
+
"aria-label": tf("Mark \u201C{title}\u201D done", { title: step.title }),
|
|
421
532
|
"data-ckf-id": `i:${step.id}`,
|
|
422
533
|
onClick: () => ctl.onboarding.check(step.id)
|
|
423
534
|
},
|
|
@@ -437,7 +548,7 @@ var mountFlows = (ctl, theme = {}) => {
|
|
|
437
548
|
{ className: "ckf-banner", role: "status" },
|
|
438
549
|
h("span", {}, h("b", {}, m.title), m.body ? h("span", { className: "ckf-small" }, m.body) : false),
|
|
439
550
|
action,
|
|
440
|
-
closeButton("Dismiss", ctl.message.dismiss)
|
|
551
|
+
closeButton(t("Dismiss"), ctl.message.dismiss)
|
|
441
552
|
);
|
|
442
553
|
return h(
|
|
443
554
|
"div",
|
|
@@ -445,7 +556,7 @@ var mountFlows = (ctl, theme = {}) => {
|
|
|
445
556
|
h(
|
|
446
557
|
"div",
|
|
447
558
|
{ className: "ckf-card", role: "dialog", "aria-modal": "true", "aria-labelledby": "ckf-title" },
|
|
448
|
-
h("div", { className: "ckf-top" }, h("span"), closeButton("Dismiss", ctl.message.dismiss)),
|
|
559
|
+
h("div", { className: "ckf-top" }, h("span"), closeButton(t("Dismiss"), ctl.message.dismiss)),
|
|
449
560
|
h("h2", { className: "ckf-h", id: "ckf-title" }, m.title),
|
|
450
561
|
m.body ? h("p", { className: "ckf-p" }, m.body) : false,
|
|
451
562
|
action ? h("div", { className: "ckf-row" }, action) : false
|
|
@@ -454,6 +565,14 @@ var mountFlows = (ctl, theme = {}) => {
|
|
|
454
565
|
};
|
|
455
566
|
const draw = () => {
|
|
456
567
|
const v = ctl.view();
|
|
568
|
+
setRootLang(root);
|
|
569
|
+
const accent = theme.accent ?? ctl.accent?.();
|
|
570
|
+
if (accent) root.style.setProperty("--ckf-accent", accent);
|
|
571
|
+
const fin = ctl.finale?.();
|
|
572
|
+
if (fin && fin.key !== played) {
|
|
573
|
+
played = fin.key;
|
|
574
|
+
playFinish(fin.kind, { accent });
|
|
575
|
+
}
|
|
457
576
|
const key = v ? screenKey(v) : "";
|
|
458
577
|
const focused = document.activeElement?.closest?.(".ckf-root") ? document.activeElement?.getAttribute("data-ckf-id") : null;
|
|
459
578
|
if (key && !lastKey && document.activeElement instanceof HTMLElement && !document.activeElement.closest(".ckf-root"))
|
|
@@ -461,13 +580,16 @@ var mountFlows = (ctl, theme = {}) => {
|
|
|
461
580
|
if (key !== lastKey && !key.startsWith("c:")) comment = "";
|
|
462
581
|
if (!key) comment = "";
|
|
463
582
|
placed = null;
|
|
583
|
+
const surface = v ? surfaceKey(v) : "";
|
|
584
|
+
root.toggleAttribute("data-still", !!surface && surface === lastSurface);
|
|
585
|
+
lastSurface = surface;
|
|
464
586
|
root.replaceChildren(
|
|
465
587
|
...v ? [v.type === "cancel" ? cancelCard(v) : v.type === "step" ? stepCard(v) : v.type === "checklist" ? checklist(v) : message(v)] : []
|
|
466
588
|
);
|
|
467
589
|
follow();
|
|
468
590
|
if (key !== lastKey) {
|
|
469
591
|
const modal = root.querySelector("[aria-modal=true]");
|
|
470
|
-
const first = modal?.querySelector("[role=radio], [data-ckf-id=go], textarea") ?? null;
|
|
592
|
+
const first = modal?.querySelector("[role=radio], input, [data-ckf-id=go], textarea") ?? null;
|
|
471
593
|
first?.focus();
|
|
472
594
|
if (!key && returnFocus) {
|
|
473
595
|
returnFocus.focus();
|
|
@@ -481,7 +603,7 @@ var mountFlows = (ctl, theme = {}) => {
|
|
|
481
603
|
const v = ctl.view();
|
|
482
604
|
if (v?.type === "cancel") ctl.cancel.close();
|
|
483
605
|
else if (v?.type === "message") ctl.message.dismiss();
|
|
484
|
-
else if (v?.type === "step" && v.step.kind === "modal") ctl.onboarding.dismiss();
|
|
606
|
+
else if (v?.type === "step" && (v.step.kind === "modal" || v.step.kind === "question")) ctl.onboarding.dismiss();
|
|
485
607
|
};
|
|
486
608
|
function follow() {
|
|
487
609
|
if (placed) place(placed.float, placed.ring, placed.target, placed.side);
|
|
@@ -490,9 +612,11 @@ var mountFlows = (ctl, theme = {}) => {
|
|
|
490
612
|
window.addEventListener("resize", follow);
|
|
491
613
|
window.addEventListener("scroll", follow, { passive: true, capture: true });
|
|
492
614
|
const off = ctl.subscribe(draw);
|
|
615
|
+
const offWords = onWords(draw);
|
|
493
616
|
draw();
|
|
494
617
|
return () => {
|
|
495
618
|
off();
|
|
619
|
+
offWords();
|
|
496
620
|
document.removeEventListener("keydown", onKey);
|
|
497
621
|
window.removeEventListener("resize", follow);
|
|
498
622
|
window.removeEventListener("scroll", follow, { capture: true });
|
|
@@ -507,6 +631,7 @@ var mountPaywall = (host, theme = {}) => {
|
|
|
507
631
|
let returnFocus = null;
|
|
508
632
|
const draw = () => {
|
|
509
633
|
const v = host.view();
|
|
634
|
+
setRootLang(root);
|
|
510
635
|
const key = v ? `${v.paywall.id}:${v.index}` : "";
|
|
511
636
|
if (key && !lastKey && document.activeElement instanceof HTMLElement) returnFocus = document.activeElement;
|
|
512
637
|
if (!v) {
|
|
@@ -520,7 +645,7 @@ var mountPaywall = (host, theme = {}) => {
|
|
|
520
645
|
const p = v.page;
|
|
521
646
|
const plans = p.kind === "plans" ? h(
|
|
522
647
|
"div",
|
|
523
|
-
{ className: "ckf-opts", role: "radiogroup", "aria-label": "Plans" },
|
|
648
|
+
{ className: "ckf-opts", role: "radiogroup", "aria-label": t("Plans") },
|
|
524
649
|
...v.plans.map(
|
|
525
650
|
(plan) => h(
|
|
526
651
|
"button",
|
|
@@ -545,7 +670,7 @@ var mountPaywall = (host, theme = {}) => {
|
|
|
545
670
|
h(
|
|
546
671
|
"div",
|
|
547
672
|
{ className: "ckf-card ckf-pw", role: "dialog", "aria-modal": "true", "aria-labelledby": "ckf-title" },
|
|
548
|
-
h("div", { className: "ckf-top" }, dots(v.index + 1, v.total), closeButton("Close", host.close)),
|
|
673
|
+
h("div", { className: "ckf-top" }, dots(v.index + 1, v.total), closeButton(t("Close"), host.close)),
|
|
549
674
|
h("h2", { className: "ckf-h", id: "ckf-title" }, p.title),
|
|
550
675
|
p.body ? h("p", { className: "ckf-p" }, p.body) : false,
|
|
551
676
|
p.kind === "benefits" && p.items?.length ? h("div", { className: "ckf-opts" }, ...p.items.map((i) => h("p", { className: "ckf-li" }, i))) : false,
|
|
@@ -555,11 +680,11 @@ var mountPaywall = (host, theme = {}) => {
|
|
|
555
680
|
h(
|
|
556
681
|
"div",
|
|
557
682
|
{ className: "ckf-btns" },
|
|
558
|
-
button(v.busy ? "One moment\u2026" : p.button || "Continue", "primary", host.next, {
|
|
683
|
+
button(v.busy ? t("One moment\u2026") : p.button || t("Continue"), "primary", host.next, {
|
|
559
684
|
disabled: v.busy || p.kind === "plans" && !v.plan,
|
|
560
685
|
id: "go"
|
|
561
686
|
}),
|
|
562
|
-
v.index ? button("Back", "quiet", host.back, { disabled: v.busy }) : false
|
|
687
|
+
v.index ? button(t("Back"), "quiet", host.back, { disabled: v.busy }) : false
|
|
563
688
|
)
|
|
564
689
|
)
|
|
565
690
|
)
|
|
@@ -573,9 +698,11 @@ var mountPaywall = (host, theme = {}) => {
|
|
|
573
698
|
};
|
|
574
699
|
document.addEventListener("keydown", onKey);
|
|
575
700
|
const off = host.subscribe(draw);
|
|
701
|
+
const offWords = onWords(draw);
|
|
576
702
|
draw();
|
|
577
703
|
return () => {
|
|
578
704
|
off();
|
|
705
|
+
offWords();
|
|
579
706
|
document.removeEventListener("keydown", onKey);
|
|
580
707
|
root.remove();
|
|
581
708
|
};
|