@appfunnel-dev/sdk 2.0.0-canary.1 → 2.0.0-canary.11
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/README.md +1 -1
- package/dist/capabilities-Dq22RCr_.d.cts +180 -0
- package/dist/capabilities-Dq22RCr_.d.ts +180 -0
- package/dist/checkout-ClaO5IYV.d.ts +333 -0
- package/dist/checkout-DBp4bCpC.d.cts +333 -0
- package/dist/chunk-7JLOF6CJ.cjs +172 -0
- package/dist/chunk-7JLOF6CJ.cjs.map +1 -0
- package/dist/chunk-EMMSS5I5.cjs +37 -0
- package/dist/chunk-EMMSS5I5.cjs.map +1 -0
- package/dist/chunk-G3PMV62Z.js +33 -0
- package/dist/chunk-G3PMV62Z.js.map +1 -0
- package/dist/chunk-JAO6AA4R.js +99 -0
- package/dist/chunk-JAO6AA4R.js.map +1 -0
- package/dist/chunk-OXQBEKZ5.js +157 -0
- package/dist/chunk-OXQBEKZ5.js.map +1 -0
- package/dist/chunk-RKPRMTSU.cjs +517 -0
- package/dist/chunk-RKPRMTSU.cjs.map +1 -0
- package/dist/chunk-SXENKZ4U.js +486 -0
- package/dist/chunk-SXENKZ4U.js.map +1 -0
- package/dist/chunk-VV7TFC64.cjs +106 -0
- package/dist/chunk-VV7TFC64.cjs.map +1 -0
- package/dist/chunk-WYUDL4FI.cjs +8 -0
- package/dist/chunk-WYUDL4FI.cjs.map +1 -0
- package/dist/chunk-ZZJG4EYL.js +6 -0
- package/dist/chunk-ZZJG4EYL.js.map +1 -0
- package/dist/driver-paddle.cjs +817 -0
- package/dist/driver-paddle.cjs.map +1 -0
- package/dist/driver-paddle.d.cts +10 -0
- package/dist/driver-paddle.d.ts +10 -0
- package/dist/driver-paddle.js +814 -0
- package/dist/driver-paddle.js.map +1 -0
- package/dist/driver-stripe.cjs +2312 -0
- package/dist/driver-stripe.cjs.map +1 -0
- package/dist/driver-stripe.d.cts +24 -0
- package/dist/driver-stripe.d.ts +24 -0
- package/dist/driver-stripe.js +2305 -0
- package/dist/driver-stripe.js.map +1 -0
- package/dist/index.cjs +2677 -846
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +358 -1005
- package/dist/index.d.ts +358 -1005
- package/dist/index.js +2395 -698
- package/dist/index.js.map +1 -1
- package/dist/manifest-C2mDXWNU.d.cts +996 -0
- package/dist/manifest-C2mDXWNU.d.ts +996 -0
- package/dist/manifest-entry.cjs +408 -0
- package/dist/manifest-entry.cjs.map +1 -0
- package/dist/manifest-entry.d.cts +192 -0
- package/dist/manifest-entry.d.ts +192 -0
- package/dist/manifest-entry.js +270 -0
- package/dist/manifest-entry.js.map +1 -0
- package/dist/protocol.cjs +13 -0
- package/dist/protocol.cjs.map +1 -0
- package/dist/protocol.d.cts +182 -0
- package/dist/protocol.d.ts +182 -0
- package/dist/protocol.js +4 -0
- package/dist/protocol.js.map +1 -0
- package/package.json +49 -4
package/dist/index.cjs
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
var
|
|
5
|
-
var
|
|
3
|
+
var chunkRKPRMTSU_cjs = require('./chunk-RKPRMTSU.cjs');
|
|
4
|
+
var chunkWYUDL4FI_cjs = require('./chunk-WYUDL4FI.cjs');
|
|
5
|
+
var chunk7JLOF6CJ_cjs = require('./chunk-7JLOF6CJ.cjs');
|
|
6
|
+
require('./chunk-EMMSS5I5.cjs');
|
|
7
|
+
var React = require('react');
|
|
8
|
+
var ReactDOM = require('react-dom');
|
|
9
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
6
10
|
|
|
7
|
-
|
|
11
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
12
|
+
|
|
13
|
+
var React__default = /*#__PURE__*/_interopDefault(React);
|
|
14
|
+
var ReactDOM__default = /*#__PURE__*/_interopDefault(ReactDOM);
|
|
8
15
|
|
|
9
16
|
// src/state/variableStore.ts
|
|
10
17
|
var VariableStore = class {
|
|
@@ -258,8 +265,12 @@ function collectClickIds() {
|
|
|
258
265
|
}
|
|
259
266
|
function readCookie(name) {
|
|
260
267
|
if (typeof document === "undefined") return void 0;
|
|
261
|
-
|
|
262
|
-
|
|
268
|
+
try {
|
|
269
|
+
const m = document.cookie.match(new RegExp("(?:^|; )" + name + "=([^;]*)"));
|
|
270
|
+
return m ? decodeURIComponent(m[1]) : void 0;
|
|
271
|
+
} catch {
|
|
272
|
+
return void 0;
|
|
273
|
+
}
|
|
263
274
|
}
|
|
264
275
|
function buildAcquisition(context) {
|
|
265
276
|
return {
|
|
@@ -272,41 +283,44 @@ function buildAcquisition(context) {
|
|
|
272
283
|
}
|
|
273
284
|
function buildContext(opts = {}) {
|
|
274
285
|
const raw = detect();
|
|
275
|
-
const
|
|
286
|
+
const now2 = opts.now ?? 0;
|
|
276
287
|
const referrer = typeof document !== "undefined" ? document.referrer : "";
|
|
277
|
-
const
|
|
288
|
+
const base = {
|
|
278
289
|
device: raw.device,
|
|
279
290
|
browser: raw.browser,
|
|
280
291
|
os: raw.os,
|
|
281
292
|
locale: raw.locale,
|
|
282
293
|
utm: collectUtm(),
|
|
283
294
|
clickIds: collectClickIds(),
|
|
284
|
-
page: { key: "", slug: "", index: 0, total: 0, progressPercentage: 0, startedAt:
|
|
285
|
-
session: { id: opts.sessionId ?? null, startedAt:
|
|
295
|
+
page: { key: "", slug: "", index: 0, total: 0, progressPercentage: 0, startedAt: now2 },
|
|
296
|
+
session: { id: opts.sessionId ?? null, startedAt: now2, referrer },
|
|
286
297
|
identity: { customerId: opts.customerId ?? null, visitorId: opts.visitorId ?? null },
|
|
287
298
|
system: {
|
|
288
299
|
funnelId: opts.funnelId ?? "",
|
|
289
300
|
campaignId: opts.campaignId ?? "",
|
|
290
301
|
mode: opts.mode ?? "live",
|
|
291
|
-
now
|
|
302
|
+
now: now2
|
|
292
303
|
}
|
|
293
304
|
};
|
|
294
|
-
if (!opts.overrides) return
|
|
305
|
+
if (!opts.overrides) return base;
|
|
295
306
|
return {
|
|
296
|
-
...
|
|
307
|
+
...base,
|
|
297
308
|
...opts.overrides,
|
|
298
|
-
device: { ...
|
|
299
|
-
browser: { ...
|
|
300
|
-
os: { ...
|
|
301
|
-
locale: { ...
|
|
302
|
-
utm: { ...
|
|
303
|
-
clickIds: { ...
|
|
304
|
-
page: { ...
|
|
305
|
-
session: { ...
|
|
306
|
-
identity: { ...
|
|
307
|
-
system: { ...
|
|
309
|
+
device: { ...base.device, ...opts.overrides.device },
|
|
310
|
+
browser: { ...base.browser, ...opts.overrides.browser },
|
|
311
|
+
os: { ...base.os, ...opts.overrides.os },
|
|
312
|
+
locale: { ...base.locale, ...opts.overrides.locale },
|
|
313
|
+
utm: { ...base.utm, ...opts.overrides.utm },
|
|
314
|
+
clickIds: { ...base.clickIds, ...opts.overrides.clickIds },
|
|
315
|
+
page: { ...base.page, ...opts.overrides.page },
|
|
316
|
+
session: { ...base.session, ...opts.overrides.session },
|
|
317
|
+
identity: { ...base.identity, ...opts.overrides.identity },
|
|
318
|
+
system: { ...base.system, ...opts.overrides.system }
|
|
308
319
|
};
|
|
309
320
|
}
|
|
321
|
+
function exposurePayload(experimentId, variant, version) {
|
|
322
|
+
return { experimentId, variant, ...version !== void 0 ? { version } : {} };
|
|
323
|
+
}
|
|
310
324
|
var _eventIdCounter = 0;
|
|
311
325
|
function newEventId() {
|
|
312
326
|
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") return crypto.randomUUID();
|
|
@@ -323,20 +337,20 @@ function createConsoleTracker(opts = {}) {
|
|
|
323
337
|
setAcquisition: (acq) => log("acquisition", acq)
|
|
324
338
|
};
|
|
325
339
|
}
|
|
326
|
-
var TrackerContext =
|
|
340
|
+
var TrackerContext = React.createContext(null);
|
|
327
341
|
function TrackerProvider({
|
|
328
342
|
tracker,
|
|
329
343
|
children
|
|
330
344
|
}) {
|
|
331
|
-
return
|
|
345
|
+
return React.createElement(TrackerContext.Provider, { value: tracker }, children);
|
|
332
346
|
}
|
|
333
347
|
function useTrackerRef() {
|
|
334
|
-
return
|
|
348
|
+
return React.useContext(TrackerContext) ?? FALLBACK;
|
|
335
349
|
}
|
|
336
350
|
var FALLBACK = createConsoleTracker({ silent: true });
|
|
337
351
|
function useTracker() {
|
|
338
352
|
const tracker = useTrackerRef();
|
|
339
|
-
const track =
|
|
353
|
+
const track = React.useCallback(
|
|
340
354
|
(event, data, userData) => tracker.track(event, data, userData),
|
|
341
355
|
[tracker]
|
|
342
356
|
);
|
|
@@ -345,7 +359,7 @@ function useTracker() {
|
|
|
345
359
|
|
|
346
360
|
// src/tracking/bus.ts
|
|
347
361
|
var MAX_HISTORY = 250;
|
|
348
|
-
function createBus(accessors,
|
|
362
|
+
function createBus(accessors, now2 = () => Date.now()) {
|
|
349
363
|
const listeners = /* @__PURE__ */ new Map();
|
|
350
364
|
const events = [];
|
|
351
365
|
const on = (event, cb) => {
|
|
@@ -358,7 +372,7 @@ function createBus(accessors, now = () => Date.now()) {
|
|
|
358
372
|
listeners.get(event)?.delete(cb);
|
|
359
373
|
};
|
|
360
374
|
const emit = (event, data) => {
|
|
361
|
-
events.push({ event, data, ts:
|
|
375
|
+
events.push({ event, data, ts: now2() });
|
|
362
376
|
if (events.length > MAX_HISTORY) events.shift();
|
|
363
377
|
for (const cb of listeners.get(event) ?? []) {
|
|
364
378
|
try {
|
|
@@ -373,7 +387,40 @@ function createBus(accessors, now = () => Date.now()) {
|
|
|
373
387
|
}
|
|
374
388
|
}
|
|
375
389
|
};
|
|
376
|
-
|
|
390
|
+
let nav = null;
|
|
391
|
+
let warnedNoNav = false;
|
|
392
|
+
const currentNav = () => {
|
|
393
|
+
if (!nav && !warnedNoNav) {
|
|
394
|
+
warnedNoNav = true;
|
|
395
|
+
console.warn(
|
|
396
|
+
"[appfunnel] window.appfunnel navigation called before the funnel flow mounted \u2014 ignored"
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
return nav;
|
|
400
|
+
};
|
|
401
|
+
const registerNavigation = (n) => {
|
|
402
|
+
nav = n;
|
|
403
|
+
warnedNoNav = false;
|
|
404
|
+
return () => {
|
|
405
|
+
if (nav === n) nav = null;
|
|
406
|
+
};
|
|
407
|
+
};
|
|
408
|
+
const goTo = (pageKey) => currentNav()?.goTo(pageKey) ?? false;
|
|
409
|
+
const goNext = () => currentNav()?.goNext();
|
|
410
|
+
const goPrevious = () => currentNav()?.goPrevious();
|
|
411
|
+
const navigation = () => nav?.snapshot() ?? null;
|
|
412
|
+
return {
|
|
413
|
+
on,
|
|
414
|
+
off,
|
|
415
|
+
events,
|
|
416
|
+
emit,
|
|
417
|
+
registerNavigation,
|
|
418
|
+
goTo,
|
|
419
|
+
goNext,
|
|
420
|
+
goPrevious,
|
|
421
|
+
navigation,
|
|
422
|
+
...accessors
|
|
423
|
+
};
|
|
377
424
|
}
|
|
378
425
|
function withBus(tracker, bus) {
|
|
379
426
|
return {
|
|
@@ -395,7 +442,19 @@ function withBus(tracker, bus) {
|
|
|
395
442
|
function attachBus(bus) {
|
|
396
443
|
if (typeof window === "undefined") return () => {
|
|
397
444
|
};
|
|
398
|
-
const {
|
|
445
|
+
const {
|
|
446
|
+
on,
|
|
447
|
+
off,
|
|
448
|
+
getVariable,
|
|
449
|
+
getVariables,
|
|
450
|
+
getCurrentPageId,
|
|
451
|
+
getCustomerId,
|
|
452
|
+
getVisitorId,
|
|
453
|
+
goTo,
|
|
454
|
+
goNext,
|
|
455
|
+
goPrevious,
|
|
456
|
+
navigation
|
|
457
|
+
} = bus;
|
|
399
458
|
const api = {
|
|
400
459
|
on,
|
|
401
460
|
off,
|
|
@@ -404,6 +463,11 @@ function attachBus(bus) {
|
|
|
404
463
|
getCurrentPageId,
|
|
405
464
|
getCustomerId,
|
|
406
465
|
getVisitorId,
|
|
466
|
+
// Flow control (bound closures over the bus's live nav registration).
|
|
467
|
+
goTo,
|
|
468
|
+
goNext,
|
|
469
|
+
goPrevious,
|
|
470
|
+
navigation,
|
|
407
471
|
get events() {
|
|
408
472
|
return bus.events;
|
|
409
473
|
}
|
|
@@ -413,21 +477,6 @@ function attachBus(bus) {
|
|
|
413
477
|
if (window.appfunnel === api) delete window.appfunnel;
|
|
414
478
|
};
|
|
415
479
|
}
|
|
416
|
-
var RTL_LANGS = /* @__PURE__ */ new Set(["ar", "he", "fa", "ur", "ps", "sd", "dv", "yi"]);
|
|
417
|
-
function isRtl(locale) {
|
|
418
|
-
return RTL_LANGS.has(locale.split("-")[0]);
|
|
419
|
-
}
|
|
420
|
-
var base = (l) => l?.split("-")[0];
|
|
421
|
-
function resolveLocale(config, detected, override) {
|
|
422
|
-
const supported = config?.supported ?? (config?.default ? [config.default] : ["en"]);
|
|
423
|
-
const def = config?.default ?? supported[0] ?? "en";
|
|
424
|
-
const pick = (l) => {
|
|
425
|
-
if (!l) return void 0;
|
|
426
|
-
if (supported.includes(l)) return l;
|
|
427
|
-
return supported.find((s) => base(s) === base(l));
|
|
428
|
-
};
|
|
429
|
-
return pick(override) ?? pick(detected) ?? def;
|
|
430
|
-
}
|
|
431
480
|
function interpolate(str, params) {
|
|
432
481
|
if (!params) return str;
|
|
433
482
|
return str.replace(/\{\{(\w+)\}\}/g, (_, k) => k in params ? String(params[k]) : `{{${k}}}`);
|
|
@@ -447,7 +496,7 @@ function devWarn(message) {
|
|
|
447
496
|
} catch {
|
|
448
497
|
}
|
|
449
498
|
}
|
|
450
|
-
var LocaleContext =
|
|
499
|
+
var LocaleContext = React.createContext(null);
|
|
451
500
|
function LocaleProvider({
|
|
452
501
|
config,
|
|
453
502
|
catalog = {},
|
|
@@ -455,26 +504,29 @@ function LocaleProvider({
|
|
|
455
504
|
override,
|
|
456
505
|
children
|
|
457
506
|
}) {
|
|
458
|
-
const [locale, setLocale] =
|
|
459
|
-
|
|
507
|
+
const [locale, setLocale] = React.useState(() => chunkRKPRMTSU_cjs.resolveLocale(config, detected, override));
|
|
508
|
+
React.useEffect(() => {
|
|
509
|
+
setLocale(chunkRKPRMTSU_cjs.resolveLocale(config, detected, override));
|
|
510
|
+
}, [config, detected, override]);
|
|
511
|
+
const value = React.useMemo(
|
|
460
512
|
() => ({ locale, setLocale, config, catalog }),
|
|
461
513
|
[locale, config, catalog]
|
|
462
514
|
);
|
|
463
|
-
return
|
|
515
|
+
return React.createElement(LocaleContext.Provider, { value }, children);
|
|
464
516
|
}
|
|
465
517
|
function useActiveLocale() {
|
|
466
|
-
return
|
|
518
|
+
return React.useContext(LocaleContext)?.locale ?? "en";
|
|
467
519
|
}
|
|
468
520
|
function useTranslation() {
|
|
469
|
-
const ctx =
|
|
521
|
+
const ctx = React.useContext(LocaleContext);
|
|
470
522
|
if (!ctx) throw new Error("useTranslation must be used inside <FunnelProvider>");
|
|
471
523
|
const { locale, setLocale, config, catalog } = ctx;
|
|
472
|
-
const chain =
|
|
524
|
+
const chain = React.useMemo(() => {
|
|
473
525
|
const def = config?.default;
|
|
474
526
|
const fb = config?.fallback;
|
|
475
527
|
return Array.from(new Set([locale, fb, def].filter(Boolean)));
|
|
476
528
|
}, [locale, config]);
|
|
477
|
-
const t =
|
|
529
|
+
const t = React.useMemo(() => {
|
|
478
530
|
const fn = ((key, params) => {
|
|
479
531
|
const raw = lookup(catalog, chain, key);
|
|
480
532
|
if (raw === void 0) {
|
|
@@ -484,17 +536,40 @@ function useTranslation() {
|
|
|
484
536
|
return interpolate(raw, params);
|
|
485
537
|
});
|
|
486
538
|
fn.plural = (n, forms, params) => {
|
|
487
|
-
|
|
539
|
+
let rule;
|
|
540
|
+
try {
|
|
541
|
+
rule = new Intl.PluralRules(locale).select(n);
|
|
542
|
+
} catch {
|
|
543
|
+
rule = "other";
|
|
544
|
+
}
|
|
488
545
|
const form = forms[rule] ?? forms.other ?? "";
|
|
489
546
|
return interpolate(form.replace(/#/g, String(n)), params);
|
|
490
547
|
};
|
|
491
548
|
return fn;
|
|
492
549
|
}, [catalog, chain, locale]);
|
|
493
|
-
const fmt =
|
|
550
|
+
const fmt = React.useMemo(
|
|
494
551
|
() => ({
|
|
495
|
-
number: (n, opts) =>
|
|
496
|
-
|
|
497
|
-
|
|
552
|
+
number: (n, opts) => {
|
|
553
|
+
try {
|
|
554
|
+
return new Intl.NumberFormat(locale, opts).format(n);
|
|
555
|
+
} catch {
|
|
556
|
+
return String(n);
|
|
557
|
+
}
|
|
558
|
+
},
|
|
559
|
+
date: (d, opts) => {
|
|
560
|
+
try {
|
|
561
|
+
return new Intl.DateTimeFormat(locale, opts).format(d);
|
|
562
|
+
} catch {
|
|
563
|
+
return String(d instanceof Date ? d.toISOString() : d);
|
|
564
|
+
}
|
|
565
|
+
},
|
|
566
|
+
percent: (n, opts) => {
|
|
567
|
+
try {
|
|
568
|
+
return new Intl.NumberFormat(locale, { style: "percent", ...opts }).format(n);
|
|
569
|
+
} catch {
|
|
570
|
+
return `${(n * 100).toFixed(0)}%`;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
498
573
|
}),
|
|
499
574
|
[locale]
|
|
500
575
|
);
|
|
@@ -503,351 +578,191 @@ function useTranslation() {
|
|
|
503
578
|
fmt,
|
|
504
579
|
locale,
|
|
505
580
|
setLocale,
|
|
506
|
-
dir: isRtl(locale) ? "rtl" : "ltr",
|
|
581
|
+
dir: chunkRKPRMTSU_cjs.isRtl(locale) ? "rtl" : "ltr",
|
|
507
582
|
locales: config?.supported ?? [locale]
|
|
508
583
|
};
|
|
509
584
|
}
|
|
510
585
|
|
|
511
586
|
// src/commerce/catalog.ts
|
|
512
|
-
var
|
|
513
|
-
day: 1,
|
|
514
|
-
week: 7,
|
|
515
|
-
month: 30,
|
|
516
|
-
year: 365,
|
|
517
|
-
one_time: 0
|
|
518
|
-
};
|
|
519
|
-
function periodDays(interval, count) {
|
|
520
|
-
return PERIOD_DAYS[interval] * count;
|
|
521
|
-
}
|
|
522
|
-
function intervalLabel(interval, count) {
|
|
523
|
-
if (interval === "one_time") return { period: "one-time", periodly: "one-time" };
|
|
524
|
-
if (interval === "month" && count === 3) return { period: "quarter", periodly: "quarterly" };
|
|
525
|
-
if (interval === "month" && count === 6) return { period: "6 months", periodly: "semiannually" };
|
|
526
|
-
if (interval === "week" && count === 2) return { period: "2 weeks", periodly: "biweekly" };
|
|
527
|
-
if (count === 1) {
|
|
528
|
-
const periodly = { day: "daily", week: "weekly", month: "monthly", year: "yearly" }[interval];
|
|
529
|
-
return { period: interval, periodly };
|
|
530
|
-
}
|
|
531
|
-
return { period: `${count} ${interval}s`, periodly: `every ${count} ${interval}s` };
|
|
532
|
-
}
|
|
533
|
-
function resolveProduct(input) {
|
|
534
|
-
const interval = input.interval ?? "one_time";
|
|
535
|
-
const count = input.intervalCount ?? 1;
|
|
536
|
-
const days = periodDays(interval, count);
|
|
537
|
-
const perDayMinor = days > 0 ? input.amount / days : input.amount;
|
|
538
|
-
const label = intervalLabel(interval, count);
|
|
539
|
-
return {
|
|
540
|
-
id: input.id,
|
|
541
|
-
name: input.name ?? input.id,
|
|
542
|
-
displayName: input.displayName ?? input.name ?? input.id,
|
|
543
|
-
provider: input.provider ?? "stripe",
|
|
544
|
-
currency: input.currency,
|
|
545
|
-
priceMinor: input.amount,
|
|
546
|
-
perDayMinor,
|
|
547
|
-
perWeekMinor: perDayMinor * 7,
|
|
548
|
-
perMonthMinor: perDayMinor * 30,
|
|
549
|
-
perYearMinor: perDayMinor * 365,
|
|
550
|
-
period: label.period,
|
|
551
|
-
periodly: label.periodly,
|
|
552
|
-
hasTrial: (input.trialDays ?? 0) > 0,
|
|
553
|
-
trialDays: input.trialDays ?? 0,
|
|
554
|
-
trialMinor: input.trialAmount ?? 0
|
|
555
|
-
};
|
|
556
|
-
}
|
|
557
|
-
function buildCatalog(inputs) {
|
|
558
|
-
return new Map(inputs.map((i) => [i.id, resolveProduct(i)]));
|
|
559
|
-
}
|
|
560
|
-
function formatMoney(minor, currency, locale) {
|
|
561
|
-
const amount = minor / 100;
|
|
562
|
-
const code = currency.toUpperCase();
|
|
563
|
-
let formatted;
|
|
564
|
-
try {
|
|
565
|
-
formatted = new Intl.NumberFormat(locale, { style: "currency", currency: code }).format(amount);
|
|
566
|
-
} catch {
|
|
567
|
-
formatted = `${code} ${amount.toFixed(2)}`;
|
|
568
|
-
}
|
|
569
|
-
return { amount, minor, currency: code, formatted };
|
|
570
|
-
}
|
|
571
|
-
function formatProduct(r, locale) {
|
|
572
|
-
const m = (minor) => formatMoney(minor, r.currency, locale);
|
|
573
|
-
return {
|
|
574
|
-
id: r.id,
|
|
575
|
-
name: r.name,
|
|
576
|
-
displayName: r.displayName,
|
|
577
|
-
provider: r.provider,
|
|
578
|
-
price: m(r.priceMinor),
|
|
579
|
-
period: r.period,
|
|
580
|
-
periodly: r.periodly,
|
|
581
|
-
perDay: m(r.perDayMinor),
|
|
582
|
-
perWeek: m(r.perWeekMinor),
|
|
583
|
-
perMonth: m(r.perMonthMinor),
|
|
584
|
-
perYear: m(r.perYearMinor),
|
|
585
|
-
hasTrial: r.hasTrial,
|
|
586
|
-
trialDays: r.trialDays,
|
|
587
|
-
trialPrice: r.hasTrial ? m(r.trialMinor) : null
|
|
588
|
-
};
|
|
589
|
-
}
|
|
590
|
-
var CatalogContext = react.createContext(null);
|
|
587
|
+
var CatalogContext = React.createContext(null);
|
|
591
588
|
function CatalogProvider({
|
|
592
589
|
catalog,
|
|
593
590
|
children
|
|
594
591
|
}) {
|
|
595
|
-
return
|
|
592
|
+
return React.createElement(CatalogContext.Provider, { value: catalog }, children);
|
|
596
593
|
}
|
|
597
594
|
function useCatalog() {
|
|
598
|
-
return
|
|
595
|
+
return React.useContext(CatalogContext) ?? EMPTY;
|
|
599
596
|
}
|
|
600
597
|
var EMPTY = /* @__PURE__ */ new Map();
|
|
601
|
-
function
|
|
598
|
+
function useOffering(id) {
|
|
602
599
|
const catalog = useCatalog();
|
|
603
600
|
const locale = useActiveLocale();
|
|
604
601
|
const resolved = id ? catalog.get(id) : void 0;
|
|
605
|
-
return
|
|
602
|
+
return React.useMemo(
|
|
603
|
+
() => resolved ? chunkRKPRMTSU_cjs.formatOffering(resolved, locale) : void 0,
|
|
604
|
+
[resolved, locale]
|
|
605
|
+
);
|
|
606
606
|
}
|
|
607
|
-
function
|
|
607
|
+
function useOfferings() {
|
|
608
608
|
const catalog = useCatalog();
|
|
609
609
|
const locale = useActiveLocale();
|
|
610
|
-
return
|
|
611
|
-
() => Array.from(catalog.values()).map((r) =>
|
|
610
|
+
return React.useMemo(
|
|
611
|
+
() => Array.from(catalog.values()).map((r) => chunkRKPRMTSU_cjs.formatOffering(r, locale)),
|
|
612
612
|
[catalog, locale]
|
|
613
613
|
);
|
|
614
614
|
}
|
|
615
615
|
|
|
616
|
-
// src/flow/
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
616
|
+
// src/flow/screenshot.ts
|
|
617
|
+
var MIME = { png: "image/png", jpeg: "image/jpeg", webp: "image/webp" };
|
|
618
|
+
var ScreenshotError = class extends Error {
|
|
619
|
+
constructor(code, message) {
|
|
620
|
+
super(message);
|
|
621
|
+
this.code = code;
|
|
622
622
|
}
|
|
623
|
-
return cur;
|
|
624
|
-
}
|
|
625
|
-
function evaluateCondition(cond, s) {
|
|
626
|
-
const val = readField(s, cond.field);
|
|
627
|
-
const { op, value } = cond;
|
|
628
|
-
switch (op) {
|
|
629
|
-
case "eq":
|
|
630
|
-
return val === value;
|
|
631
|
-
case "neq":
|
|
632
|
-
return val !== value;
|
|
633
|
-
case "in":
|
|
634
|
-
return Array.isArray(value) && value.includes(val);
|
|
635
|
-
case "gt":
|
|
636
|
-
return typeof val === "number" && typeof value === "number" && val > value;
|
|
637
|
-
case "gte":
|
|
638
|
-
return typeof val === "number" && typeof value === "number" && val >= value;
|
|
639
|
-
case "lt":
|
|
640
|
-
return typeof val === "number" && typeof value === "number" && val < value;
|
|
641
|
-
case "lte":
|
|
642
|
-
return typeof val === "number" && typeof value === "number" && val <= value;
|
|
643
|
-
case "contains":
|
|
644
|
-
if (typeof val === "string") return val.includes(String(value));
|
|
645
|
-
if (Array.isArray(val)) return val.includes(value);
|
|
646
|
-
return false;
|
|
647
|
-
case "exists":
|
|
648
|
-
return val !== void 0 && val !== null && val !== "";
|
|
649
|
-
case "empty":
|
|
650
|
-
return val === void 0 || val === null || val === "" || Array.isArray(val) && val.length === 0;
|
|
651
|
-
default:
|
|
652
|
-
return false;
|
|
653
|
-
}
|
|
654
|
-
}
|
|
655
|
-
function evaluateGate(gate, s) {
|
|
656
|
-
return typeof gate === "function" ? gate(s) : evaluateCondition(gate, s);
|
|
657
|
-
}
|
|
658
|
-
function resolveRoute(routes, s) {
|
|
659
|
-
if (!routes) return void 0;
|
|
660
|
-
for (const route of routes) {
|
|
661
|
-
if (!route.when || evaluateGate(route.when, s)) return route.to;
|
|
662
|
-
}
|
|
663
|
-
return void 0;
|
|
664
|
-
}
|
|
665
|
-
function pageMeta(meta) {
|
|
666
|
-
return meta;
|
|
667
|
-
}
|
|
668
|
-
var TYPE_GUARDS = {
|
|
669
|
-
paywall: { field: "user.email", op: "exists" },
|
|
670
|
-
upsell: { field: "user.email", op: "exists" }
|
|
671
623
|
};
|
|
672
|
-
function
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
function
|
|
679
|
-
const
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
}
|
|
685
|
-
function outgoingKeys(pages, currentKey) {
|
|
686
|
-
const idx = pages.findIndex((p) => p.key === currentKey);
|
|
687
|
-
if (idx === -1) return [];
|
|
688
|
-
const out = /* @__PURE__ */ new Set();
|
|
689
|
-
for (const route of pages[idx].meta?.next ?? []) out.add(route.to);
|
|
690
|
-
const linear = pages[idx + 1]?.key;
|
|
691
|
-
if (linear) out.add(linear);
|
|
692
|
-
return [...out];
|
|
693
|
-
}
|
|
694
|
-
function expectedPathLength(pages, startKey, s) {
|
|
695
|
-
const seen = /* @__PURE__ */ new Set();
|
|
696
|
-
let key = startKey;
|
|
697
|
-
let count = 0;
|
|
698
|
-
while (key && !seen.has(key)) {
|
|
699
|
-
seen.add(key);
|
|
700
|
-
count++;
|
|
701
|
-
if (pages.find((p) => p.key === key)?.meta?.type === "finish") break;
|
|
702
|
-
key = nextPage(pages, key, s);
|
|
703
|
-
}
|
|
704
|
-
return count;
|
|
705
|
-
}
|
|
706
|
-
function defineFunnel(def) {
|
|
707
|
-
return def;
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
// src/flow/experiments.ts
|
|
711
|
-
function fnv1a(input) {
|
|
712
|
-
let h = 2166136261;
|
|
713
|
-
for (let i = 0; i < input.length; i++) {
|
|
714
|
-
h ^= input.charCodeAt(i);
|
|
715
|
-
h = h + ((h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24)) >>> 0;
|
|
624
|
+
function resolveTarget(t) {
|
|
625
|
+
if (!t || t.kind === "page") return document.getElementById("root") ?? document.body;
|
|
626
|
+
if (t.kind === "viewport") return document.body;
|
|
627
|
+
return document.querySelector(t.selector);
|
|
628
|
+
}
|
|
629
|
+
var delay = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
630
|
+
function hideAll(selectors) {
|
|
631
|
+
const touched = [];
|
|
632
|
+
for (const sel of selectors) {
|
|
633
|
+
document.querySelectorAll(sel).forEach((el) => {
|
|
634
|
+
touched.push([el, el.style.visibility]);
|
|
635
|
+
el.style.visibility = "hidden";
|
|
636
|
+
});
|
|
716
637
|
}
|
|
717
|
-
return
|
|
718
|
-
}
|
|
719
|
-
function
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
const total = entries.reduce((sum, [, w]) => sum + w, 0);
|
|
726
|
-
const target = u * total;
|
|
727
|
-
let acc = 0;
|
|
728
|
-
for (const [key, w] of entries) {
|
|
729
|
-
acc += w;
|
|
730
|
-
if (target < acc) return key;
|
|
638
|
+
return () => touched.forEach(([el, v]) => el.style.visibility = v);
|
|
639
|
+
}
|
|
640
|
+
async function scrollThrough(el) {
|
|
641
|
+
const prev = el.scrollTop;
|
|
642
|
+
const step = el.clientHeight || window.innerHeight;
|
|
643
|
+
for (let y = 0; y < el.scrollHeight; y += step) {
|
|
644
|
+
el.scrollTop = y;
|
|
645
|
+
await delay(16);
|
|
731
646
|
}
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
return at === -1 ? { slot: key } : { slot: key.slice(0, at), variant: key.slice(at + 1) };
|
|
743
|
-
}
|
|
744
|
-
function isVariantKey(key) {
|
|
745
|
-
return parseSlotKey(key).variant !== void 0;
|
|
746
|
-
}
|
|
747
|
-
function weightsOf(exp) {
|
|
748
|
-
const out = {};
|
|
749
|
-
for (const [label, v] of Object.entries(exp.variants)) out[label] = v.weight;
|
|
750
|
-
return out;
|
|
647
|
+
el.scrollTop = prev;
|
|
648
|
+
await delay(16);
|
|
649
|
+
}
|
|
650
|
+
function blobToDataURL(blob) {
|
|
651
|
+
return new Promise((resolve, reject) => {
|
|
652
|
+
const fr = new FileReader();
|
|
653
|
+
fr.onload = () => resolve(String(fr.result));
|
|
654
|
+
fr.onerror = () => reject(fr.error);
|
|
655
|
+
fr.readAsDataURL(blob);
|
|
656
|
+
});
|
|
751
657
|
}
|
|
752
|
-
function
|
|
753
|
-
const
|
|
754
|
-
|
|
755
|
-
const
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
const arm = exp.variants[label];
|
|
763
|
-
if (!arm) continue;
|
|
764
|
-
assignments[exp.id] = label;
|
|
765
|
-
experimentOf[exp.slot] = exp.id;
|
|
766
|
-
if (arm.page !== exp.slot) render[exp.slot] = arm.page;
|
|
767
|
-
}
|
|
768
|
-
const slotOf = {};
|
|
769
|
-
const activeKeys = [];
|
|
770
|
-
for (const p of pages) {
|
|
771
|
-
const { slot, variant } = parseSlotKey(p.key);
|
|
772
|
-
if (variant !== void 0) slotOf[p.key] = slot;
|
|
773
|
-
else activeKeys.push(p.key);
|
|
774
|
-
}
|
|
775
|
-
return { assignments, activeKeys, render, slotOf, experimentOf };
|
|
776
|
-
}
|
|
777
|
-
function validateExperiments(experiments, pages) {
|
|
778
|
-
const errors = [];
|
|
779
|
-
const warnings = [];
|
|
780
|
-
const pageKeys = new Set(pages.map((p) => p.key));
|
|
781
|
-
const seenIds = /* @__PURE__ */ new Set();
|
|
782
|
-
const slotOwner = /* @__PURE__ */ new Map();
|
|
783
|
-
const renderedVariantPages = /* @__PURE__ */ new Set();
|
|
784
|
-
for (const exp of experiments) {
|
|
785
|
-
const err = (code, message) => errors.push({ experimentId: exp.id, code, message });
|
|
786
|
-
const warn = (code, message) => warnings.push({ experimentId: exp.id, code, message });
|
|
787
|
-
if (seenIds.has(exp.id)) err("duplicate_id", `Experiment id "${exp.id}" is used more than once.`);
|
|
788
|
-
seenIds.add(exp.id);
|
|
789
|
-
if (!pageKeys.has(exp.slot)) err("slot_missing", `Slot "${exp.slot}" is not a page in the funnel.`);
|
|
790
|
-
if (isVariantKey(exp.slot)) err("slot_is_variant", `Slot "${exp.slot}" is a variant page; a slot must be a real flow page.`);
|
|
791
|
-
if (slotOwner.has(exp.slot)) {
|
|
792
|
-
err("slot_conflict", `Slot "${exp.slot}" is already targeted by experiment "${slotOwner.get(exp.slot)}".`);
|
|
793
|
-
} else {
|
|
794
|
-
slotOwner.set(exp.slot, exp.id);
|
|
795
|
-
}
|
|
796
|
-
const arms = Object.entries(exp.variants);
|
|
797
|
-
if (arms.length < 2) warn("single_arm", `Experiment "${exp.id}" has fewer than two variants \u2014 nothing to test.`);
|
|
798
|
-
let hasControl = false;
|
|
799
|
-
let totalWeight = 0;
|
|
800
|
-
for (const [label, arm] of arms) {
|
|
801
|
-
if (!pageKeys.has(arm.page)) {
|
|
802
|
-
err("variant_page_missing", `Variant "${label}" references page "${arm.page}", which doesn't exist.`);
|
|
803
|
-
}
|
|
804
|
-
if (arm.page === exp.slot) hasControl = true;
|
|
805
|
-
else if (isVariantKey(arm.page)) {
|
|
806
|
-
renderedVariantPages.add(arm.page);
|
|
807
|
-
if (parseSlotKey(arm.page).slot !== exp.slot) {
|
|
808
|
-
err("variant_slot_mismatch", `Variant "${label}" page "${arm.page}" belongs to a different slot than "${exp.slot}".`);
|
|
809
|
-
}
|
|
810
|
-
}
|
|
811
|
-
if (arm.weight < 0) err("negative_weight", `Variant "${label}" has a negative weight (${arm.weight}).`);
|
|
812
|
-
totalWeight += arm.weight;
|
|
658
|
+
async function captureScreenshot(params = {}) {
|
|
659
|
+
const el = resolveTarget(params.target);
|
|
660
|
+
if (!el) throw new ScreenshotError("bad_params", "screenshot target not found");
|
|
661
|
+
const restore = params.hideSelectors?.length ? hideAll(params.hideSelectors) : null;
|
|
662
|
+
const scale = Math.min(params.scale ?? 2, params.maxScale ?? 3);
|
|
663
|
+
const format = params.format ?? "png";
|
|
664
|
+
const timeout = params.timeout ?? 1e4;
|
|
665
|
+
try {
|
|
666
|
+
if (params.waitForFonts !== false && document.fonts?.ready) {
|
|
667
|
+
await Promise.race([document.fonts.ready, delay(timeout)]);
|
|
813
668
|
}
|
|
814
|
-
if (
|
|
815
|
-
if (
|
|
816
|
-
|
|
817
|
-
|
|
669
|
+
if (params.triggerLazyLoad) await scrollThrough(el);
|
|
670
|
+
if (params.waitForImages !== false) {
|
|
671
|
+
await Promise.all(
|
|
672
|
+
Array.from(el.querySelectorAll("img")).map(
|
|
673
|
+
(img) => typeof img.decode === "function" ? img.decode().catch(() => {
|
|
674
|
+
}) : Promise.resolve()
|
|
675
|
+
)
|
|
676
|
+
);
|
|
818
677
|
}
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
678
|
+
const { domToBlob } = await import('modern-screenshot');
|
|
679
|
+
let blob;
|
|
680
|
+
try {
|
|
681
|
+
blob = await domToBlob(el, {
|
|
682
|
+
type: MIME[format],
|
|
683
|
+
quality: params.quality ?? 0.92,
|
|
684
|
+
scale,
|
|
685
|
+
width: params.width,
|
|
686
|
+
height: params.height,
|
|
687
|
+
// `null` = transparent (png/webp); only fall back to white when the caller omitted it. `??`
|
|
688
|
+
// would wrongly collapse an explicit `null` to white.
|
|
689
|
+
backgroundColor: params.backgroundColor === void 0 ? "#ffffff" : params.backgroundColor,
|
|
690
|
+
timeout
|
|
691
|
+
});
|
|
692
|
+
} catch (e) {
|
|
693
|
+
throw new ScreenshotError("failed", `capture failed (an image/font may be served without CORS '*'): ${String(e?.message || e)}`);
|
|
826
694
|
}
|
|
695
|
+
const result = {
|
|
696
|
+
mime: MIME[format],
|
|
697
|
+
width: Math.round((params.width ?? el.clientWidth) * scale),
|
|
698
|
+
height: Math.round((params.height ?? el.scrollHeight) * scale),
|
|
699
|
+
scale,
|
|
700
|
+
bytesApprox: blob.size
|
|
701
|
+
};
|
|
702
|
+
if ((params.encoding ?? "blob") === "dataURL") result.dataURL = await blobToDataURL(blob);
|
|
703
|
+
else result.blob = blob;
|
|
704
|
+
return result;
|
|
705
|
+
} finally {
|
|
706
|
+
restore?.();
|
|
827
707
|
}
|
|
828
|
-
return { ok: errors.length === 0, errors, warnings };
|
|
829
708
|
}
|
|
830
709
|
|
|
831
710
|
// src/flow/flow.tsx
|
|
832
|
-
var NavContext =
|
|
833
|
-
var
|
|
711
|
+
var NavContext = React.createContext(null);
|
|
712
|
+
var GroupProgressContext = React.createContext(null);
|
|
713
|
+
var ExperimentContext = React.createContext({});
|
|
834
714
|
function useExperiment(id) {
|
|
835
|
-
return
|
|
715
|
+
return React.useContext(ExperimentContext)[id];
|
|
836
716
|
}
|
|
837
|
-
function pageContextFor(currentKey, slug, index, total, startedAt) {
|
|
717
|
+
function pageContextFor(currentKey, slug, group, index, total, startedAt) {
|
|
838
718
|
const denom = Math.max(total, index + 1);
|
|
839
719
|
return {
|
|
840
720
|
key: currentKey,
|
|
841
721
|
slug,
|
|
722
|
+
group,
|
|
842
723
|
index,
|
|
843
724
|
total: denom,
|
|
844
725
|
progressPercentage: denom > 0 ? Math.min(100, Math.round((index + 1) / denom * 100)) : 0,
|
|
845
726
|
startedAt
|
|
846
727
|
};
|
|
847
728
|
}
|
|
729
|
+
var now = () => typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
730
|
+
function usePageExitTimer(tracker, currentKey) {
|
|
731
|
+
const timer = React.useRef(null);
|
|
732
|
+
const emitExit = React.useCallback(() => {
|
|
733
|
+
const t = timer.current;
|
|
734
|
+
if (!t) return;
|
|
735
|
+
const at = now();
|
|
736
|
+
const hidden = t.hidden + (t.hiddenSince != null ? at - t.hiddenSince : 0);
|
|
737
|
+
const durationMs = Math.round(at - t.enteredAt);
|
|
738
|
+
tracker.track("page.exit", { pageId: t.key, durationMs, activeMs: Math.max(0, Math.round(durationMs - hidden)) });
|
|
739
|
+
}, [tracker]);
|
|
740
|
+
React.useEffect(() => {
|
|
741
|
+
if (typeof document === "undefined") return;
|
|
742
|
+
const onVis = () => {
|
|
743
|
+
const t = timer.current;
|
|
744
|
+
if (!t) return;
|
|
745
|
+
if (document.visibilityState === "hidden") t.hiddenSince = now();
|
|
746
|
+
else if (t.hiddenSince != null) {
|
|
747
|
+
t.hidden += now() - t.hiddenSince;
|
|
748
|
+
t.hiddenSince = null;
|
|
749
|
+
}
|
|
750
|
+
};
|
|
751
|
+
document.addEventListener("visibilitychange", onVis);
|
|
752
|
+
return () => document.removeEventListener("visibilitychange", onVis);
|
|
753
|
+
}, []);
|
|
754
|
+
React.useEffect(() => {
|
|
755
|
+
if (!currentKey) return;
|
|
756
|
+
if (timer.current && timer.current.key !== currentKey) emitExit();
|
|
757
|
+
const hidden = typeof document !== "undefined" && document.visibilityState === "hidden";
|
|
758
|
+
timer.current = { key: currentKey, enteredAt: now(), hidden: 0, hiddenSince: hidden ? now() : null };
|
|
759
|
+
}, [currentKey, emitExit]);
|
|
760
|
+
React.useEffect(() => () => emitExit(), [emitExit]);
|
|
761
|
+
}
|
|
848
762
|
function FunnelView({
|
|
849
763
|
pages,
|
|
850
764
|
initialKey,
|
|
765
|
+
trustInitialKey = false,
|
|
851
766
|
layout,
|
|
852
767
|
fallback,
|
|
853
768
|
prefetch: prefetchMode = "auto",
|
|
@@ -855,141 +770,129 @@ function FunnelView({
|
|
|
855
770
|
}) {
|
|
856
771
|
const funnel = useFunnel();
|
|
857
772
|
const tracker = useTrackerRef();
|
|
858
|
-
const components =
|
|
773
|
+
const components = React.useMemo(() => {
|
|
859
774
|
const map = /* @__PURE__ */ new Map();
|
|
860
775
|
for (const p of pages) {
|
|
861
776
|
if (p.Component) map.set(p.key, p.Component);
|
|
862
777
|
else if (p.load) {
|
|
863
778
|
const load = p.load;
|
|
864
|
-
map.set(p.key,
|
|
779
|
+
map.set(p.key, React.lazy(() => Promise.resolve(load()).then(
|
|
865
780
|
(m) => typeof m === "function" ? { default: m } : m
|
|
866
781
|
)));
|
|
867
782
|
}
|
|
868
783
|
}
|
|
869
784
|
return map;
|
|
870
785
|
}, [pages]);
|
|
871
|
-
const loaders =
|
|
786
|
+
const loaders = React.useMemo(
|
|
872
787
|
() => new Map(pages.filter((p) => p.load).map((p) => [p.key, p.load])),
|
|
873
788
|
[pages]
|
|
874
789
|
);
|
|
875
|
-
const
|
|
876
|
-
|
|
877
|
-
|
|
790
|
+
const seedRef = React.useRef(null);
|
|
791
|
+
if (seedRef.current === null) seedRef.current = chunkRKPRMTSU_cjs.bucketingSeed(funnel.context);
|
|
792
|
+
const seed = seedRef.current;
|
|
793
|
+
const experiments = React.useMemo(
|
|
794
|
+
() => chunkRKPRMTSU_cjs.resolveExperiments(pages, funnel.experiments, seed),
|
|
878
795
|
[pages, funnel.experiments, seed]
|
|
879
796
|
);
|
|
880
|
-
const
|
|
881
|
-
const
|
|
882
|
-
|
|
797
|
+
const toSlot = React.useCallback((k) => experiments.slotOf[k] ?? k, [experiments]);
|
|
798
|
+
const toRenderKey = React.useCallback((k) => experiments.render[k] ?? k, [experiments]);
|
|
799
|
+
const pageByKey = React.useMemo(() => new Map(pages.map((p) => [p.key, p])), [pages]);
|
|
800
|
+
const flowPages = React.useMemo(
|
|
801
|
+
() => experiments.activeKeys.map((k) => pageByKey.get(k)).filter((p) => p !== void 0),
|
|
883
802
|
[experiments, pageByKey]
|
|
884
803
|
);
|
|
885
|
-
const flow =
|
|
886
|
-
() => flowPages.map((p) =>
|
|
887
|
-
|
|
804
|
+
const flow = React.useMemo(
|
|
805
|
+
() => flowPages.map((p) => {
|
|
806
|
+
const servedVariant = experiments.render[p.key];
|
|
807
|
+
const variantNext = servedVariant ? pageByKey.get(servedVariant)?.meta?.next : void 0;
|
|
808
|
+
return variantNext ? { key: p.key, meta: { ...p.meta ?? {}, next: variantNext } } : { key: p.key, meta: p.meta };
|
|
809
|
+
}),
|
|
810
|
+
[flowPages, experiments, pageByKey]
|
|
888
811
|
);
|
|
889
|
-
const [history, setHistory] =
|
|
812
|
+
const [history, setHistory] = React.useState(() => {
|
|
890
813
|
const start = flowPages[0]?.key;
|
|
891
|
-
if (!initialKey || initialKey === start) return [start]
|
|
892
|
-
const target =
|
|
814
|
+
if (!initialKey || initialKey === start) return start ? [start] : [];
|
|
815
|
+
const target = toSlot(initialKey);
|
|
893
816
|
const page2 = flowPages.find((p) => p.key === target);
|
|
894
|
-
|
|
895
|
-
const
|
|
896
|
-
|
|
817
|
+
if (trustInitialKey) return page2 ? [target] : start ? [start] : [];
|
|
818
|
+
const guard = chunkRKPRMTSU_cjs.entryGuard(page2?.meta);
|
|
819
|
+
const ok = page2 && (!guard || chunkRKPRMTSU_cjs.evaluateGate(guard, funnel.snapshot()));
|
|
820
|
+
const first = ok ? target : start;
|
|
821
|
+
return first ? [first] : [];
|
|
897
822
|
});
|
|
898
823
|
const currentKey = history[history.length - 1];
|
|
899
824
|
const index = history.length - 1;
|
|
900
825
|
const startKey = history[0];
|
|
901
|
-
const total =
|
|
902
|
-
() => expectedPathLength(flow, startKey, funnel.snapshot()),
|
|
826
|
+
const total = React.useMemo(
|
|
827
|
+
() => chunkRKPRMTSU_cjs.expectedPathLength(flow, startKey, funnel.snapshot()),
|
|
903
828
|
// currentKey in deps: re-trace as each step's answers settle the branch.
|
|
904
829
|
[flow, startKey, currentKey, funnel]
|
|
905
830
|
);
|
|
906
831
|
const slug = pageByKey.get(currentKey)?.meta?.slug ?? currentKey;
|
|
907
|
-
const
|
|
908
|
-
|
|
909
|
-
|
|
832
|
+
const group = pageByKey.get(currentKey)?.meta?.group;
|
|
833
|
+
const page = React.useMemo(
|
|
834
|
+
() => pageContextFor(currentKey, slug, group, index, total, funnel.context.system.now),
|
|
835
|
+
[currentKey, slug, group, index, total, funnel.context.system.now]
|
|
836
|
+
);
|
|
837
|
+
const groups = React.useMemo(
|
|
838
|
+
() => chunkRKPRMTSU_cjs.groupProgress(flow, startKey, currentKey, funnel.snapshot()),
|
|
839
|
+
// currentKey in deps: re-trace as each step's answers settle the branch.
|
|
840
|
+
[flow, startKey, currentKey, funnel]
|
|
910
841
|
);
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
842
|
+
React.useEffect(() => {
|
|
843
|
+
funnel.context.page = page;
|
|
844
|
+
}, [funnel, page]);
|
|
845
|
+
const started = React.useRef(false);
|
|
846
|
+
React.useEffect(() => {
|
|
914
847
|
if (started.current) return;
|
|
915
848
|
started.current = true;
|
|
916
849
|
tracker.track("funnel.start", {});
|
|
917
850
|
}, [tracker]);
|
|
918
|
-
|
|
851
|
+
React.useEffect(() => {
|
|
919
852
|
if (!currentKey) return;
|
|
920
853
|
tracker.track("page.view", { pageId: currentKey, pageKey: currentKey, isInitial: index === 0, eventId: newEventId() });
|
|
921
854
|
}, [tracker, currentKey, index]);
|
|
922
|
-
|
|
855
|
+
React.useEffect(() => {
|
|
923
856
|
if (currentKey && onNavigate) onNavigate(currentKey, slug);
|
|
924
857
|
}, [onNavigate, currentKey, slug]);
|
|
925
|
-
const exposed =
|
|
926
|
-
|
|
858
|
+
const exposed = React.useRef(/* @__PURE__ */ new Set());
|
|
859
|
+
React.useEffect(() => {
|
|
927
860
|
const expId = experiments.experimentOf[currentKey];
|
|
928
861
|
if (!expId || exposed.current.has(expId)) return;
|
|
929
862
|
exposed.current.add(expId);
|
|
930
|
-
tracker.track("experiment.exposure",
|
|
863
|
+
tracker.track("experiment.exposure", exposurePayload(expId, experiments.assignments[expId], experiments.versionOf[expId]));
|
|
931
864
|
}, [tracker, currentKey, experiments]);
|
|
932
|
-
|
|
933
|
-
const
|
|
934
|
-
const
|
|
935
|
-
const
|
|
936
|
-
|
|
937
|
-
const at = now();
|
|
938
|
-
const hidden = t.hidden + (t.hiddenSince != null ? at - t.hiddenSince : 0);
|
|
939
|
-
const durationMs = Math.round(at - t.enteredAt);
|
|
940
|
-
tracker.track("page.exit", { pageId: t.key, durationMs, activeMs: Math.max(0, Math.round(durationMs - hidden)) });
|
|
941
|
-
}, [tracker]);
|
|
942
|
-
react.useEffect(() => {
|
|
943
|
-
if (typeof document === "undefined") return;
|
|
944
|
-
const onVis = () => {
|
|
945
|
-
const t = timer.current;
|
|
946
|
-
if (!t) return;
|
|
947
|
-
if (document.visibilityState === "hidden") t.hiddenSince = now();
|
|
948
|
-
else if (t.hiddenSince != null) {
|
|
949
|
-
t.hidden += now() - t.hiddenSince;
|
|
950
|
-
t.hiddenSince = null;
|
|
951
|
-
}
|
|
952
|
-
};
|
|
953
|
-
document.addEventListener("visibilitychange", onVis);
|
|
954
|
-
return () => document.removeEventListener("visibilitychange", onVis);
|
|
955
|
-
}, []);
|
|
956
|
-
react.useEffect(() => {
|
|
957
|
-
if (!currentKey) return;
|
|
958
|
-
if (timer.current && timer.current.key !== currentKey) emitExit();
|
|
959
|
-
const hidden = typeof document !== "undefined" && document.visibilityState === "hidden";
|
|
960
|
-
timer.current = { key: currentKey, enteredAt: now(), hidden: 0, hiddenSince: hidden ? now() : null };
|
|
961
|
-
}, [currentKey, emitExit]);
|
|
962
|
-
react.useEffect(() => () => emitExit(), [emitExit]);
|
|
963
|
-
const [isNavigating, startNav] = react.useTransition();
|
|
964
|
-
const next = react.useCallback(() => {
|
|
965
|
-
const target = nextPage(flow, currentKey, funnel.snapshot());
|
|
966
|
-
const resolved = target ? experiments.slotOf[target] ?? target : target;
|
|
865
|
+
usePageExitTimer(tracker, currentKey);
|
|
866
|
+
const [isNavigating, startNav] = React.useTransition();
|
|
867
|
+
const next = React.useCallback(() => {
|
|
868
|
+
const target = chunkRKPRMTSU_cjs.nextPage(flow, currentKey, funnel.snapshot());
|
|
869
|
+
const resolved = target ? toSlot(target) : target;
|
|
967
870
|
if (resolved) startNav(() => setHistory((h) => [...h, resolved]));
|
|
968
|
-
}, [flow, currentKey, funnel,
|
|
969
|
-
const back =
|
|
871
|
+
}, [flow, currentKey, funnel, toSlot]);
|
|
872
|
+
const back = React.useCallback(
|
|
970
873
|
() => startNav(() => setHistory((h) => h.length > 1 ? h.slice(0, -1) : h)),
|
|
971
874
|
[]
|
|
972
875
|
);
|
|
973
|
-
const go =
|
|
974
|
-
(key) => startNav(() => setHistory((h) => [...h,
|
|
975
|
-
[
|
|
876
|
+
const go = React.useCallback(
|
|
877
|
+
(key) => startNav(() => setHistory((h) => [...h, toSlot(key)])),
|
|
878
|
+
[toSlot]
|
|
976
879
|
);
|
|
977
|
-
const prefetched =
|
|
978
|
-
const prefetch =
|
|
880
|
+
const prefetched = React.useRef(/* @__PURE__ */ new Set());
|
|
881
|
+
const prefetch = React.useCallback(
|
|
979
882
|
(key) => {
|
|
980
|
-
const target =
|
|
883
|
+
const target = toRenderKey(key);
|
|
981
884
|
const load = loaders.get(target);
|
|
982
885
|
if (!load || prefetched.current.has(target)) return;
|
|
983
886
|
prefetched.current.add(target);
|
|
984
887
|
load().catch(() => prefetched.current.delete(target));
|
|
985
888
|
},
|
|
986
|
-
[loaders,
|
|
889
|
+
[loaders, toRenderKey]
|
|
987
890
|
);
|
|
988
|
-
const nextCandidates =
|
|
989
|
-
() => [...new Set(outgoingKeys(flow, currentKey).map(
|
|
990
|
-
[flow, currentKey,
|
|
891
|
+
const nextCandidates = React.useMemo(
|
|
892
|
+
() => [...new Set(chunkRKPRMTSU_cjs.outgoingKeys(flow, currentKey).map(toRenderKey))],
|
|
893
|
+
[flow, currentKey, toRenderKey]
|
|
991
894
|
);
|
|
992
|
-
|
|
895
|
+
React.useEffect(() => {
|
|
993
896
|
if (prefetchMode !== "auto") return;
|
|
994
897
|
for (const key of nextCandidates) prefetch(key);
|
|
995
898
|
}, [prefetchMode, nextCandidates, prefetch]);
|
|
@@ -1003,36 +906,166 @@ function FunnelView({
|
|
|
1003
906
|
canGoBack: history.length > 1,
|
|
1004
907
|
isNavigating
|
|
1005
908
|
};
|
|
1006
|
-
const
|
|
909
|
+
const bus = useBusInternal();
|
|
910
|
+
const navRef = React.useRef(value);
|
|
911
|
+
navRef.current = value;
|
|
912
|
+
React.useEffect(() => {
|
|
913
|
+
if (!bus) return;
|
|
914
|
+
const control = {
|
|
915
|
+
goTo: (key) => {
|
|
916
|
+
if (!components.has(toRenderKey(key))) return false;
|
|
917
|
+
navRef.current.go(key);
|
|
918
|
+
return true;
|
|
919
|
+
},
|
|
920
|
+
goNext: () => navRef.current.next(),
|
|
921
|
+
goPrevious: () => navRef.current.back(),
|
|
922
|
+
snapshot: () => {
|
|
923
|
+
const v = navRef.current;
|
|
924
|
+
return {
|
|
925
|
+
key: v.page.key,
|
|
926
|
+
index: v.page.index,
|
|
927
|
+
total: v.page.total,
|
|
928
|
+
progressPercentage: v.page.progressPercentage,
|
|
929
|
+
canGoBack: v.canGoBack,
|
|
930
|
+
nextCandidates: v.nextCandidates
|
|
931
|
+
};
|
|
932
|
+
}
|
|
933
|
+
};
|
|
934
|
+
return bus.registerNavigation(control);
|
|
935
|
+
}, [bus]);
|
|
936
|
+
React.useEffect(() => {
|
|
937
|
+
const v = navRef.current;
|
|
938
|
+
bus?.emit("navigation", {
|
|
939
|
+
key: v.page.key,
|
|
940
|
+
index: v.page.index,
|
|
941
|
+
total: v.page.total,
|
|
942
|
+
progressPercentage: v.page.progressPercentage,
|
|
943
|
+
canGoBack: v.canGoBack,
|
|
944
|
+
nextCandidates: v.nextCandidates
|
|
945
|
+
});
|
|
946
|
+
}, [bus, currentKey]);
|
|
947
|
+
const renderKey = toRenderKey(currentKey);
|
|
1007
948
|
const Current = components.get(renderKey);
|
|
1008
|
-
const pageEl =
|
|
1009
|
-
const content = layout ?
|
|
1010
|
-
return
|
|
949
|
+
const pageEl = React.createElement(React.Suspense, { fallback: fallback ?? null }, Current ? React.createElement(Current) : null);
|
|
950
|
+
const content = layout ? React.createElement(layout, null, pageEl) : pageEl;
|
|
951
|
+
return React.createElement(
|
|
1011
952
|
ExperimentContext.Provider,
|
|
1012
953
|
{ value: experiments.assignments },
|
|
1013
|
-
|
|
954
|
+
React.createElement(
|
|
955
|
+
NavContext.Provider,
|
|
956
|
+
{ value },
|
|
957
|
+
React.createElement(GroupProgressContext.Provider, { value: groups }, content)
|
|
958
|
+
)
|
|
1014
959
|
);
|
|
1015
960
|
}
|
|
1016
961
|
function useNavigation() {
|
|
1017
|
-
const ctx =
|
|
962
|
+
const ctx = React.useContext(NavContext);
|
|
1018
963
|
if (!ctx) throw new Error("useNavigation must be used inside <FunnelView>");
|
|
1019
964
|
return ctx;
|
|
1020
965
|
}
|
|
966
|
+
var EMBED_CAPABILITIES = {
|
|
967
|
+
methods: ["nav.snapshot", "nav.goTo", "nav.goNext", "nav.goPrevious", "screenshot"],
|
|
968
|
+
events: ["navigation"]
|
|
969
|
+
};
|
|
970
|
+
function EmbedBridge() {
|
|
971
|
+
const bus = useBusInternal();
|
|
972
|
+
React.useEffect(() => {
|
|
973
|
+
if (!bus || typeof window === "undefined" || window.parent === window) return;
|
|
974
|
+
const parent = window.parent;
|
|
975
|
+
let disposed = false;
|
|
976
|
+
const send = (msg) => {
|
|
977
|
+
try {
|
|
978
|
+
parent.postMessage({ af: chunkWYUDL4FI_cjs.AF_PROTOCOL, source: "af-preview", ...msg }, "*");
|
|
979
|
+
} catch {
|
|
980
|
+
}
|
|
981
|
+
};
|
|
982
|
+
const hello = () => send({ kind: "hello", role: "funnel", protocol: chunkWYUDL4FI_cjs.AF_PROTOCOL, capabilities: EMBED_CAPABILITIES });
|
|
983
|
+
const sendSnapshot = () => {
|
|
984
|
+
const s = bus.navigation();
|
|
985
|
+
if (s) send({ kind: "event", event: "navigation", data: s });
|
|
986
|
+
};
|
|
987
|
+
async function invoke(method, params) {
|
|
988
|
+
switch (method) {
|
|
989
|
+
case "nav.snapshot":
|
|
990
|
+
return bus.navigation();
|
|
991
|
+
case "nav.goTo": {
|
|
992
|
+
const key = params?.key;
|
|
993
|
+
if (typeof key !== "string" || !bus.goTo(key)) throw { code: "bad_params", message: `unknown page key: ${String(key)}` };
|
|
994
|
+
return void 0;
|
|
995
|
+
}
|
|
996
|
+
case "nav.goNext":
|
|
997
|
+
bus.goNext();
|
|
998
|
+
return void 0;
|
|
999
|
+
case "nav.goPrevious":
|
|
1000
|
+
bus.goPrevious();
|
|
1001
|
+
return void 0;
|
|
1002
|
+
case "screenshot":
|
|
1003
|
+
return await captureScreenshot(params ?? {});
|
|
1004
|
+
default:
|
|
1005
|
+
throw { code: "unknown_method", message: `unknown method: ${method}` };
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
async function onMsg(e) {
|
|
1009
|
+
if (e.source !== parent) return;
|
|
1010
|
+
const d = e.data;
|
|
1011
|
+
if (!d || typeof d !== "object" || d.af !== chunkWYUDL4FI_cjs.AF_PROTOCOL || d.source !== "af-host") return;
|
|
1012
|
+
if (d.kind === "hello") {
|
|
1013
|
+
hello();
|
|
1014
|
+
sendSnapshot();
|
|
1015
|
+
return;
|
|
1016
|
+
}
|
|
1017
|
+
if (d.kind !== "request" || typeof d.id !== "string") return;
|
|
1018
|
+
try {
|
|
1019
|
+
const result = await invoke(d.method, d.params);
|
|
1020
|
+
if (!disposed) send({ kind: "response", id: d.id, ok: true, result });
|
|
1021
|
+
} catch (err) {
|
|
1022
|
+
const e2 = err;
|
|
1023
|
+
const error = e2 && typeof e2.code === "string" ? { code: e2.code, message: String(e2.message ?? e2.code) } : { code: "failed", message: String(e2?.message ?? err) };
|
|
1024
|
+
if (!disposed) send({ kind: "response", id: d.id, ok: false, error });
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
window.addEventListener("message", onMsg);
|
|
1028
|
+
const offNav = bus.on("navigation", (data) => {
|
|
1029
|
+
if (!disposed) send({ kind: "event", event: "navigation", data });
|
|
1030
|
+
});
|
|
1031
|
+
hello();
|
|
1032
|
+
sendSnapshot();
|
|
1033
|
+
return () => {
|
|
1034
|
+
disposed = true;
|
|
1035
|
+
offNav();
|
|
1036
|
+
window.removeEventListener("message", onMsg);
|
|
1037
|
+
};
|
|
1038
|
+
}, [bus]);
|
|
1039
|
+
return null;
|
|
1040
|
+
}
|
|
1021
1041
|
function usePage() {
|
|
1022
1042
|
return useNavigation().page;
|
|
1023
1043
|
}
|
|
1024
|
-
|
|
1044
|
+
function useGroupProgress() {
|
|
1045
|
+
const ctx = React.useContext(GroupProgressContext);
|
|
1046
|
+
if (!ctx) throw new Error("useGroupProgress must be used inside <FunnelView>");
|
|
1047
|
+
return ctx;
|
|
1048
|
+
}
|
|
1049
|
+
var symModalId = /* @__PURE__ */ Symbol("AppfunnelModalId");
|
|
1025
1050
|
var initialState = {};
|
|
1026
|
-
var ModalStateContext =
|
|
1027
|
-
var ModalIdContext =
|
|
1051
|
+
var ModalStateContext = React.createContext(initialState);
|
|
1052
|
+
var ModalIdContext = React.createContext(null);
|
|
1053
|
+
var ModalDispatchContext = React.createContext(null);
|
|
1028
1054
|
var REGISTRY = {};
|
|
1029
1055
|
var ALREADY_MOUNTED = {};
|
|
1030
1056
|
var modalCallbacks = {};
|
|
1031
1057
|
var hideCallbacks = {};
|
|
1032
1058
|
var uid = 0;
|
|
1033
1059
|
var getUid = () => `_af_modal_${uid++}`;
|
|
1034
|
-
var
|
|
1035
|
-
|
|
1060
|
+
var runtimeStack = [];
|
|
1061
|
+
var dispatch = (action) => {
|
|
1062
|
+
const top = runtimeStack[runtimeStack.length - 1];
|
|
1063
|
+
if (!top) {
|
|
1064
|
+
throw new Error(
|
|
1065
|
+
"No modal dispatch \u2014 render inside <FunnelProvider> (which mounts the modal runtime)."
|
|
1066
|
+
);
|
|
1067
|
+
}
|
|
1068
|
+
top(action);
|
|
1036
1069
|
};
|
|
1037
1070
|
var modalReducer = (state = initialState, action) => {
|
|
1038
1071
|
const { modalId, args, flags } = action.payload;
|
|
@@ -1075,10 +1108,10 @@ function registerModal(id, comp, props) {
|
|
|
1075
1108
|
function unregisterModal(id) {
|
|
1076
1109
|
delete REGISTRY[id];
|
|
1077
1110
|
}
|
|
1078
|
-
function
|
|
1111
|
+
function showModalVia(d, modal, args) {
|
|
1079
1112
|
const id = idOf(modal);
|
|
1080
1113
|
if (typeof modal !== "string" && !REGISTRY[id]) registerModal(id, modal);
|
|
1081
|
-
|
|
1114
|
+
d({ type: "show", payload: { modalId: id, args } });
|
|
1082
1115
|
if (!modalCallbacks[id]) {
|
|
1083
1116
|
let res;
|
|
1084
1117
|
let rej;
|
|
@@ -1090,9 +1123,12 @@ function showModal(modal, args) {
|
|
|
1090
1123
|
}
|
|
1091
1124
|
return modalCallbacks[id].promise;
|
|
1092
1125
|
}
|
|
1093
|
-
function
|
|
1126
|
+
function showModal(modal, args) {
|
|
1127
|
+
return showModalVia(dispatch, modal, args);
|
|
1128
|
+
}
|
|
1129
|
+
function hideModalVia(d, modal) {
|
|
1094
1130
|
const id = idOf(modal);
|
|
1095
|
-
|
|
1131
|
+
d({ type: "hide", payload: { modalId: id } });
|
|
1096
1132
|
delete modalCallbacks[id];
|
|
1097
1133
|
if (!hideCallbacks[id]) {
|
|
1098
1134
|
let res;
|
|
@@ -1105,41 +1141,64 @@ function hideModal(modal) {
|
|
|
1105
1141
|
}
|
|
1106
1142
|
return hideCallbacks[id].promise;
|
|
1107
1143
|
}
|
|
1108
|
-
function
|
|
1144
|
+
function hideModal(modal) {
|
|
1145
|
+
return hideModalVia(dispatch, modal);
|
|
1146
|
+
}
|
|
1147
|
+
function removeModalVia(d, modal) {
|
|
1109
1148
|
const id = idOf(modal);
|
|
1110
|
-
|
|
1149
|
+
d({ type: "remove", payload: { modalId: id } });
|
|
1111
1150
|
delete modalCallbacks[id];
|
|
1112
1151
|
delete hideCallbacks[id];
|
|
1113
1152
|
}
|
|
1114
|
-
function
|
|
1115
|
-
dispatch
|
|
1153
|
+
function removeModal(modal) {
|
|
1154
|
+
removeModalVia(dispatch, modal);
|
|
1155
|
+
}
|
|
1156
|
+
function setFlags(d, id, flags) {
|
|
1157
|
+
d({ type: "set-flags", payload: { modalId: id, flags } });
|
|
1116
1158
|
}
|
|
1117
1159
|
function useModal(modal, args) {
|
|
1118
|
-
const modals =
|
|
1119
|
-
const contextId =
|
|
1160
|
+
const modals = React.useContext(ModalStateContext);
|
|
1161
|
+
const contextId = React.useContext(ModalIdContext);
|
|
1162
|
+
const d = React.useContext(ModalDispatchContext) ?? dispatch;
|
|
1120
1163
|
const isComponent = !!modal && typeof modal !== "string";
|
|
1121
1164
|
const id = modal ? idOf(modal) : contextId;
|
|
1122
|
-
if (!id)
|
|
1123
|
-
|
|
1124
|
-
|
|
1165
|
+
if (!id)
|
|
1166
|
+
throw new Error(
|
|
1167
|
+
"useModal: no modal id (call inside a defineModal component, or pass an id/component)."
|
|
1168
|
+
);
|
|
1169
|
+
React.useEffect(() => {
|
|
1170
|
+
if (isComponent && !REGISTRY[id])
|
|
1171
|
+
registerModal(id, modal, args);
|
|
1125
1172
|
}, [isComponent, id, modal, args]);
|
|
1126
1173
|
const info = modals[id];
|
|
1127
|
-
const show =
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1174
|
+
const show = React.useCallback(
|
|
1175
|
+
(a) => showModalVia(d, id, a),
|
|
1176
|
+
[d, id]
|
|
1177
|
+
);
|
|
1178
|
+
const hide = React.useCallback(() => hideModalVia(d, id), [d, id]);
|
|
1179
|
+
const remove = React.useCallback(() => removeModalVia(d, id), [d, id]);
|
|
1180
|
+
const resolve = React.useCallback(
|
|
1181
|
+
(v) => {
|
|
1182
|
+
modalCallbacks[id]?.resolve(v);
|
|
1183
|
+
delete modalCallbacks[id];
|
|
1184
|
+
},
|
|
1185
|
+
[id]
|
|
1186
|
+
);
|
|
1187
|
+
const reject = React.useCallback(
|
|
1188
|
+
(v) => {
|
|
1189
|
+
modalCallbacks[id]?.reject(v);
|
|
1190
|
+
delete modalCallbacks[id];
|
|
1191
|
+
},
|
|
1192
|
+
[id]
|
|
1193
|
+
);
|
|
1194
|
+
const resolveHide = React.useCallback(
|
|
1195
|
+
(v) => {
|
|
1196
|
+
hideCallbacks[id]?.resolve(v);
|
|
1197
|
+
delete hideCallbacks[id];
|
|
1198
|
+
},
|
|
1199
|
+
[id]
|
|
1200
|
+
);
|
|
1201
|
+
return React.useMemo(
|
|
1143
1202
|
() => ({
|
|
1144
1203
|
id,
|
|
1145
1204
|
args: info?.args,
|
|
@@ -1152,53 +1211,90 @@ function useModal(modal, args) {
|
|
|
1152
1211
|
reject,
|
|
1153
1212
|
resolveHide
|
|
1154
1213
|
}),
|
|
1155
|
-
[
|
|
1214
|
+
[
|
|
1215
|
+
id,
|
|
1216
|
+
info?.args,
|
|
1217
|
+
info?.visible,
|
|
1218
|
+
info?.keepMounted,
|
|
1219
|
+
show,
|
|
1220
|
+
hide,
|
|
1221
|
+
remove,
|
|
1222
|
+
resolve,
|
|
1223
|
+
reject,
|
|
1224
|
+
resolveHide
|
|
1225
|
+
]
|
|
1156
1226
|
);
|
|
1157
1227
|
}
|
|
1158
1228
|
function defineModal(Comp) {
|
|
1159
|
-
return function Wrapped({
|
|
1229
|
+
return function Wrapped({
|
|
1230
|
+
id,
|
|
1231
|
+
defaultVisible,
|
|
1232
|
+
keepMounted,
|
|
1233
|
+
...props
|
|
1234
|
+
}) {
|
|
1160
1235
|
const { args, show } = useModal(id);
|
|
1161
|
-
const modals =
|
|
1236
|
+
const modals = React.useContext(ModalStateContext);
|
|
1237
|
+
const d = React.useContext(ModalDispatchContext) ?? dispatch;
|
|
1162
1238
|
const shouldMount = !!modals[id];
|
|
1163
|
-
|
|
1239
|
+
React.useEffect(() => {
|
|
1164
1240
|
if (defaultVisible) show();
|
|
1165
1241
|
ALREADY_MOUNTED[id] = true;
|
|
1166
1242
|
return () => {
|
|
1167
1243
|
delete ALREADY_MOUNTED[id];
|
|
1168
1244
|
};
|
|
1169
1245
|
}, [id, show, defaultVisible]);
|
|
1170
|
-
|
|
1171
|
-
if (keepMounted) setFlags(id, { keepMounted: true });
|
|
1172
|
-
}, [id, keepMounted]);
|
|
1246
|
+
React.useEffect(() => {
|
|
1247
|
+
if (keepMounted) setFlags(d, id, { keepMounted: true });
|
|
1248
|
+
}, [d, id, keepMounted]);
|
|
1173
1249
|
const delayVisible = modals[id]?.delayVisible;
|
|
1174
|
-
|
|
1250
|
+
React.useEffect(() => {
|
|
1175
1251
|
if (delayVisible) show(args);
|
|
1176
1252
|
}, [delayVisible, args, show]);
|
|
1177
1253
|
if (!shouldMount) return null;
|
|
1178
|
-
return
|
|
1254
|
+
return React.createElement(
|
|
1179
1255
|
ModalIdContext.Provider,
|
|
1180
1256
|
{ value: id },
|
|
1181
|
-
|
|
1257
|
+
React.createElement(Comp, { ...props, ...args })
|
|
1182
1258
|
);
|
|
1183
1259
|
};
|
|
1184
1260
|
}
|
|
1185
1261
|
function ModalRoot() {
|
|
1186
|
-
const modals =
|
|
1262
|
+
const modals = React.useContext(ModalStateContext);
|
|
1187
1263
|
const toRender = Object.keys(modals).filter((id) => REGISTRY[id]).map((id) => ({ id, ...REGISTRY[id] }));
|
|
1188
|
-
return
|
|
1264
|
+
return React.createElement(
|
|
1189
1265
|
"div",
|
|
1190
1266
|
{ "data-appfunnel-modal-root": "" },
|
|
1191
|
-
...toRender.map(
|
|
1267
|
+
...toRender.map(
|
|
1268
|
+
(t) => React.createElement(t.comp, { key: t.id, id: t.id, ...t.props })
|
|
1269
|
+
)
|
|
1192
1270
|
);
|
|
1193
1271
|
}
|
|
1194
1272
|
function ModalRuntime({ children }) {
|
|
1195
|
-
const [state, localDispatch] =
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1273
|
+
const [state, localDispatch] = React.useReducer(modalReducer, initialState);
|
|
1274
|
+
React.useEffect(() => {
|
|
1275
|
+
runtimeStack.push(localDispatch);
|
|
1276
|
+
if (runtimeStack.length > 1) {
|
|
1277
|
+
try {
|
|
1278
|
+
console.warn(
|
|
1279
|
+
`[appfunnel] ${runtimeStack.length} modal runtimes are mounted \u2014 the imperative showModal()/hideModal() API binds to the most recent one. Use the useModal() hook inside each tree instead.`
|
|
1280
|
+
);
|
|
1281
|
+
} catch {
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
return () => {
|
|
1285
|
+
const i = runtimeStack.indexOf(localDispatch);
|
|
1286
|
+
if (i !== -1) runtimeStack.splice(i, 1);
|
|
1287
|
+
};
|
|
1288
|
+
}, []);
|
|
1289
|
+
return React.createElement(
|
|
1290
|
+
ModalDispatchContext.Provider,
|
|
1291
|
+
{ value: localDispatch },
|
|
1292
|
+
React.createElement(
|
|
1293
|
+
ModalStateContext.Provider,
|
|
1294
|
+
{ value: state },
|
|
1295
|
+
children,
|
|
1296
|
+
React.createElement(ModalRoot, null)
|
|
1297
|
+
)
|
|
1202
1298
|
);
|
|
1203
1299
|
}
|
|
1204
1300
|
|
|
@@ -1225,7 +1321,7 @@ function OverlayShell({
|
|
|
1225
1321
|
panel,
|
|
1226
1322
|
children
|
|
1227
1323
|
}) {
|
|
1228
|
-
|
|
1324
|
+
React.useEffect(() => {
|
|
1229
1325
|
if (!open || typeof document === "undefined") return;
|
|
1230
1326
|
const onKey = (e) => {
|
|
1231
1327
|
if (e.key === "Escape") onClose();
|
|
@@ -1239,7 +1335,7 @@ function OverlayShell({
|
|
|
1239
1335
|
};
|
|
1240
1336
|
}, [open, onClose]);
|
|
1241
1337
|
if (!open || typeof document === "undefined") return null;
|
|
1242
|
-
const overlay =
|
|
1338
|
+
const overlay = React.createElement(
|
|
1243
1339
|
"div",
|
|
1244
1340
|
{
|
|
1245
1341
|
role: "dialog",
|
|
@@ -1248,19 +1344,19 @@ function OverlayShell({
|
|
|
1248
1344
|
"data-appfunnel-overlay": "",
|
|
1249
1345
|
style: { position: "fixed", inset: 0, zIndex: 1e3, display: "flex", ...container }
|
|
1250
1346
|
},
|
|
1251
|
-
|
|
1347
|
+
React.createElement("div", {
|
|
1252
1348
|
"data-appfunnel-overlay-backdrop": "",
|
|
1253
1349
|
className: backdropClassName,
|
|
1254
1350
|
onClick: dismissOnBackdrop ? onClose : void 0,
|
|
1255
1351
|
style: { position: "absolute", inset: 0, background: "rgba(0,0,0,0.5)" }
|
|
1256
1352
|
}),
|
|
1257
|
-
|
|
1353
|
+
React.createElement(
|
|
1258
1354
|
"div",
|
|
1259
1355
|
{ "data-appfunnel-overlay-panel": "", className, style: { position: "relative", background: "#fff", ...panel } },
|
|
1260
1356
|
children
|
|
1261
1357
|
)
|
|
1262
1358
|
);
|
|
1263
|
-
return
|
|
1359
|
+
return ReactDOM.createPortal(overlay, document.body);
|
|
1264
1360
|
}
|
|
1265
1361
|
var SHEET_LAYOUT = {
|
|
1266
1362
|
bottom: { container: { justifyContent: "flex-end", alignItems: "stretch" }, panel: { borderTopLeftRadius: 16, borderTopRightRadius: 16, padding: 16, maxHeight: "90vh", overflow: "auto" } },
|
|
@@ -1271,7 +1367,7 @@ var SHEET_LAYOUT = {
|
|
|
1271
1367
|
function Sheet(props) {
|
|
1272
1368
|
const control = useModalControl();
|
|
1273
1369
|
const layout = SHEET_LAYOUT[props.side ?? "bottom"];
|
|
1274
|
-
return
|
|
1370
|
+
return React.createElement(OverlayShell, {
|
|
1275
1371
|
...control,
|
|
1276
1372
|
className: props.className,
|
|
1277
1373
|
backdropClassName: props.backdropClassName,
|
|
@@ -1284,7 +1380,7 @@ function Sheet(props) {
|
|
|
1284
1380
|
}
|
|
1285
1381
|
function Modal(props) {
|
|
1286
1382
|
const control = useModalControl();
|
|
1287
|
-
return
|
|
1383
|
+
return React.createElement(OverlayShell, {
|
|
1288
1384
|
...control,
|
|
1289
1385
|
className: props.className,
|
|
1290
1386
|
backdropClassName: props.backdropClassName,
|
|
@@ -1297,263 +1393,412 @@ function Modal(props) {
|
|
|
1297
1393
|
}
|
|
1298
1394
|
|
|
1299
1395
|
// src/commerce/checkout.tsx
|
|
1300
|
-
var INLINE_SURFACES = /* @__PURE__ */ new Set([
|
|
1301
|
-
"express",
|
|
1302
|
-
"element",
|
|
1303
|
-
"embedded"
|
|
1304
|
-
]);
|
|
1305
|
-
function isInlineSurface(surface) {
|
|
1306
|
-
return INLINE_SURFACES.has(surface);
|
|
1307
|
-
}
|
|
1308
|
-
var ON = { purchase: true, wallets: true, offSession: "reliable" };
|
|
1309
|
-
var MANAGED = { purchase: true, wallets: true, offSession: "conditional" };
|
|
1310
|
-
var ALL_UPSELLS = ["subscription", "one-time", "upgrade"];
|
|
1311
|
-
var PROVIDER_PROFILES = {
|
|
1312
|
-
// PSP; you hold the PaymentMethod and fire your own off-session PaymentIntent on
|
|
1313
|
-
// the Elements surfaces (reliable). **Managed** Checkout (`embedded`/`redirect`)
|
|
1314
|
-
// upsells too, but as a direct charge against Stripe's vaulted card → conditional.
|
|
1315
|
-
// No provider "popup" surface.
|
|
1316
|
-
stripe: {
|
|
1317
|
-
isMoR: false,
|
|
1318
|
-
offSessionUpsells: ALL_UPSELLS,
|
|
1319
|
-
tokenModel: "merchant",
|
|
1320
|
-
orchestrator: false,
|
|
1321
|
-
surfaces: { express: ON, element: ON, sheet: ON, embedded: MANAGED, redirect: MANAGED }
|
|
1322
|
-
},
|
|
1323
|
-
// Merchant-of-Record; card entry is always Paddle's frame → no own element/sheet/
|
|
1324
|
-
// express. Off-session via subscription one-time charge / collection_mode, but
|
|
1325
|
-
// **no 2nd concurrent subscription** (upgrade + one-time only), provider-initiated.
|
|
1326
|
-
paddle: {
|
|
1327
|
-
isMoR: true,
|
|
1328
|
-
offSessionUpsells: ["one-time", "upgrade"],
|
|
1329
|
-
tokenModel: "provider",
|
|
1330
|
-
orchestrator: false,
|
|
1331
|
-
surfaces: { embedded: ON, popup: ON, redirect: ON }
|
|
1332
|
-
},
|
|
1333
|
-
// Merchant-of-Record; iframe-only (no raw card field). Off-session works
|
|
1334
|
-
// (provider-initiated, async via webhooks). `sheet` = our chrome hosting their embed.
|
|
1335
|
-
whop: {
|
|
1336
|
-
isMoR: true,
|
|
1337
|
-
offSessionUpsells: ALL_UPSELLS,
|
|
1338
|
-
tokenModel: "provider",
|
|
1339
|
-
orchestrator: false,
|
|
1340
|
-
surfaces: { embedded: ON, redirect: ON, sheet: ON }
|
|
1341
|
-
},
|
|
1342
|
-
// Pure orchestrator (PSP-side); Headless renders express/element/sheet/embedded.
|
|
1343
|
-
// No hosted redirect page, no popup checkout. Off-session is PSP-conditional.
|
|
1344
|
-
primer: {
|
|
1345
|
-
isMoR: false,
|
|
1346
|
-
offSessionUpsells: ALL_UPSELLS,
|
|
1347
|
-
tokenModel: "merchant",
|
|
1348
|
-
orchestrator: true,
|
|
1349
|
-
surfaces: { express: ON, element: ON, sheet: ON, embedded: ON }
|
|
1350
|
-
},
|
|
1351
|
-
// PSP **and** orchestrator (own acquiring + connectors routing across Stripe/Adyen/
|
|
1352
|
-
// PayPal). One Payment Form (element/sheet/embedded) + hosted page + wallet button.
|
|
1353
|
-
// Strong native off-session (Recurring API). No popup.
|
|
1354
|
-
solidgate: {
|
|
1355
|
-
isMoR: false,
|
|
1356
|
-
offSessionUpsells: ALL_UPSELLS,
|
|
1357
|
-
tokenModel: "merchant",
|
|
1358
|
-
orchestrator: true,
|
|
1359
|
-
surfaces: { express: ON, element: ON, sheet: ON, embedded: ON, redirect: ON }
|
|
1360
|
-
}
|
|
1361
|
-
};
|
|
1362
|
-
function surfacesFor(provider) {
|
|
1363
|
-
return Object.keys(PROVIDER_PROFILES[provider]?.surfaces ?? {});
|
|
1364
|
-
}
|
|
1365
|
-
function isMerchantOfRecord(provider) {
|
|
1366
|
-
return PROVIDER_PROFILES[provider]?.isMoR ?? false;
|
|
1367
|
-
}
|
|
1368
|
-
function isOrchestrator(provider) {
|
|
1369
|
-
return PROVIDER_PROFILES[provider]?.orchestrator ?? false;
|
|
1370
|
-
}
|
|
1371
|
-
function validateCheckout(provider, surface) {
|
|
1372
|
-
const cap = PROVIDER_PROFILES[provider]?.surfaces[surface];
|
|
1373
|
-
if (!cap) return { ok: false, reason: `${provider} has no '${surface}' surface` };
|
|
1374
|
-
return cap.purchase ? { ok: true } : { ok: false, reason: `'${surface}' can't take a purchase on ${provider}` };
|
|
1375
|
-
}
|
|
1376
|
-
function validateUpsell(provider, paywallSurface, kind = "subscription") {
|
|
1377
|
-
const profile = PROVIDER_PROFILES[provider];
|
|
1378
|
-
if (!profile) return { ok: false, reason: `unknown provider '${provider}'` };
|
|
1379
|
-
if (!profile.offSessionUpsells.includes(kind)) {
|
|
1380
|
-
const can = profile.offSessionUpsells.length ? ` (it supports: ${profile.offSessionUpsells.join(", ")})` : "";
|
|
1381
|
-
return { ok: false, reason: `${provider} can't charge a '${kind}' upsell off-session${can}` };
|
|
1382
|
-
}
|
|
1383
|
-
const cap = profile.surfaces[paywallSurface];
|
|
1384
|
-
if (!cap) return { ok: false, reason: `${provider} has no '${paywallSurface}' surface to upsell after` };
|
|
1385
|
-
if (cap.offSession === "none") {
|
|
1386
|
-
return { ok: false, reason: `a purchase on '${paywallSurface}' retains no chargeable method for an upsell` };
|
|
1387
|
-
}
|
|
1388
|
-
if (cap.offSession === "conditional") {
|
|
1389
|
-
return { ok: true, note: `off-session upsell after ${provider} '${paywallSurface}' (managed checkout) may decline \u2014 prefer element/sheet/express` };
|
|
1390
|
-
}
|
|
1391
|
-
return { ok: true };
|
|
1392
|
-
}
|
|
1393
|
-
function checkoutError(category, message, extra = {}) {
|
|
1394
|
-
const retryable = extra.retryable ?? (category === "authentication_required" || category === "requires_payment_method" || category === "processing_error");
|
|
1395
|
-
return { category, message, declineCode: extra.declineCode, retryable };
|
|
1396
|
-
}
|
|
1397
1396
|
function createMockDriver(provider = "stripe", options = {}) {
|
|
1398
|
-
const success = options.result ?? {
|
|
1399
|
-
|
|
1397
|
+
const success = options.result ?? {
|
|
1398
|
+
ok: true,
|
|
1399
|
+
amountMinor: 0,
|
|
1400
|
+
currency: "USD"
|
|
1401
|
+
};
|
|
1402
|
+
const offError = typeof options.failOffSession === "object" ? options.failOffSession : chunk7JLOF6CJ_cjs.checkoutError(
|
|
1403
|
+
"authentication_required",
|
|
1404
|
+
"off-session charge declined (mock)"
|
|
1405
|
+
);
|
|
1400
1406
|
return {
|
|
1401
1407
|
provider,
|
|
1402
1408
|
start: (req) => {
|
|
1403
|
-
if (options.failOffSession && !req.surface)
|
|
1409
|
+
if (options.failOffSession && !req.surface)
|
|
1410
|
+
return Promise.resolve({ ok: false, error: offError });
|
|
1404
1411
|
return Promise.resolve(success);
|
|
1405
1412
|
},
|
|
1406
|
-
renderInline: (req, cb) =>
|
|
1413
|
+
renderInline: (req, cb) => React.createElement(
|
|
1407
1414
|
"button",
|
|
1408
1415
|
{
|
|
1409
1416
|
type: "button",
|
|
1410
1417
|
"data-mock-surface": req.surface,
|
|
1411
|
-
onClick: () =>
|
|
1418
|
+
onClick: () => {
|
|
1419
|
+
cb.onStart?.();
|
|
1420
|
+
if (success.ok) cb.onSuccess(success);
|
|
1421
|
+
else
|
|
1422
|
+
cb.onError(
|
|
1423
|
+
success.error ?? chunk7JLOF6CJ_cjs.checkoutError(
|
|
1424
|
+
"processing_error",
|
|
1425
|
+
"failed (mock)"
|
|
1426
|
+
)
|
|
1427
|
+
);
|
|
1428
|
+
}
|
|
1412
1429
|
},
|
|
1413
1430
|
`Pay (mock ${req.surface})`
|
|
1414
|
-
)
|
|
1431
|
+
),
|
|
1432
|
+
resume: () => Promise.resolve(success)
|
|
1433
|
+
};
|
|
1434
|
+
}
|
|
1435
|
+
function driverWithEmail(driver, getEmail) {
|
|
1436
|
+
const enrich = (req) => req.email !== void 0 ? req : { ...req, email: getEmail() };
|
|
1437
|
+
return {
|
|
1438
|
+
...driver,
|
|
1439
|
+
start: (req) => driver.start(enrich(req)),
|
|
1440
|
+
renderInline: driver.renderInline ? (req, cb) => driver.renderInline(enrich(req), cb) : void 0
|
|
1441
|
+
};
|
|
1442
|
+
}
|
|
1443
|
+
function driverWithCatalog(driver, catalog) {
|
|
1444
|
+
const enrich = (req) => req.catalogKey !== void 0 ? req : { ...req, catalogKey: catalog.get(req.product)?.catalogKey };
|
|
1445
|
+
return {
|
|
1446
|
+
...driver,
|
|
1447
|
+
start: (req) => driver.start(enrich(req)),
|
|
1448
|
+
renderInline: driver.renderInline ? (req, cb) => driver.renderInline(enrich(req), cb) : void 0
|
|
1415
1449
|
};
|
|
1416
1450
|
}
|
|
1417
|
-
var DriverContext =
|
|
1451
|
+
var DriverContext = React.createContext(null);
|
|
1418
1452
|
function CheckoutDriverProvider({
|
|
1419
1453
|
driver,
|
|
1420
1454
|
children
|
|
1421
1455
|
}) {
|
|
1422
|
-
return
|
|
1456
|
+
return React.createElement(DriverContext.Provider, { value: driver }, children);
|
|
1423
1457
|
}
|
|
1424
1458
|
function useDriver() {
|
|
1425
|
-
return
|
|
1459
|
+
return React.useContext(DriverContext) ?? FALLBACK_DRIVER;
|
|
1426
1460
|
}
|
|
1427
1461
|
var FALLBACK_DRIVER = createMockDriver();
|
|
1428
1462
|
function useCheckout(opts = {}) {
|
|
1429
1463
|
const driver = useDriver();
|
|
1430
1464
|
const tracker = useTrackerRef();
|
|
1465
|
+
const catalog = useCatalog();
|
|
1431
1466
|
const nav = useNavigation();
|
|
1432
|
-
const [status, setStatus] =
|
|
1433
|
-
const [error, setError] =
|
|
1467
|
+
const [status, setStatus] = React.useState("idle");
|
|
1468
|
+
const [error, setError] = React.useState(null);
|
|
1434
1469
|
const intent = opts.intent ?? "purchase";
|
|
1435
1470
|
const kind = opts.kind;
|
|
1436
1471
|
const advance = opts.advanceOnSuccess ?? true;
|
|
1437
|
-
const
|
|
1438
|
-
(
|
|
1472
|
+
const productFacts = React.useCallback(
|
|
1473
|
+
(product) => {
|
|
1474
|
+
const resolved = product ? catalog.get(product) : void 0;
|
|
1475
|
+
return {
|
|
1476
|
+
productId: resolved?.catalogKey ?? product,
|
|
1477
|
+
amount: resolved ? resolved.priceMinor / 100 : void 0,
|
|
1478
|
+
currency: resolved?.currency,
|
|
1479
|
+
isSubscription: resolved ? resolved.period !== "one-time" : kind === "subscription" || kind === "upgrade"
|
|
1480
|
+
};
|
|
1481
|
+
},
|
|
1482
|
+
[catalog, kind]
|
|
1483
|
+
);
|
|
1484
|
+
const recovering = React.useRef(false);
|
|
1485
|
+
const failRef = React.useRef(
|
|
1486
|
+
() => {
|
|
1487
|
+
}
|
|
1488
|
+
);
|
|
1489
|
+
const attemptRef = React.useRef(async () => ({ ok: false }));
|
|
1490
|
+
const succeed = React.useCallback(
|
|
1491
|
+
(result, product) => {
|
|
1439
1492
|
setStatus("success");
|
|
1493
|
+
const { productId, isSubscription } = productFacts(product);
|
|
1494
|
+
const amount = result.amountMinor != null ? result.amountMinor / 100 : void 0;
|
|
1440
1495
|
tracker.track("purchase.complete", {
|
|
1441
|
-
amount
|
|
1496
|
+
amount,
|
|
1442
1497
|
currency: result.currency,
|
|
1498
|
+
productId,
|
|
1443
1499
|
eventId: result.eventId
|
|
1444
1500
|
// server-minted (browser↔CAPI dedup); undefined if none
|
|
1445
1501
|
});
|
|
1502
|
+
if (isSubscription) {
|
|
1503
|
+
tracker.track("subscription.created", {
|
|
1504
|
+
amount,
|
|
1505
|
+
currency: result.currency,
|
|
1506
|
+
productId,
|
|
1507
|
+
eventId: result.eventId
|
|
1508
|
+
});
|
|
1509
|
+
}
|
|
1446
1510
|
opts.onSuccess?.(result);
|
|
1447
1511
|
if (advance) nav.next();
|
|
1448
1512
|
},
|
|
1449
|
-
[tracker, nav, advance, opts]
|
|
1513
|
+
[tracker, nav, advance, opts, productFacts]
|
|
1450
1514
|
);
|
|
1451
|
-
const
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
setError(err);
|
|
1455
|
-
tracker.track("checkout.failed", {
|
|
1456
|
-
category: err.category,
|
|
1457
|
-
declineCode: err.declineCode,
|
|
1458
|
-
productId: product,
|
|
1459
|
-
intent
|
|
1460
|
-
});
|
|
1461
|
-
opts.onError?.(err);
|
|
1462
|
-
},
|
|
1463
|
-
[tracker, intent, opts]
|
|
1464
|
-
);
|
|
1465
|
-
const begin = react.useCallback(
|
|
1515
|
+
const succeedRef = React.useRef(succeed);
|
|
1516
|
+
succeedRef.current = succeed;
|
|
1517
|
+
const begin = React.useCallback(
|
|
1466
1518
|
(product, surface) => {
|
|
1467
1519
|
setStatus("loading");
|
|
1468
1520
|
setError(null);
|
|
1469
|
-
|
|
1521
|
+
const { productId, amount, currency } = productFacts(product);
|
|
1522
|
+
tracker.track("checkout.start", {
|
|
1523
|
+
productId,
|
|
1524
|
+
amount,
|
|
1525
|
+
currency,
|
|
1526
|
+
surface,
|
|
1527
|
+
eventId: newEventId()
|
|
1528
|
+
});
|
|
1470
1529
|
},
|
|
1471
|
-
[tracker]
|
|
1530
|
+
[tracker, productFacts]
|
|
1531
|
+
);
|
|
1532
|
+
const paymentAdded = React.useCallback(
|
|
1533
|
+
(product) => {
|
|
1534
|
+
const { productId, amount, currency } = productFacts(product);
|
|
1535
|
+
tracker.track("checkout.payment_added", {
|
|
1536
|
+
productId,
|
|
1537
|
+
amount,
|
|
1538
|
+
currency,
|
|
1539
|
+
eventId: newEventId()
|
|
1540
|
+
});
|
|
1541
|
+
},
|
|
1542
|
+
[tracker, productFacts]
|
|
1472
1543
|
);
|
|
1473
|
-
const
|
|
1474
|
-
const
|
|
1544
|
+
const hostInSheet = (surface) => surface === "sheet" || chunk7JLOF6CJ_cjs.isInlineSurface(surface) && !!chunk7JLOF6CJ_cjs.PROVIDER_PROFILES[driver.provider]?.surfaces.sheet;
|
|
1545
|
+
const startRecovery = (product, surface) => {
|
|
1546
|
+
recovering.current = true;
|
|
1547
|
+
if (hostInSheet(surface)) {
|
|
1548
|
+
begin(product, "sheet");
|
|
1549
|
+
void showModal(CheckoutSheet, {
|
|
1550
|
+
product,
|
|
1551
|
+
surface: "sheet",
|
|
1552
|
+
intent,
|
|
1553
|
+
kind,
|
|
1554
|
+
onSuccess: (r) => succeedRef.current(r, product),
|
|
1555
|
+
onError: (e) => failRef.current(e, product),
|
|
1556
|
+
onPaymentAdded: () => paymentAdded(product)
|
|
1557
|
+
});
|
|
1558
|
+
} else {
|
|
1559
|
+
void attemptRef.current(product, surface);
|
|
1560
|
+
}
|
|
1561
|
+
};
|
|
1562
|
+
failRef.current = (err, product) => {
|
|
1563
|
+
setStatus(
|
|
1564
|
+
err.category === "authentication_required" ? "requires_action" : "error"
|
|
1565
|
+
);
|
|
1566
|
+
setError(err);
|
|
1567
|
+
tracker.track("checkout.failed", {
|
|
1568
|
+
category: err.category,
|
|
1569
|
+
declineCode: err.declineCode,
|
|
1570
|
+
productId: product,
|
|
1571
|
+
intent
|
|
1572
|
+
});
|
|
1573
|
+
opts.onError?.(err);
|
|
1574
|
+
if (err.category === "canceled" && !opts.onFailed?.canceled) return;
|
|
1575
|
+
if (product && !recovering.current && opts.recoverySurface && err.category !== "canceled") {
|
|
1576
|
+
startRecovery(product, opts.recoverySurface);
|
|
1577
|
+
return;
|
|
1578
|
+
}
|
|
1579
|
+
let route = opts.onFailed?.[err.category] ?? (err.category === "canceled" ? void 0 : opts.onFailed?.default);
|
|
1580
|
+
if (route && typeof route === "object" && "fallback" in route && recovering.current) {
|
|
1581
|
+
const dflt = opts.onFailed?.default;
|
|
1582
|
+
route = dflt && dflt !== route && !(typeof dflt === "object" && "fallback" in dflt) ? dflt : void 0;
|
|
1583
|
+
}
|
|
1584
|
+
if (!route) return;
|
|
1585
|
+
if (typeof route === "function") route(err);
|
|
1586
|
+
else if ("go" in route) nav.go(route.go);
|
|
1587
|
+
else if ("modal" in route) {
|
|
1588
|
+
const props = {
|
|
1589
|
+
error: err,
|
|
1590
|
+
product,
|
|
1591
|
+
go: (pageKey) => nav.go(pageKey),
|
|
1592
|
+
retry: (surface) => {
|
|
1593
|
+
if (!product) return;
|
|
1594
|
+
recovering.current = true;
|
|
1595
|
+
if (!surface) {
|
|
1596
|
+
if (intent === "upsell")
|
|
1597
|
+
void attemptRef.current(product);
|
|
1598
|
+
else
|
|
1599
|
+
startRecovery(
|
|
1600
|
+
product,
|
|
1601
|
+
chunk7JLOF6CJ_cjs.PROVIDER_PROFILES[driver.provider]?.surfaces.sheet ? "sheet" : "popup"
|
|
1602
|
+
);
|
|
1603
|
+
return;
|
|
1604
|
+
}
|
|
1605
|
+
startRecovery(product, surface);
|
|
1606
|
+
}
|
|
1607
|
+
};
|
|
1608
|
+
void showModal(
|
|
1609
|
+
route.modal,
|
|
1610
|
+
props
|
|
1611
|
+
);
|
|
1612
|
+
} else if ("fallback" in route) {
|
|
1613
|
+
if (product && !recovering.current)
|
|
1614
|
+
startRecovery(product, route.fallback);
|
|
1615
|
+
}
|
|
1616
|
+
};
|
|
1617
|
+
attemptRef.current = async (product, surface) => {
|
|
1618
|
+
begin(product, surface);
|
|
1619
|
+
try {
|
|
1620
|
+
const result = await driver.start({
|
|
1621
|
+
product,
|
|
1622
|
+
intent,
|
|
1623
|
+
surface,
|
|
1624
|
+
kind
|
|
1625
|
+
});
|
|
1626
|
+
if (result.ok) succeedRef.current(result, product);
|
|
1627
|
+
else
|
|
1628
|
+
failRef.current(
|
|
1629
|
+
result.error ?? chunk7JLOF6CJ_cjs.checkoutError("unknown", "Checkout failed"),
|
|
1630
|
+
product
|
|
1631
|
+
);
|
|
1632
|
+
return result;
|
|
1633
|
+
} catch (e) {
|
|
1634
|
+
const err = chunk7JLOF6CJ_cjs.checkoutError(
|
|
1635
|
+
"processing_error",
|
|
1636
|
+
e instanceof Error ? e.message : "Checkout failed"
|
|
1637
|
+
);
|
|
1638
|
+
failRef.current(err, product);
|
|
1639
|
+
return { ok: false, error: err };
|
|
1640
|
+
}
|
|
1641
|
+
};
|
|
1642
|
+
const open = React.useCallback(
|
|
1475
1643
|
async (product, surface) => {
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
const result = await driver.start({ product, intent, surface, kind });
|
|
1479
|
-
if (result.ok) succeed(result);
|
|
1480
|
-
else fail(result.error ?? checkoutError("unknown", "Checkout failed"), product);
|
|
1481
|
-
return result;
|
|
1482
|
-
} catch (e) {
|
|
1483
|
-
const err = checkoutError("processing_error", e instanceof Error ? e.message : "Checkout failed");
|
|
1484
|
-
fail(err, product);
|
|
1485
|
-
return { ok: false, error: err };
|
|
1486
|
-
}
|
|
1644
|
+
recovering.current = false;
|
|
1645
|
+
return attemptRef.current(product, surface);
|
|
1487
1646
|
},
|
|
1488
|
-
[
|
|
1647
|
+
[]
|
|
1489
1648
|
);
|
|
1490
|
-
const callbacksFor =
|
|
1649
|
+
const callbacksFor = React.useCallback(
|
|
1491
1650
|
(product, surface) => ({
|
|
1492
|
-
onStart: () =>
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1651
|
+
onStart: () => {
|
|
1652
|
+
recovering.current = false;
|
|
1653
|
+
begin(product, surface);
|
|
1654
|
+
},
|
|
1655
|
+
onSuccess: (result) => succeedRef.current(result, product),
|
|
1656
|
+
onError: (err) => failRef.current(err, product),
|
|
1657
|
+
onPaymentAdded: () => paymentAdded(product)
|
|
1496
1658
|
}),
|
|
1497
|
-
[begin,
|
|
1659
|
+
[begin, paymentAdded]
|
|
1498
1660
|
);
|
|
1499
|
-
const reset =
|
|
1661
|
+
const reset = React.useCallback(() => {
|
|
1662
|
+
recovering.current = false;
|
|
1500
1663
|
setStatus("idle");
|
|
1501
1664
|
setError(null);
|
|
1502
1665
|
}, []);
|
|
1503
|
-
return {
|
|
1666
|
+
return {
|
|
1667
|
+
open,
|
|
1668
|
+
status,
|
|
1669
|
+
error,
|
|
1670
|
+
isLoading: status === "loading",
|
|
1671
|
+
reset,
|
|
1672
|
+
callbacksFor
|
|
1673
|
+
};
|
|
1504
1674
|
}
|
|
1505
|
-
var CheckoutSheet = defineModal(({ product, surface, intent, onSuccess, onError }) => {
|
|
1675
|
+
var CheckoutSheet = defineModal(({ product, surface, intent, kind, onSuccess, onError, onPaymentAdded }) => {
|
|
1506
1676
|
const driver = useDriver();
|
|
1507
1677
|
const modal = useModal();
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1678
|
+
const resolved = React.useRef(false);
|
|
1679
|
+
const onErrorRef = React.useRef(onError);
|
|
1680
|
+
onErrorRef.current = onError;
|
|
1681
|
+
const hostable = !!driver.renderInline;
|
|
1682
|
+
React.useEffect(() => {
|
|
1683
|
+
if (!hostable) {
|
|
1684
|
+
resolved.current = true;
|
|
1685
|
+
onErrorRef.current(
|
|
1686
|
+
chunk7JLOF6CJ_cjs.checkoutError(
|
|
1687
|
+
"processing_error",
|
|
1688
|
+
"This checkout driver has no inline surface to host in the sheet"
|
|
1689
|
+
)
|
|
1690
|
+
);
|
|
1691
|
+
void modal.hide();
|
|
1692
|
+
modal.remove();
|
|
1693
|
+
return;
|
|
1694
|
+
}
|
|
1695
|
+
return () => {
|
|
1696
|
+
if (!resolved.current) {
|
|
1697
|
+
onErrorRef.current(
|
|
1698
|
+
chunk7JLOF6CJ_cjs.checkoutError(
|
|
1699
|
+
"canceled",
|
|
1700
|
+
"Checkout was dismissed before completing"
|
|
1701
|
+
)
|
|
1702
|
+
);
|
|
1703
|
+
}
|
|
1704
|
+
};
|
|
1705
|
+
}, []);
|
|
1706
|
+
if (!driver.renderInline) return null;
|
|
1707
|
+
const close = () => {
|
|
1708
|
+
resolved.current = true;
|
|
1709
|
+
void modal.hide();
|
|
1710
|
+
modal.remove();
|
|
1711
|
+
};
|
|
1712
|
+
return React.createElement(Sheet, {
|
|
1713
|
+
children: driver.renderInline(
|
|
1714
|
+
{ product, intent, surface, kind },
|
|
1512
1715
|
{
|
|
1513
1716
|
onSuccess: (r) => {
|
|
1514
|
-
|
|
1717
|
+
close();
|
|
1515
1718
|
onSuccess(r);
|
|
1516
1719
|
},
|
|
1517
1720
|
onError: (e) => {
|
|
1518
|
-
|
|
1721
|
+
close();
|
|
1519
1722
|
onError(e);
|
|
1520
|
-
}
|
|
1723
|
+
},
|
|
1724
|
+
onPaymentAdded
|
|
1521
1725
|
}
|
|
1522
1726
|
)
|
|
1523
1727
|
});
|
|
1524
1728
|
});
|
|
1525
|
-
function trigger(onClick, {
|
|
1526
|
-
|
|
1729
|
+
function trigger(onClick, {
|
|
1730
|
+
asChild,
|
|
1731
|
+
children,
|
|
1732
|
+
className,
|
|
1733
|
+
disabled
|
|
1734
|
+
}) {
|
|
1735
|
+
if (asChild && React.isValidElement(children)) {
|
|
1527
1736
|
const child = children;
|
|
1528
|
-
return
|
|
1737
|
+
return React.cloneElement(child, { onClick });
|
|
1738
|
+
}
|
|
1739
|
+
return React.createElement(
|
|
1740
|
+
"button",
|
|
1741
|
+
{ type: "button", className, disabled, onClick },
|
|
1742
|
+
children ?? "Continue"
|
|
1743
|
+
);
|
|
1744
|
+
}
|
|
1745
|
+
function detectDeviceClass() {
|
|
1746
|
+
if (typeof window === "undefined") return "desktop";
|
|
1747
|
+
const ua = navigator.userAgent?.toLowerCase() ?? "";
|
|
1748
|
+
if (/mobile|android(?!.*tablet)|iphone|ipod/.test(ua)) return "mobile";
|
|
1749
|
+
try {
|
|
1750
|
+
if (window.matchMedia?.("(pointer: coarse)").matches && window.innerWidth > 0 && window.innerWidth < 768) {
|
|
1751
|
+
return "mobile";
|
|
1752
|
+
}
|
|
1753
|
+
} catch {
|
|
1529
1754
|
}
|
|
1530
|
-
return
|
|
1755
|
+
return "desktop";
|
|
1531
1756
|
}
|
|
1532
1757
|
function Checkout({
|
|
1533
|
-
|
|
1534
|
-
surface,
|
|
1758
|
+
offering,
|
|
1759
|
+
surface: surfacePreference,
|
|
1535
1760
|
asChild,
|
|
1536
1761
|
children,
|
|
1537
1762
|
className,
|
|
1538
1763
|
...options
|
|
1539
1764
|
}) {
|
|
1765
|
+
const product = offering;
|
|
1540
1766
|
const driver = useDriver();
|
|
1767
|
+
const [surface] = React.useState(
|
|
1768
|
+
() => chunk7JLOF6CJ_cjs.resolveSurface(driver.provider, surfacePreference, detectDeviceClass())
|
|
1769
|
+
);
|
|
1541
1770
|
const checkout = useCheckout(options);
|
|
1542
1771
|
const intent = options.intent ?? "purchase";
|
|
1543
|
-
if (isInlineSurface(surface)) {
|
|
1544
|
-
return driver.renderInline ?
|
|
1772
|
+
if (chunk7JLOF6CJ_cjs.isInlineSurface(surface)) {
|
|
1773
|
+
return driver.renderInline ? React.createElement(
|
|
1545
1774
|
"div",
|
|
1546
1775
|
{ key: product, className },
|
|
1547
|
-
driver.renderInline(
|
|
1776
|
+
driver.renderInline(
|
|
1777
|
+
{ product, intent, surface },
|
|
1778
|
+
checkout.callbacksFor(product, surface)
|
|
1779
|
+
)
|
|
1548
1780
|
) : null;
|
|
1549
1781
|
}
|
|
1550
1782
|
if (surface === "sheet") {
|
|
1551
1783
|
const cb = checkout.callbacksFor(product, surface);
|
|
1552
1784
|
const open = () => {
|
|
1553
1785
|
cb.onStart();
|
|
1554
|
-
showModal(CheckoutSheet, {
|
|
1786
|
+
showModal(CheckoutSheet, {
|
|
1787
|
+
product,
|
|
1788
|
+
surface,
|
|
1789
|
+
intent,
|
|
1790
|
+
kind: options.kind,
|
|
1791
|
+
onSuccess: cb.onSuccess,
|
|
1792
|
+
onError: cb.onError,
|
|
1793
|
+
onPaymentAdded: cb.onPaymentAdded
|
|
1794
|
+
});
|
|
1555
1795
|
};
|
|
1556
|
-
return trigger(open, {
|
|
1796
|
+
return trigger(open, {
|
|
1797
|
+
asChild,
|
|
1798
|
+
children,
|
|
1799
|
+
className,
|
|
1800
|
+
disabled: checkout.isLoading
|
|
1801
|
+
});
|
|
1557
1802
|
}
|
|
1558
1803
|
return trigger(() => checkout.open(product, surface), {
|
|
1559
1804
|
asChild,
|
|
@@ -1562,32 +1807,1150 @@ function Checkout({
|
|
|
1562
1807
|
disabled: checkout.isLoading
|
|
1563
1808
|
});
|
|
1564
1809
|
}
|
|
1565
|
-
function Upsell({
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1810
|
+
function Upsell({
|
|
1811
|
+
offering,
|
|
1812
|
+
fallback,
|
|
1813
|
+
asChild,
|
|
1814
|
+
children,
|
|
1815
|
+
className,
|
|
1816
|
+
...options
|
|
1817
|
+
}) {
|
|
1818
|
+
const product = offering;
|
|
1819
|
+
const checkout = useCheckout({
|
|
1820
|
+
...options,
|
|
1821
|
+
intent: "upsell",
|
|
1822
|
+
recoverySurface: fallback ?? options.recoverySurface
|
|
1823
|
+
});
|
|
1824
|
+
return trigger(
|
|
1825
|
+
() => void checkout.open(product),
|
|
1826
|
+
{
|
|
1827
|
+
asChild,
|
|
1828
|
+
children: children ?? "Add to my plan",
|
|
1829
|
+
className,
|
|
1830
|
+
disabled: checkout.isLoading
|
|
1831
|
+
}
|
|
1832
|
+
);
|
|
1833
|
+
}
|
|
1834
|
+
var consumedResumes = /* @__PURE__ */ new Set();
|
|
1835
|
+
function CheckoutResume(options = {}) {
|
|
1836
|
+
const driver = useDriver();
|
|
1837
|
+
const checkout = useCheckout(options);
|
|
1838
|
+
const callbacksRef = React.useRef(checkout.callbacksFor);
|
|
1839
|
+
callbacksRef.current = checkout.callbacksFor;
|
|
1840
|
+
React.useEffect(() => {
|
|
1841
|
+
if (typeof window === "undefined" || !driver.resume) return;
|
|
1842
|
+
const url = new URL(window.location.href);
|
|
1843
|
+
const correlationId = url.searchParams.get("af_checkout");
|
|
1844
|
+
if (!correlationId || consumedResumes.has(correlationId)) return;
|
|
1845
|
+
consumedResumes.add(correlationId);
|
|
1846
|
+
void driver.resume(correlationId).then((result) => {
|
|
1847
|
+
const cb = callbacksRef.current();
|
|
1848
|
+
if (result.ok) {
|
|
1849
|
+
url.searchParams.delete("af_checkout");
|
|
1850
|
+
window.history.replaceState(
|
|
1851
|
+
window.history.state,
|
|
1852
|
+
"",
|
|
1853
|
+
url.toString()
|
|
1854
|
+
);
|
|
1855
|
+
cb.onSuccess(result);
|
|
1856
|
+
} else {
|
|
1857
|
+
cb.onError(
|
|
1858
|
+
result.error ?? chunk7JLOF6CJ_cjs.checkoutError(
|
|
1859
|
+
"unknown",
|
|
1860
|
+
"The checkout could not be verified"
|
|
1861
|
+
)
|
|
1862
|
+
);
|
|
1863
|
+
}
|
|
1864
|
+
});
|
|
1865
|
+
}, []);
|
|
1866
|
+
return null;
|
|
1867
|
+
}
|
|
1868
|
+
function __insertCSS(code) {
|
|
1869
|
+
if (typeof document == "undefined") return;
|
|
1870
|
+
let head = document.head || document.getElementsByTagName("head")[0];
|
|
1871
|
+
let style = document.createElement("style");
|
|
1872
|
+
style.type = "text/css";
|
|
1873
|
+
head.appendChild(style);
|
|
1874
|
+
style.styleSheet ? style.styleSheet.cssText = code : style.appendChild(document.createTextNode(code));
|
|
1875
|
+
}
|
|
1876
|
+
var getAsset = (type) => {
|
|
1877
|
+
switch (type) {
|
|
1878
|
+
case "success":
|
|
1879
|
+
return SuccessIcon;
|
|
1880
|
+
case "info":
|
|
1881
|
+
return InfoIcon;
|
|
1882
|
+
case "warning":
|
|
1883
|
+
return WarningIcon;
|
|
1884
|
+
case "error":
|
|
1885
|
+
return ErrorIcon;
|
|
1886
|
+
default:
|
|
1887
|
+
return null;
|
|
1888
|
+
}
|
|
1889
|
+
};
|
|
1890
|
+
var bars = Array(12).fill(0);
|
|
1891
|
+
var Loader = ({ visible, className }) => {
|
|
1892
|
+
return /* @__PURE__ */ React__default.default.createElement("div", {
|
|
1893
|
+
className: [
|
|
1894
|
+
"sonner-loading-wrapper",
|
|
1895
|
+
className
|
|
1896
|
+
].filter(Boolean).join(" "),
|
|
1897
|
+
"data-visible": visible
|
|
1898
|
+
}, /* @__PURE__ */ React__default.default.createElement("div", {
|
|
1899
|
+
className: "sonner-spinner"
|
|
1900
|
+
}, bars.map((_, i) => /* @__PURE__ */ React__default.default.createElement("div", {
|
|
1901
|
+
className: "sonner-loading-bar",
|
|
1902
|
+
key: `spinner-bar-${i}`
|
|
1903
|
+
}))));
|
|
1904
|
+
};
|
|
1905
|
+
var SuccessIcon = /* @__PURE__ */ React__default.default.createElement("svg", {
|
|
1906
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1907
|
+
viewBox: "0 0 20 20",
|
|
1908
|
+
fill: "currentColor",
|
|
1909
|
+
height: "20",
|
|
1910
|
+
width: "20"
|
|
1911
|
+
}, /* @__PURE__ */ React__default.default.createElement("path", {
|
|
1912
|
+
fillRule: "evenodd",
|
|
1913
|
+
d: "M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z",
|
|
1914
|
+
clipRule: "evenodd"
|
|
1915
|
+
}));
|
|
1916
|
+
var WarningIcon = /* @__PURE__ */ React__default.default.createElement("svg", {
|
|
1917
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1918
|
+
viewBox: "0 0 24 24",
|
|
1919
|
+
fill: "currentColor",
|
|
1920
|
+
height: "20",
|
|
1921
|
+
width: "20"
|
|
1922
|
+
}, /* @__PURE__ */ React__default.default.createElement("path", {
|
|
1923
|
+
fillRule: "evenodd",
|
|
1924
|
+
d: "M9.401 3.003c1.155-2 4.043-2 5.197 0l7.355 12.748c1.154 2-.29 4.5-2.599 4.5H4.645c-2.309 0-3.752-2.5-2.598-4.5L9.4 3.003zM12 8.25a.75.75 0 01.75.75v3.75a.75.75 0 01-1.5 0V9a.75.75 0 01.75-.75zm0 8.25a.75.75 0 100-1.5.75.75 0 000 1.5z",
|
|
1925
|
+
clipRule: "evenodd"
|
|
1926
|
+
}));
|
|
1927
|
+
var InfoIcon = /* @__PURE__ */ React__default.default.createElement("svg", {
|
|
1928
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1929
|
+
viewBox: "0 0 20 20",
|
|
1930
|
+
fill: "currentColor",
|
|
1931
|
+
height: "20",
|
|
1932
|
+
width: "20"
|
|
1933
|
+
}, /* @__PURE__ */ React__default.default.createElement("path", {
|
|
1934
|
+
fillRule: "evenodd",
|
|
1935
|
+
d: "M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0 01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25 0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z",
|
|
1936
|
+
clipRule: "evenodd"
|
|
1937
|
+
}));
|
|
1938
|
+
var ErrorIcon = /* @__PURE__ */ React__default.default.createElement("svg", {
|
|
1939
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1940
|
+
viewBox: "0 0 20 20",
|
|
1941
|
+
fill: "currentColor",
|
|
1942
|
+
height: "20",
|
|
1943
|
+
width: "20"
|
|
1944
|
+
}, /* @__PURE__ */ React__default.default.createElement("path", {
|
|
1945
|
+
fillRule: "evenodd",
|
|
1946
|
+
d: "M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-5a.75.75 0 01.75.75v4.5a.75.75 0 01-1.5 0v-4.5A.75.75 0 0110 5zm0 10a1 1 0 100-2 1 1 0 000 2z",
|
|
1947
|
+
clipRule: "evenodd"
|
|
1948
|
+
}));
|
|
1949
|
+
var CloseIcon = /* @__PURE__ */ React__default.default.createElement("svg", {
|
|
1950
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1951
|
+
width: "12",
|
|
1952
|
+
height: "12",
|
|
1953
|
+
viewBox: "0 0 24 24",
|
|
1954
|
+
fill: "none",
|
|
1955
|
+
stroke: "currentColor",
|
|
1956
|
+
strokeWidth: "1.5",
|
|
1957
|
+
strokeLinecap: "round",
|
|
1958
|
+
strokeLinejoin: "round"
|
|
1959
|
+
}, /* @__PURE__ */ React__default.default.createElement("line", {
|
|
1960
|
+
x1: "18",
|
|
1961
|
+
y1: "6",
|
|
1962
|
+
x2: "6",
|
|
1963
|
+
y2: "18"
|
|
1964
|
+
}), /* @__PURE__ */ React__default.default.createElement("line", {
|
|
1965
|
+
x1: "6",
|
|
1966
|
+
y1: "6",
|
|
1967
|
+
x2: "18",
|
|
1968
|
+
y2: "18"
|
|
1969
|
+
}));
|
|
1970
|
+
var useIsDocumentHidden = () => {
|
|
1971
|
+
const [isDocumentHidden, setIsDocumentHidden] = React__default.default.useState(document.hidden);
|
|
1972
|
+
React__default.default.useEffect(() => {
|
|
1973
|
+
const callback = () => {
|
|
1974
|
+
setIsDocumentHidden(document.hidden);
|
|
1975
|
+
};
|
|
1976
|
+
document.addEventListener("visibilitychange", callback);
|
|
1977
|
+
return () => window.removeEventListener("visibilitychange", callback);
|
|
1978
|
+
}, []);
|
|
1979
|
+
return isDocumentHidden;
|
|
1980
|
+
};
|
|
1981
|
+
var toastsCounter = 1;
|
|
1982
|
+
var Observer = class {
|
|
1983
|
+
constructor() {
|
|
1984
|
+
this.subscribe = (subscriber) => {
|
|
1985
|
+
this.subscribers.push(subscriber);
|
|
1986
|
+
return () => {
|
|
1987
|
+
const index = this.subscribers.indexOf(subscriber);
|
|
1988
|
+
this.subscribers.splice(index, 1);
|
|
1989
|
+
};
|
|
1990
|
+
};
|
|
1991
|
+
this.publish = (data) => {
|
|
1992
|
+
this.subscribers.forEach((subscriber) => subscriber(data));
|
|
1993
|
+
};
|
|
1994
|
+
this.addToast = (data) => {
|
|
1995
|
+
this.publish(data);
|
|
1996
|
+
this.toasts = [
|
|
1997
|
+
...this.toasts,
|
|
1998
|
+
data
|
|
1999
|
+
];
|
|
2000
|
+
};
|
|
2001
|
+
this.create = (data) => {
|
|
2002
|
+
var _data_id;
|
|
2003
|
+
const { message, ...rest } = data;
|
|
2004
|
+
const id = typeof (data == null ? void 0 : data.id) === "number" || ((_data_id = data.id) == null ? void 0 : _data_id.length) > 0 ? data.id : toastsCounter++;
|
|
2005
|
+
const alreadyExists = this.toasts.find((toast2) => {
|
|
2006
|
+
return toast2.id === id;
|
|
2007
|
+
});
|
|
2008
|
+
const dismissible = data.dismissible === void 0 ? true : data.dismissible;
|
|
2009
|
+
if (this.dismissedToasts.has(id)) {
|
|
2010
|
+
this.dismissedToasts.delete(id);
|
|
2011
|
+
}
|
|
2012
|
+
if (alreadyExists) {
|
|
2013
|
+
this.toasts = this.toasts.map((toast2) => {
|
|
2014
|
+
if (toast2.id === id) {
|
|
2015
|
+
this.publish({
|
|
2016
|
+
...toast2,
|
|
2017
|
+
...data,
|
|
2018
|
+
id,
|
|
2019
|
+
title: message
|
|
2020
|
+
});
|
|
2021
|
+
return {
|
|
2022
|
+
...toast2,
|
|
2023
|
+
...data,
|
|
2024
|
+
id,
|
|
2025
|
+
dismissible,
|
|
2026
|
+
title: message
|
|
2027
|
+
};
|
|
2028
|
+
}
|
|
2029
|
+
return toast2;
|
|
2030
|
+
});
|
|
2031
|
+
} else {
|
|
2032
|
+
this.addToast({
|
|
2033
|
+
title: message,
|
|
2034
|
+
...rest,
|
|
2035
|
+
dismissible,
|
|
2036
|
+
id
|
|
2037
|
+
});
|
|
2038
|
+
}
|
|
2039
|
+
return id;
|
|
2040
|
+
};
|
|
2041
|
+
this.dismiss = (id) => {
|
|
2042
|
+
if (id) {
|
|
2043
|
+
this.dismissedToasts.add(id);
|
|
2044
|
+
requestAnimationFrame(() => this.subscribers.forEach((subscriber) => subscriber({
|
|
2045
|
+
id,
|
|
2046
|
+
dismiss: true
|
|
2047
|
+
})));
|
|
2048
|
+
} else {
|
|
2049
|
+
this.toasts.forEach((toast2) => {
|
|
2050
|
+
this.subscribers.forEach((subscriber) => subscriber({
|
|
2051
|
+
id: toast2.id,
|
|
2052
|
+
dismiss: true
|
|
2053
|
+
}));
|
|
2054
|
+
});
|
|
2055
|
+
}
|
|
2056
|
+
return id;
|
|
2057
|
+
};
|
|
2058
|
+
this.message = (message, data) => {
|
|
2059
|
+
return this.create({
|
|
2060
|
+
...data,
|
|
2061
|
+
message
|
|
2062
|
+
});
|
|
2063
|
+
};
|
|
2064
|
+
this.error = (message, data) => {
|
|
2065
|
+
return this.create({
|
|
2066
|
+
...data,
|
|
2067
|
+
message,
|
|
2068
|
+
type: "error"
|
|
2069
|
+
});
|
|
2070
|
+
};
|
|
2071
|
+
this.success = (message, data) => {
|
|
2072
|
+
return this.create({
|
|
2073
|
+
...data,
|
|
2074
|
+
type: "success",
|
|
2075
|
+
message
|
|
2076
|
+
});
|
|
2077
|
+
};
|
|
2078
|
+
this.info = (message, data) => {
|
|
2079
|
+
return this.create({
|
|
2080
|
+
...data,
|
|
2081
|
+
type: "info",
|
|
2082
|
+
message
|
|
2083
|
+
});
|
|
2084
|
+
};
|
|
2085
|
+
this.warning = (message, data) => {
|
|
2086
|
+
return this.create({
|
|
2087
|
+
...data,
|
|
2088
|
+
type: "warning",
|
|
2089
|
+
message
|
|
2090
|
+
});
|
|
2091
|
+
};
|
|
2092
|
+
this.loading = (message, data) => {
|
|
2093
|
+
return this.create({
|
|
2094
|
+
...data,
|
|
2095
|
+
type: "loading",
|
|
2096
|
+
message
|
|
2097
|
+
});
|
|
2098
|
+
};
|
|
2099
|
+
this.promise = (promise, data) => {
|
|
2100
|
+
if (!data) {
|
|
2101
|
+
return;
|
|
2102
|
+
}
|
|
2103
|
+
let id = void 0;
|
|
2104
|
+
if (data.loading !== void 0) {
|
|
2105
|
+
id = this.create({
|
|
2106
|
+
...data,
|
|
2107
|
+
promise,
|
|
2108
|
+
type: "loading",
|
|
2109
|
+
message: data.loading,
|
|
2110
|
+
description: typeof data.description !== "function" ? data.description : void 0
|
|
2111
|
+
});
|
|
2112
|
+
}
|
|
2113
|
+
const p = Promise.resolve(promise instanceof Function ? promise() : promise);
|
|
2114
|
+
let shouldDismiss = id !== void 0;
|
|
2115
|
+
let result;
|
|
2116
|
+
const originalPromise = p.then(async (response) => {
|
|
2117
|
+
result = [
|
|
2118
|
+
"resolve",
|
|
2119
|
+
response
|
|
2120
|
+
];
|
|
2121
|
+
const isReactElementResponse = React__default.default.isValidElement(response);
|
|
2122
|
+
if (isReactElementResponse) {
|
|
2123
|
+
shouldDismiss = false;
|
|
2124
|
+
this.create({
|
|
2125
|
+
id,
|
|
2126
|
+
type: "default",
|
|
2127
|
+
message: response
|
|
2128
|
+
});
|
|
2129
|
+
} else if (isHttpResponse(response) && !response.ok) {
|
|
2130
|
+
shouldDismiss = false;
|
|
2131
|
+
const promiseData = typeof data.error === "function" ? await data.error(`HTTP error! status: ${response.status}`) : data.error;
|
|
2132
|
+
const description = typeof data.description === "function" ? await data.description(`HTTP error! status: ${response.status}`) : data.description;
|
|
2133
|
+
const isExtendedResult = typeof promiseData === "object" && !React__default.default.isValidElement(promiseData);
|
|
2134
|
+
const toastSettings = isExtendedResult ? promiseData : {
|
|
2135
|
+
message: promiseData
|
|
2136
|
+
};
|
|
2137
|
+
this.create({
|
|
2138
|
+
id,
|
|
2139
|
+
type: "error",
|
|
2140
|
+
description,
|
|
2141
|
+
...toastSettings
|
|
2142
|
+
});
|
|
2143
|
+
} else if (response instanceof Error) {
|
|
2144
|
+
shouldDismiss = false;
|
|
2145
|
+
const promiseData = typeof data.error === "function" ? await data.error(response) : data.error;
|
|
2146
|
+
const description = typeof data.description === "function" ? await data.description(response) : data.description;
|
|
2147
|
+
const isExtendedResult = typeof promiseData === "object" && !React__default.default.isValidElement(promiseData);
|
|
2148
|
+
const toastSettings = isExtendedResult ? promiseData : {
|
|
2149
|
+
message: promiseData
|
|
2150
|
+
};
|
|
2151
|
+
this.create({
|
|
2152
|
+
id,
|
|
2153
|
+
type: "error",
|
|
2154
|
+
description,
|
|
2155
|
+
...toastSettings
|
|
2156
|
+
});
|
|
2157
|
+
} else if (data.success !== void 0) {
|
|
2158
|
+
shouldDismiss = false;
|
|
2159
|
+
const promiseData = typeof data.success === "function" ? await data.success(response) : data.success;
|
|
2160
|
+
const description = typeof data.description === "function" ? await data.description(response) : data.description;
|
|
2161
|
+
const isExtendedResult = typeof promiseData === "object" && !React__default.default.isValidElement(promiseData);
|
|
2162
|
+
const toastSettings = isExtendedResult ? promiseData : {
|
|
2163
|
+
message: promiseData
|
|
2164
|
+
};
|
|
2165
|
+
this.create({
|
|
2166
|
+
id,
|
|
2167
|
+
type: "success",
|
|
2168
|
+
description,
|
|
2169
|
+
...toastSettings
|
|
2170
|
+
});
|
|
2171
|
+
}
|
|
2172
|
+
}).catch(async (error) => {
|
|
2173
|
+
result = [
|
|
2174
|
+
"reject",
|
|
2175
|
+
error
|
|
2176
|
+
];
|
|
2177
|
+
if (data.error !== void 0) {
|
|
2178
|
+
shouldDismiss = false;
|
|
2179
|
+
const promiseData = typeof data.error === "function" ? await data.error(error) : data.error;
|
|
2180
|
+
const description = typeof data.description === "function" ? await data.description(error) : data.description;
|
|
2181
|
+
const isExtendedResult = typeof promiseData === "object" && !React__default.default.isValidElement(promiseData);
|
|
2182
|
+
const toastSettings = isExtendedResult ? promiseData : {
|
|
2183
|
+
message: promiseData
|
|
2184
|
+
};
|
|
2185
|
+
this.create({
|
|
2186
|
+
id,
|
|
2187
|
+
type: "error",
|
|
2188
|
+
description,
|
|
2189
|
+
...toastSettings
|
|
2190
|
+
});
|
|
2191
|
+
}
|
|
2192
|
+
}).finally(() => {
|
|
2193
|
+
if (shouldDismiss) {
|
|
2194
|
+
this.dismiss(id);
|
|
2195
|
+
id = void 0;
|
|
2196
|
+
}
|
|
2197
|
+
data.finally == null ? void 0 : data.finally.call(data);
|
|
2198
|
+
});
|
|
2199
|
+
const unwrap = () => new Promise((resolve, reject) => originalPromise.then(() => result[0] === "reject" ? reject(result[1]) : resolve(result[1])).catch(reject));
|
|
2200
|
+
if (typeof id !== "string" && typeof id !== "number") {
|
|
2201
|
+
return {
|
|
2202
|
+
unwrap
|
|
2203
|
+
};
|
|
2204
|
+
} else {
|
|
2205
|
+
return Object.assign(id, {
|
|
2206
|
+
unwrap
|
|
2207
|
+
});
|
|
2208
|
+
}
|
|
2209
|
+
};
|
|
2210
|
+
this.custom = (jsx2, data) => {
|
|
2211
|
+
const id = (data == null ? void 0 : data.id) || toastsCounter++;
|
|
2212
|
+
this.create({
|
|
2213
|
+
jsx: jsx2(id),
|
|
2214
|
+
id,
|
|
2215
|
+
...data
|
|
2216
|
+
});
|
|
2217
|
+
return id;
|
|
2218
|
+
};
|
|
2219
|
+
this.getActiveToasts = () => {
|
|
2220
|
+
return this.toasts.filter((toast2) => !this.dismissedToasts.has(toast2.id));
|
|
2221
|
+
};
|
|
2222
|
+
this.subscribers = [];
|
|
2223
|
+
this.toasts = [];
|
|
2224
|
+
this.dismissedToasts = /* @__PURE__ */ new Set();
|
|
2225
|
+
}
|
|
2226
|
+
};
|
|
2227
|
+
var ToastState = new Observer();
|
|
2228
|
+
var toastFunction = (message, data) => {
|
|
2229
|
+
const id = (data == null ? void 0 : data.id) || toastsCounter++;
|
|
2230
|
+
ToastState.addToast({
|
|
2231
|
+
title: message,
|
|
2232
|
+
...data,
|
|
2233
|
+
id
|
|
2234
|
+
});
|
|
2235
|
+
return id;
|
|
2236
|
+
};
|
|
2237
|
+
var isHttpResponse = (data) => {
|
|
2238
|
+
return data && typeof data === "object" && "ok" in data && typeof data.ok === "boolean" && "status" in data && typeof data.status === "number";
|
|
2239
|
+
};
|
|
2240
|
+
var basicToast = toastFunction;
|
|
2241
|
+
var getHistory = () => ToastState.toasts;
|
|
2242
|
+
var getToasts = () => ToastState.getActiveToasts();
|
|
2243
|
+
var toast = Object.assign(basicToast, {
|
|
2244
|
+
success: ToastState.success,
|
|
2245
|
+
info: ToastState.info,
|
|
2246
|
+
warning: ToastState.warning,
|
|
2247
|
+
error: ToastState.error,
|
|
2248
|
+
custom: ToastState.custom,
|
|
2249
|
+
message: ToastState.message,
|
|
2250
|
+
promise: ToastState.promise,
|
|
2251
|
+
dismiss: ToastState.dismiss,
|
|
2252
|
+
loading: ToastState.loading
|
|
2253
|
+
}, {
|
|
2254
|
+
getHistory,
|
|
2255
|
+
getToasts
|
|
2256
|
+
});
|
|
2257
|
+
__insertCSS("[data-sonner-toaster][dir=ltr],html[dir=ltr]{--toast-icon-margin-start:-3px;--toast-icon-margin-end:4px;--toast-svg-margin-start:-1px;--toast-svg-margin-end:0px;--toast-button-margin-start:auto;--toast-button-margin-end:0;--toast-close-button-start:0;--toast-close-button-end:unset;--toast-close-button-transform:translate(-35%, -35%)}[data-sonner-toaster][dir=rtl],html[dir=rtl]{--toast-icon-margin-start:4px;--toast-icon-margin-end:-3px;--toast-svg-margin-start:0px;--toast-svg-margin-end:-1px;--toast-button-margin-start:0;--toast-button-margin-end:auto;--toast-close-button-start:unset;--toast-close-button-end:0;--toast-close-button-transform:translate(35%, -35%)}[data-sonner-toaster]{position:fixed;width:var(--width);font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;--gray1:hsl(0, 0%, 99%);--gray2:hsl(0, 0%, 97.3%);--gray3:hsl(0, 0%, 95.1%);--gray4:hsl(0, 0%, 93%);--gray5:hsl(0, 0%, 90.9%);--gray6:hsl(0, 0%, 88.7%);--gray7:hsl(0, 0%, 85.8%);--gray8:hsl(0, 0%, 78%);--gray9:hsl(0, 0%, 56.1%);--gray10:hsl(0, 0%, 52.3%);--gray11:hsl(0, 0%, 43.5%);--gray12:hsl(0, 0%, 9%);--border-radius:8px;box-sizing:border-box;padding:0;margin:0;list-style:none;outline:0;z-index:999999999;transition:transform .4s ease}@media (hover:none) and (pointer:coarse){[data-sonner-toaster][data-lifted=true]{transform:none}}[data-sonner-toaster][data-x-position=right]{right:var(--offset-right)}[data-sonner-toaster][data-x-position=left]{left:var(--offset-left)}[data-sonner-toaster][data-x-position=center]{left:50%;transform:translateX(-50%)}[data-sonner-toaster][data-y-position=top]{top:var(--offset-top)}[data-sonner-toaster][data-y-position=bottom]{bottom:var(--offset-bottom)}[data-sonner-toast]{--y:translateY(100%);--lift-amount:calc(var(--lift) * var(--gap));z-index:var(--z-index);position:absolute;opacity:0;transform:var(--y);touch-action:none;transition:transform .4s,opacity .4s,height .4s,box-shadow .2s;box-sizing:border-box;outline:0;overflow-wrap:anywhere}[data-sonner-toast][data-styled=true]{padding:16px;background:var(--normal-bg);border:1px solid var(--normal-border);color:var(--normal-text);border-radius:var(--border-radius);box-shadow:0 4px 12px rgba(0,0,0,.1);width:var(--width);font-size:13px;display:flex;align-items:center;gap:6px}[data-sonner-toast]:focus-visible{box-shadow:0 4px 12px rgba(0,0,0,.1),0 0 0 2px rgba(0,0,0,.2)}[data-sonner-toast][data-y-position=top]{top:0;--y:translateY(-100%);--lift:1;--lift-amount:calc(1 * var(--gap))}[data-sonner-toast][data-y-position=bottom]{bottom:0;--y:translateY(100%);--lift:-1;--lift-amount:calc(var(--lift) * var(--gap))}[data-sonner-toast][data-styled=true] [data-description]{font-weight:400;line-height:1.4;color:#3f3f3f}[data-rich-colors=true][data-sonner-toast][data-styled=true] [data-description]{color:inherit}[data-sonner-toaster][data-sonner-theme=dark] [data-description]{color:#e8e8e8}[data-sonner-toast][data-styled=true] [data-title]{font-weight:500;line-height:1.5;color:inherit}[data-sonner-toast][data-styled=true] [data-icon]{display:flex;height:16px;width:16px;position:relative;justify-content:flex-start;align-items:center;flex-shrink:0;margin-left:var(--toast-icon-margin-start);margin-right:var(--toast-icon-margin-end)}[data-sonner-toast][data-promise=true] [data-icon]>svg{opacity:0;transform:scale(.8);transform-origin:center;animation:sonner-fade-in .3s ease forwards}[data-sonner-toast][data-styled=true] [data-icon]>*{flex-shrink:0}[data-sonner-toast][data-styled=true] [data-icon] svg{margin-left:var(--toast-svg-margin-start);margin-right:var(--toast-svg-margin-end)}[data-sonner-toast][data-styled=true] [data-content]{display:flex;flex-direction:column;gap:2px}[data-sonner-toast][data-styled=true] [data-button]{border-radius:4px;padding-left:8px;padding-right:8px;height:24px;font-size:12px;color:var(--normal-bg);background:var(--normal-text);margin-left:var(--toast-button-margin-start);margin-right:var(--toast-button-margin-end);border:none;font-weight:500;cursor:pointer;outline:0;display:flex;align-items:center;flex-shrink:0;transition:opacity .4s,box-shadow .2s}[data-sonner-toast][data-styled=true] [data-button]:focus-visible{box-shadow:0 0 0 2px rgba(0,0,0,.4)}[data-sonner-toast][data-styled=true] [data-button]:first-of-type{margin-left:var(--toast-button-margin-start);margin-right:var(--toast-button-margin-end)}[data-sonner-toast][data-styled=true] [data-cancel]{color:var(--normal-text);background:rgba(0,0,0,.08)}[data-sonner-toaster][data-sonner-theme=dark] [data-sonner-toast][data-styled=true] [data-cancel]{background:rgba(255,255,255,.3)}[data-sonner-toast][data-styled=true] [data-close-button]{position:absolute;left:var(--toast-close-button-start);right:var(--toast-close-button-end);top:0;height:20px;width:20px;display:flex;justify-content:center;align-items:center;padding:0;color:var(--gray12);background:var(--normal-bg);border:1px solid var(--gray4);transform:var(--toast-close-button-transform);border-radius:50%;cursor:pointer;z-index:1;transition:opacity .1s,background .2s,border-color .2s}[data-sonner-toast][data-styled=true] [data-close-button]:focus-visible{box-shadow:0 4px 12px rgba(0,0,0,.1),0 0 0 2px rgba(0,0,0,.2)}[data-sonner-toast][data-styled=true] [data-disabled=true]{cursor:not-allowed}[data-sonner-toast][data-styled=true]:hover [data-close-button]:hover{background:var(--gray2);border-color:var(--gray5)}[data-sonner-toast][data-swiping=true]::before{content:'';position:absolute;left:-100%;right:-100%;height:100%;z-index:-1}[data-sonner-toast][data-y-position=top][data-swiping=true]::before{bottom:50%;transform:scaleY(3) translateY(50%)}[data-sonner-toast][data-y-position=bottom][data-swiping=true]::before{top:50%;transform:scaleY(3) translateY(-50%)}[data-sonner-toast][data-swiping=false][data-removed=true]::before{content:'';position:absolute;inset:0;transform:scaleY(2)}[data-sonner-toast][data-expanded=true]::after{content:'';position:absolute;left:0;height:calc(var(--gap) + 1px);bottom:100%;width:100%}[data-sonner-toast][data-mounted=true]{--y:translateY(0);opacity:1}[data-sonner-toast][data-expanded=false][data-front=false]{--scale:var(--toasts-before) * 0.05 + 1;--y:translateY(calc(var(--lift-amount) * var(--toasts-before))) scale(calc(-1 * var(--scale)));height:var(--front-toast-height)}[data-sonner-toast]>*{transition:opacity .4s}[data-sonner-toast][data-x-position=right]{right:0}[data-sonner-toast][data-x-position=left]{left:0}[data-sonner-toast][data-expanded=false][data-front=false][data-styled=true]>*{opacity:0}[data-sonner-toast][data-visible=false]{opacity:0;pointer-events:none}[data-sonner-toast][data-mounted=true][data-expanded=true]{--y:translateY(calc(var(--lift) * var(--offset)));height:var(--initial-height)}[data-sonner-toast][data-removed=true][data-front=true][data-swipe-out=false]{--y:translateY(calc(var(--lift) * -100%));opacity:0}[data-sonner-toast][data-removed=true][data-front=false][data-swipe-out=false][data-expanded=true]{--y:translateY(calc(var(--lift) * var(--offset) + var(--lift) * -100%));opacity:0}[data-sonner-toast][data-removed=true][data-front=false][data-swipe-out=false][data-expanded=false]{--y:translateY(40%);opacity:0;transition:transform .5s,opacity .2s}[data-sonner-toast][data-removed=true][data-front=false]::before{height:calc(var(--initial-height) + 20%)}[data-sonner-toast][data-swiping=true]{transform:var(--y) translateY(var(--swipe-amount-y,0)) translateX(var(--swipe-amount-x,0));transition:none}[data-sonner-toast][data-swiped=true]{user-select:none}[data-sonner-toast][data-swipe-out=true][data-y-position=bottom],[data-sonner-toast][data-swipe-out=true][data-y-position=top]{animation-duration:.2s;animation-timing-function:ease-out;animation-fill-mode:forwards}[data-sonner-toast][data-swipe-out=true][data-swipe-direction=left]{animation-name:swipe-out-left}[data-sonner-toast][data-swipe-out=true][data-swipe-direction=right]{animation-name:swipe-out-right}[data-sonner-toast][data-swipe-out=true][data-swipe-direction=up]{animation-name:swipe-out-up}[data-sonner-toast][data-swipe-out=true][data-swipe-direction=down]{animation-name:swipe-out-down}@keyframes swipe-out-left{from{transform:var(--y) translateX(var(--swipe-amount-x));opacity:1}to{transform:var(--y) translateX(calc(var(--swipe-amount-x) - 100%));opacity:0}}@keyframes swipe-out-right{from{transform:var(--y) translateX(var(--swipe-amount-x));opacity:1}to{transform:var(--y) translateX(calc(var(--swipe-amount-x) + 100%));opacity:0}}@keyframes swipe-out-up{from{transform:var(--y) translateY(var(--swipe-amount-y));opacity:1}to{transform:var(--y) translateY(calc(var(--swipe-amount-y) - 100%));opacity:0}}@keyframes swipe-out-down{from{transform:var(--y) translateY(var(--swipe-amount-y));opacity:1}to{transform:var(--y) translateY(calc(var(--swipe-amount-y) + 100%));opacity:0}}@media (max-width:600px){[data-sonner-toaster]{position:fixed;right:var(--mobile-offset-right);left:var(--mobile-offset-left);width:100%}[data-sonner-toaster][dir=rtl]{left:calc(var(--mobile-offset-left) * -1)}[data-sonner-toaster] [data-sonner-toast]{left:0;right:0;width:calc(100% - var(--mobile-offset-left) * 2)}[data-sonner-toaster][data-x-position=left]{left:var(--mobile-offset-left)}[data-sonner-toaster][data-y-position=bottom]{bottom:var(--mobile-offset-bottom)}[data-sonner-toaster][data-y-position=top]{top:var(--mobile-offset-top)}[data-sonner-toaster][data-x-position=center]{left:var(--mobile-offset-left);right:var(--mobile-offset-right);transform:none}}[data-sonner-toaster][data-sonner-theme=light]{--normal-bg:#fff;--normal-border:var(--gray4);--normal-text:var(--gray12);--success-bg:hsl(143, 85%, 96%);--success-border:hsl(145, 92%, 87%);--success-text:hsl(140, 100%, 27%);--info-bg:hsl(208, 100%, 97%);--info-border:hsl(221, 91%, 93%);--info-text:hsl(210, 92%, 45%);--warning-bg:hsl(49, 100%, 97%);--warning-border:hsl(49, 91%, 84%);--warning-text:hsl(31, 92%, 45%);--error-bg:hsl(359, 100%, 97%);--error-border:hsl(359, 100%, 94%);--error-text:hsl(360, 100%, 45%)}[data-sonner-toaster][data-sonner-theme=light] [data-sonner-toast][data-invert=true]{--normal-bg:#000;--normal-border:hsl(0, 0%, 20%);--normal-text:var(--gray1)}[data-sonner-toaster][data-sonner-theme=dark] [data-sonner-toast][data-invert=true]{--normal-bg:#fff;--normal-border:var(--gray3);--normal-text:var(--gray12)}[data-sonner-toaster][data-sonner-theme=dark]{--normal-bg:#000;--normal-bg-hover:hsl(0, 0%, 12%);--normal-border:hsl(0, 0%, 20%);--normal-border-hover:hsl(0, 0%, 25%);--normal-text:var(--gray1);--success-bg:hsl(150, 100%, 6%);--success-border:hsl(147, 100%, 12%);--success-text:hsl(150, 86%, 65%);--info-bg:hsl(215, 100%, 6%);--info-border:hsl(223, 43%, 17%);--info-text:hsl(216, 87%, 65%);--warning-bg:hsl(64, 100%, 6%);--warning-border:hsl(60, 100%, 9%);--warning-text:hsl(46, 87%, 65%);--error-bg:hsl(358, 76%, 10%);--error-border:hsl(357, 89%, 16%);--error-text:hsl(358, 100%, 81%)}[data-sonner-toaster][data-sonner-theme=dark] [data-sonner-toast] [data-close-button]{background:var(--normal-bg);border-color:var(--normal-border);color:var(--normal-text)}[data-sonner-toaster][data-sonner-theme=dark] [data-sonner-toast] [data-close-button]:hover{background:var(--normal-bg-hover);border-color:var(--normal-border-hover)}[data-rich-colors=true][data-sonner-toast][data-type=success]{background:var(--success-bg);border-color:var(--success-border);color:var(--success-text)}[data-rich-colors=true][data-sonner-toast][data-type=success] [data-close-button]{background:var(--success-bg);border-color:var(--success-border);color:var(--success-text)}[data-rich-colors=true][data-sonner-toast][data-type=info]{background:var(--info-bg);border-color:var(--info-border);color:var(--info-text)}[data-rich-colors=true][data-sonner-toast][data-type=info] [data-close-button]{background:var(--info-bg);border-color:var(--info-border);color:var(--info-text)}[data-rich-colors=true][data-sonner-toast][data-type=warning]{background:var(--warning-bg);border-color:var(--warning-border);color:var(--warning-text)}[data-rich-colors=true][data-sonner-toast][data-type=warning] [data-close-button]{background:var(--warning-bg);border-color:var(--warning-border);color:var(--warning-text)}[data-rich-colors=true][data-sonner-toast][data-type=error]{background:var(--error-bg);border-color:var(--error-border);color:var(--error-text)}[data-rich-colors=true][data-sonner-toast][data-type=error] [data-close-button]{background:var(--error-bg);border-color:var(--error-border);color:var(--error-text)}.sonner-loading-wrapper{--size:16px;height:var(--size);width:var(--size);position:absolute;inset:0;z-index:10}.sonner-loading-wrapper[data-visible=false]{transform-origin:center;animation:sonner-fade-out .2s ease forwards}.sonner-spinner{position:relative;top:50%;left:50%;height:var(--size);width:var(--size)}.sonner-loading-bar{animation:sonner-spin 1.2s linear infinite;background:var(--gray11);border-radius:6px;height:8%;left:-10%;position:absolute;top:-3.9%;width:24%}.sonner-loading-bar:first-child{animation-delay:-1.2s;transform:rotate(.0001deg) translate(146%)}.sonner-loading-bar:nth-child(2){animation-delay:-1.1s;transform:rotate(30deg) translate(146%)}.sonner-loading-bar:nth-child(3){animation-delay:-1s;transform:rotate(60deg) translate(146%)}.sonner-loading-bar:nth-child(4){animation-delay:-.9s;transform:rotate(90deg) translate(146%)}.sonner-loading-bar:nth-child(5){animation-delay:-.8s;transform:rotate(120deg) translate(146%)}.sonner-loading-bar:nth-child(6){animation-delay:-.7s;transform:rotate(150deg) translate(146%)}.sonner-loading-bar:nth-child(7){animation-delay:-.6s;transform:rotate(180deg) translate(146%)}.sonner-loading-bar:nth-child(8){animation-delay:-.5s;transform:rotate(210deg) translate(146%)}.sonner-loading-bar:nth-child(9){animation-delay:-.4s;transform:rotate(240deg) translate(146%)}.sonner-loading-bar:nth-child(10){animation-delay:-.3s;transform:rotate(270deg) translate(146%)}.sonner-loading-bar:nth-child(11){animation-delay:-.2s;transform:rotate(300deg) translate(146%)}.sonner-loading-bar:nth-child(12){animation-delay:-.1s;transform:rotate(330deg) translate(146%)}@keyframes sonner-fade-in{0%{opacity:0;transform:scale(.8)}100%{opacity:1;transform:scale(1)}}@keyframes sonner-fade-out{0%{opacity:1;transform:scale(1)}100%{opacity:0;transform:scale(.8)}}@keyframes sonner-spin{0%{opacity:1}100%{opacity:.15}}@media (prefers-reduced-motion){.sonner-loading-bar,[data-sonner-toast],[data-sonner-toast]>*{transition:none!important;animation:none!important}}.sonner-loader{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);transform-origin:center;transition:opacity .2s,transform .2s}.sonner-loader[data-visible=false]{opacity:0;transform:scale(.8) translate(-50%,-50%)}");
|
|
2258
|
+
function isAction(action) {
|
|
2259
|
+
return action.label !== void 0;
|
|
2260
|
+
}
|
|
2261
|
+
var VISIBLE_TOASTS_AMOUNT = 3;
|
|
2262
|
+
var VIEWPORT_OFFSET = "24px";
|
|
2263
|
+
var MOBILE_VIEWPORT_OFFSET = "16px";
|
|
2264
|
+
var TOAST_LIFETIME = 4e3;
|
|
2265
|
+
var TOAST_WIDTH = 356;
|
|
2266
|
+
var GAP = 14;
|
|
2267
|
+
var SWIPE_THRESHOLD = 45;
|
|
2268
|
+
var TIME_BEFORE_UNMOUNT = 200;
|
|
2269
|
+
function cn(...classes) {
|
|
2270
|
+
return classes.filter(Boolean).join(" ");
|
|
2271
|
+
}
|
|
2272
|
+
function getDefaultSwipeDirections(position) {
|
|
2273
|
+
const [y, x] = position.split("-");
|
|
2274
|
+
const directions = [];
|
|
2275
|
+
if (y) {
|
|
2276
|
+
directions.push(y);
|
|
2277
|
+
}
|
|
2278
|
+
if (x) {
|
|
2279
|
+
directions.push(x);
|
|
2280
|
+
}
|
|
2281
|
+
return directions;
|
|
2282
|
+
}
|
|
2283
|
+
var Toast = (props) => {
|
|
2284
|
+
var _toast_classNames, _toast_classNames1, _toast_classNames2, _toast_classNames3, _toast_classNames4, _toast_classNames5, _toast_classNames6, _toast_classNames7, _toast_classNames8;
|
|
2285
|
+
const { invert: ToasterInvert, toast: toast2, unstyled, interacting, setHeights, visibleToasts, heights, index, toasts, expanded, removeToast, defaultRichColors, closeButton: closeButtonFromToaster, style, cancelButtonStyle, actionButtonStyle, className = "", descriptionClassName = "", duration: durationFromToaster, position, gap, expandByDefault, classNames, icons, closeButtonAriaLabel = "Close toast" } = props;
|
|
2286
|
+
const [swipeDirection, setSwipeDirection] = React__default.default.useState(null);
|
|
2287
|
+
const [swipeOutDirection, setSwipeOutDirection] = React__default.default.useState(null);
|
|
2288
|
+
const [mounted, setMounted] = React__default.default.useState(false);
|
|
2289
|
+
const [removed, setRemoved] = React__default.default.useState(false);
|
|
2290
|
+
const [swiping, setSwiping] = React__default.default.useState(false);
|
|
2291
|
+
const [swipeOut, setSwipeOut] = React__default.default.useState(false);
|
|
2292
|
+
const [isSwiped, setIsSwiped] = React__default.default.useState(false);
|
|
2293
|
+
const [offsetBeforeRemove, setOffsetBeforeRemove] = React__default.default.useState(0);
|
|
2294
|
+
const [initialHeight, setInitialHeight] = React__default.default.useState(0);
|
|
2295
|
+
const remainingTime = React__default.default.useRef(toast2.duration || durationFromToaster || TOAST_LIFETIME);
|
|
2296
|
+
const dragStartTime = React__default.default.useRef(null);
|
|
2297
|
+
const toastRef = React__default.default.useRef(null);
|
|
2298
|
+
const isFront = index === 0;
|
|
2299
|
+
const isVisible = index + 1 <= visibleToasts;
|
|
2300
|
+
const toastType = toast2.type;
|
|
2301
|
+
const dismissible = toast2.dismissible !== false;
|
|
2302
|
+
const toastClassname = toast2.className || "";
|
|
2303
|
+
const toastDescriptionClassname = toast2.descriptionClassName || "";
|
|
2304
|
+
const heightIndex = React__default.default.useMemo(() => heights.findIndex((height) => height.toastId === toast2.id) || 0, [
|
|
2305
|
+
heights,
|
|
2306
|
+
toast2.id
|
|
2307
|
+
]);
|
|
2308
|
+
const closeButton = React__default.default.useMemo(() => {
|
|
2309
|
+
var _toast_closeButton;
|
|
2310
|
+
return (_toast_closeButton = toast2.closeButton) != null ? _toast_closeButton : closeButtonFromToaster;
|
|
2311
|
+
}, [
|
|
2312
|
+
toast2.closeButton,
|
|
2313
|
+
closeButtonFromToaster
|
|
2314
|
+
]);
|
|
2315
|
+
const duration = React__default.default.useMemo(() => toast2.duration || durationFromToaster || TOAST_LIFETIME, [
|
|
2316
|
+
toast2.duration,
|
|
2317
|
+
durationFromToaster
|
|
2318
|
+
]);
|
|
2319
|
+
const closeTimerStartTimeRef = React__default.default.useRef(0);
|
|
2320
|
+
const offset = React__default.default.useRef(0);
|
|
2321
|
+
const lastCloseTimerStartTimeRef = React__default.default.useRef(0);
|
|
2322
|
+
const pointerStartRef = React__default.default.useRef(null);
|
|
2323
|
+
const [y, x] = position.split("-");
|
|
2324
|
+
const toastsHeightBefore = React__default.default.useMemo(() => {
|
|
2325
|
+
return heights.reduce((prev, curr, reducerIndex) => {
|
|
2326
|
+
if (reducerIndex >= heightIndex) {
|
|
2327
|
+
return prev;
|
|
2328
|
+
}
|
|
2329
|
+
return prev + curr.height;
|
|
2330
|
+
}, 0);
|
|
2331
|
+
}, [
|
|
2332
|
+
heights,
|
|
2333
|
+
heightIndex
|
|
2334
|
+
]);
|
|
2335
|
+
const isDocumentHidden = useIsDocumentHidden();
|
|
2336
|
+
const invert = toast2.invert || ToasterInvert;
|
|
2337
|
+
const disabled = toastType === "loading";
|
|
2338
|
+
offset.current = React__default.default.useMemo(() => heightIndex * gap + toastsHeightBefore, [
|
|
2339
|
+
heightIndex,
|
|
2340
|
+
toastsHeightBefore
|
|
2341
|
+
]);
|
|
2342
|
+
React__default.default.useEffect(() => {
|
|
2343
|
+
remainingTime.current = duration;
|
|
2344
|
+
}, [
|
|
2345
|
+
duration
|
|
2346
|
+
]);
|
|
2347
|
+
React__default.default.useEffect(() => {
|
|
2348
|
+
setMounted(true);
|
|
2349
|
+
}, []);
|
|
2350
|
+
React__default.default.useEffect(() => {
|
|
2351
|
+
const toastNode = toastRef.current;
|
|
2352
|
+
if (toastNode) {
|
|
2353
|
+
const height = toastNode.getBoundingClientRect().height;
|
|
2354
|
+
setInitialHeight(height);
|
|
2355
|
+
setHeights((h) => [
|
|
2356
|
+
{
|
|
2357
|
+
toastId: toast2.id,
|
|
2358
|
+
height,
|
|
2359
|
+
position: toast2.position
|
|
2360
|
+
},
|
|
2361
|
+
...h
|
|
2362
|
+
]);
|
|
2363
|
+
return () => setHeights((h) => h.filter((height2) => height2.toastId !== toast2.id));
|
|
2364
|
+
}
|
|
2365
|
+
}, [
|
|
2366
|
+
setHeights,
|
|
2367
|
+
toast2.id
|
|
2368
|
+
]);
|
|
2369
|
+
React__default.default.useLayoutEffect(() => {
|
|
2370
|
+
if (!mounted) return;
|
|
2371
|
+
const toastNode = toastRef.current;
|
|
2372
|
+
const originalHeight = toastNode.style.height;
|
|
2373
|
+
toastNode.style.height = "auto";
|
|
2374
|
+
const newHeight = toastNode.getBoundingClientRect().height;
|
|
2375
|
+
toastNode.style.height = originalHeight;
|
|
2376
|
+
setInitialHeight(newHeight);
|
|
2377
|
+
setHeights((heights2) => {
|
|
2378
|
+
const alreadyExists = heights2.find((height) => height.toastId === toast2.id);
|
|
2379
|
+
if (!alreadyExists) {
|
|
2380
|
+
return [
|
|
2381
|
+
{
|
|
2382
|
+
toastId: toast2.id,
|
|
2383
|
+
height: newHeight,
|
|
2384
|
+
position: toast2.position
|
|
2385
|
+
},
|
|
2386
|
+
...heights2
|
|
2387
|
+
];
|
|
2388
|
+
} else {
|
|
2389
|
+
return heights2.map((height) => height.toastId === toast2.id ? {
|
|
2390
|
+
...height,
|
|
2391
|
+
height: newHeight
|
|
2392
|
+
} : height);
|
|
2393
|
+
}
|
|
2394
|
+
});
|
|
2395
|
+
}, [
|
|
2396
|
+
mounted,
|
|
2397
|
+
toast2.title,
|
|
2398
|
+
toast2.description,
|
|
2399
|
+
setHeights,
|
|
2400
|
+
toast2.id,
|
|
2401
|
+
toast2.jsx,
|
|
2402
|
+
toast2.action,
|
|
2403
|
+
toast2.cancel
|
|
2404
|
+
]);
|
|
2405
|
+
const deleteToast = React__default.default.useCallback(() => {
|
|
2406
|
+
setRemoved(true);
|
|
2407
|
+
setOffsetBeforeRemove(offset.current);
|
|
2408
|
+
setHeights((h) => h.filter((height) => height.toastId !== toast2.id));
|
|
2409
|
+
setTimeout(() => {
|
|
2410
|
+
removeToast(toast2);
|
|
2411
|
+
}, TIME_BEFORE_UNMOUNT);
|
|
2412
|
+
}, [
|
|
2413
|
+
toast2,
|
|
2414
|
+
removeToast,
|
|
2415
|
+
setHeights,
|
|
2416
|
+
offset
|
|
2417
|
+
]);
|
|
2418
|
+
React__default.default.useEffect(() => {
|
|
2419
|
+
if (toast2.promise && toastType === "loading" || toast2.duration === Infinity || toast2.type === "loading") return;
|
|
2420
|
+
let timeoutId;
|
|
2421
|
+
const pauseTimer = () => {
|
|
2422
|
+
if (lastCloseTimerStartTimeRef.current < closeTimerStartTimeRef.current) {
|
|
2423
|
+
const elapsedTime = (/* @__PURE__ */ new Date()).getTime() - closeTimerStartTimeRef.current;
|
|
2424
|
+
remainingTime.current = remainingTime.current - elapsedTime;
|
|
2425
|
+
}
|
|
2426
|
+
lastCloseTimerStartTimeRef.current = (/* @__PURE__ */ new Date()).getTime();
|
|
2427
|
+
};
|
|
2428
|
+
const startTimer = () => {
|
|
2429
|
+
if (remainingTime.current === Infinity) return;
|
|
2430
|
+
closeTimerStartTimeRef.current = (/* @__PURE__ */ new Date()).getTime();
|
|
2431
|
+
timeoutId = setTimeout(() => {
|
|
2432
|
+
toast2.onAutoClose == null ? void 0 : toast2.onAutoClose.call(toast2, toast2);
|
|
2433
|
+
deleteToast();
|
|
2434
|
+
}, remainingTime.current);
|
|
2435
|
+
};
|
|
2436
|
+
if (expanded || interacting || isDocumentHidden) {
|
|
2437
|
+
pauseTimer();
|
|
1574
2438
|
} else {
|
|
1575
|
-
|
|
2439
|
+
startTimer();
|
|
2440
|
+
}
|
|
2441
|
+
return () => clearTimeout(timeoutId);
|
|
2442
|
+
}, [
|
|
2443
|
+
expanded,
|
|
2444
|
+
interacting,
|
|
2445
|
+
toast2,
|
|
2446
|
+
toastType,
|
|
2447
|
+
isDocumentHidden,
|
|
2448
|
+
deleteToast
|
|
2449
|
+
]);
|
|
2450
|
+
React__default.default.useEffect(() => {
|
|
2451
|
+
if (toast2.delete) {
|
|
2452
|
+
deleteToast();
|
|
2453
|
+
toast2.onDismiss == null ? void 0 : toast2.onDismiss.call(toast2, toast2);
|
|
2454
|
+
}
|
|
2455
|
+
}, [
|
|
2456
|
+
deleteToast,
|
|
2457
|
+
toast2.delete
|
|
2458
|
+
]);
|
|
2459
|
+
function getLoadingIcon() {
|
|
2460
|
+
var _toast_classNames9;
|
|
2461
|
+
if (icons == null ? void 0 : icons.loading) {
|
|
2462
|
+
var _toast_classNames12;
|
|
2463
|
+
return /* @__PURE__ */ React__default.default.createElement("div", {
|
|
2464
|
+
className: cn(classNames == null ? void 0 : classNames.loader, toast2 == null ? void 0 : (_toast_classNames12 = toast2.classNames) == null ? void 0 : _toast_classNames12.loader, "sonner-loader"),
|
|
2465
|
+
"data-visible": toastType === "loading"
|
|
2466
|
+
}, icons.loading);
|
|
2467
|
+
}
|
|
2468
|
+
return /* @__PURE__ */ React__default.default.createElement(Loader, {
|
|
2469
|
+
className: cn(classNames == null ? void 0 : classNames.loader, toast2 == null ? void 0 : (_toast_classNames9 = toast2.classNames) == null ? void 0 : _toast_classNames9.loader),
|
|
2470
|
+
visible: toastType === "loading"
|
|
2471
|
+
});
|
|
2472
|
+
}
|
|
2473
|
+
const icon = toast2.icon || (icons == null ? void 0 : icons[toastType]) || getAsset(toastType);
|
|
2474
|
+
var _toast_richColors, _icons_close;
|
|
2475
|
+
return /* @__PURE__ */ React__default.default.createElement("li", {
|
|
2476
|
+
tabIndex: 0,
|
|
2477
|
+
ref: toastRef,
|
|
2478
|
+
className: cn(className, toastClassname, classNames == null ? void 0 : classNames.toast, toast2 == null ? void 0 : (_toast_classNames = toast2.classNames) == null ? void 0 : _toast_classNames.toast, classNames == null ? void 0 : classNames.default, classNames == null ? void 0 : classNames[toastType], toast2 == null ? void 0 : (_toast_classNames1 = toast2.classNames) == null ? void 0 : _toast_classNames1[toastType]),
|
|
2479
|
+
"data-sonner-toast": "",
|
|
2480
|
+
"data-rich-colors": (_toast_richColors = toast2.richColors) != null ? _toast_richColors : defaultRichColors,
|
|
2481
|
+
"data-styled": !Boolean(toast2.jsx || toast2.unstyled || unstyled),
|
|
2482
|
+
"data-mounted": mounted,
|
|
2483
|
+
"data-promise": Boolean(toast2.promise),
|
|
2484
|
+
"data-swiped": isSwiped,
|
|
2485
|
+
"data-removed": removed,
|
|
2486
|
+
"data-visible": isVisible,
|
|
2487
|
+
"data-y-position": y,
|
|
2488
|
+
"data-x-position": x,
|
|
2489
|
+
"data-index": index,
|
|
2490
|
+
"data-front": isFront,
|
|
2491
|
+
"data-swiping": swiping,
|
|
2492
|
+
"data-dismissible": dismissible,
|
|
2493
|
+
"data-type": toastType,
|
|
2494
|
+
"data-invert": invert,
|
|
2495
|
+
"data-swipe-out": swipeOut,
|
|
2496
|
+
"data-swipe-direction": swipeOutDirection,
|
|
2497
|
+
"data-expanded": Boolean(expanded || expandByDefault && mounted),
|
|
2498
|
+
"data-testid": toast2.testId,
|
|
2499
|
+
style: {
|
|
2500
|
+
"--index": index,
|
|
2501
|
+
"--toasts-before": index,
|
|
2502
|
+
"--z-index": toasts.length - index,
|
|
2503
|
+
"--offset": `${removed ? offsetBeforeRemove : offset.current}px`,
|
|
2504
|
+
"--initial-height": expandByDefault ? "auto" : `${initialHeight}px`,
|
|
2505
|
+
...style,
|
|
2506
|
+
...toast2.style
|
|
2507
|
+
},
|
|
2508
|
+
onDragEnd: () => {
|
|
2509
|
+
setSwiping(false);
|
|
2510
|
+
setSwipeDirection(null);
|
|
2511
|
+
pointerStartRef.current = null;
|
|
2512
|
+
},
|
|
2513
|
+
onPointerDown: (event) => {
|
|
2514
|
+
if (event.button === 2) return;
|
|
2515
|
+
if (disabled || !dismissible) return;
|
|
2516
|
+
dragStartTime.current = /* @__PURE__ */ new Date();
|
|
2517
|
+
setOffsetBeforeRemove(offset.current);
|
|
2518
|
+
event.target.setPointerCapture(event.pointerId);
|
|
2519
|
+
if (event.target.tagName === "BUTTON") return;
|
|
2520
|
+
setSwiping(true);
|
|
2521
|
+
pointerStartRef.current = {
|
|
2522
|
+
x: event.clientX,
|
|
2523
|
+
y: event.clientY
|
|
2524
|
+
};
|
|
2525
|
+
},
|
|
2526
|
+
onPointerUp: () => {
|
|
2527
|
+
var _toastRef_current, _toastRef_current1, _dragStartTime_current;
|
|
2528
|
+
if (swipeOut || !dismissible) return;
|
|
2529
|
+
pointerStartRef.current = null;
|
|
2530
|
+
const swipeAmountX = Number(((_toastRef_current = toastRef.current) == null ? void 0 : _toastRef_current.style.getPropertyValue("--swipe-amount-x").replace("px", "")) || 0);
|
|
2531
|
+
const swipeAmountY = Number(((_toastRef_current1 = toastRef.current) == null ? void 0 : _toastRef_current1.style.getPropertyValue("--swipe-amount-y").replace("px", "")) || 0);
|
|
2532
|
+
const timeTaken = (/* @__PURE__ */ new Date()).getTime() - ((_dragStartTime_current = dragStartTime.current) == null ? void 0 : _dragStartTime_current.getTime());
|
|
2533
|
+
const swipeAmount = swipeDirection === "x" ? swipeAmountX : swipeAmountY;
|
|
2534
|
+
const velocity = Math.abs(swipeAmount) / timeTaken;
|
|
2535
|
+
if (Math.abs(swipeAmount) >= SWIPE_THRESHOLD || velocity > 0.11) {
|
|
2536
|
+
setOffsetBeforeRemove(offset.current);
|
|
2537
|
+
toast2.onDismiss == null ? void 0 : toast2.onDismiss.call(toast2, toast2);
|
|
2538
|
+
if (swipeDirection === "x") {
|
|
2539
|
+
setSwipeOutDirection(swipeAmountX > 0 ? "right" : "left");
|
|
2540
|
+
} else {
|
|
2541
|
+
setSwipeOutDirection(swipeAmountY > 0 ? "down" : "up");
|
|
2542
|
+
}
|
|
2543
|
+
deleteToast();
|
|
2544
|
+
setSwipeOut(true);
|
|
2545
|
+
return;
|
|
2546
|
+
} else {
|
|
2547
|
+
var _toastRef_current2, _toastRef_current3;
|
|
2548
|
+
(_toastRef_current2 = toastRef.current) == null ? void 0 : _toastRef_current2.style.setProperty("--swipe-amount-x", `0px`);
|
|
2549
|
+
(_toastRef_current3 = toastRef.current) == null ? void 0 : _toastRef_current3.style.setProperty("--swipe-amount-y", `0px`);
|
|
2550
|
+
}
|
|
2551
|
+
setIsSwiped(false);
|
|
2552
|
+
setSwiping(false);
|
|
2553
|
+
setSwipeDirection(null);
|
|
2554
|
+
},
|
|
2555
|
+
onPointerMove: (event) => {
|
|
2556
|
+
var _window_getSelection, _toastRef_current, _toastRef_current1;
|
|
2557
|
+
if (!pointerStartRef.current || !dismissible) return;
|
|
2558
|
+
const isHighlighted = ((_window_getSelection = window.getSelection()) == null ? void 0 : _window_getSelection.toString().length) > 0;
|
|
2559
|
+
if (isHighlighted) return;
|
|
2560
|
+
const yDelta = event.clientY - pointerStartRef.current.y;
|
|
2561
|
+
const xDelta = event.clientX - pointerStartRef.current.x;
|
|
2562
|
+
var _props_swipeDirections;
|
|
2563
|
+
const swipeDirections = (_props_swipeDirections = props.swipeDirections) != null ? _props_swipeDirections : getDefaultSwipeDirections(position);
|
|
2564
|
+
if (!swipeDirection && (Math.abs(xDelta) > 1 || Math.abs(yDelta) > 1)) {
|
|
2565
|
+
setSwipeDirection(Math.abs(xDelta) > Math.abs(yDelta) ? "x" : "y");
|
|
2566
|
+
}
|
|
2567
|
+
let swipeAmount = {
|
|
2568
|
+
x: 0,
|
|
2569
|
+
y: 0
|
|
2570
|
+
};
|
|
2571
|
+
const getDampening = (delta) => {
|
|
2572
|
+
const factor = Math.abs(delta) / 20;
|
|
2573
|
+
return 1 / (1.5 + factor);
|
|
2574
|
+
};
|
|
2575
|
+
if (swipeDirection === "y") {
|
|
2576
|
+
if (swipeDirections.includes("top") || swipeDirections.includes("bottom")) {
|
|
2577
|
+
if (swipeDirections.includes("top") && yDelta < 0 || swipeDirections.includes("bottom") && yDelta > 0) {
|
|
2578
|
+
swipeAmount.y = yDelta;
|
|
2579
|
+
} else {
|
|
2580
|
+
const dampenedDelta = yDelta * getDampening(yDelta);
|
|
2581
|
+
swipeAmount.y = Math.abs(dampenedDelta) < Math.abs(yDelta) ? dampenedDelta : yDelta;
|
|
2582
|
+
}
|
|
2583
|
+
}
|
|
2584
|
+
} else if (swipeDirection === "x") {
|
|
2585
|
+
if (swipeDirections.includes("left") || swipeDirections.includes("right")) {
|
|
2586
|
+
if (swipeDirections.includes("left") && xDelta < 0 || swipeDirections.includes("right") && xDelta > 0) {
|
|
2587
|
+
swipeAmount.x = xDelta;
|
|
2588
|
+
} else {
|
|
2589
|
+
const dampenedDelta = xDelta * getDampening(xDelta);
|
|
2590
|
+
swipeAmount.x = Math.abs(dampenedDelta) < Math.abs(xDelta) ? dampenedDelta : xDelta;
|
|
2591
|
+
}
|
|
2592
|
+
}
|
|
2593
|
+
}
|
|
2594
|
+
if (Math.abs(swipeAmount.x) > 0 || Math.abs(swipeAmount.y) > 0) {
|
|
2595
|
+
setIsSwiped(true);
|
|
2596
|
+
}
|
|
2597
|
+
(_toastRef_current = toastRef.current) == null ? void 0 : _toastRef_current.style.setProperty("--swipe-amount-x", `${swipeAmount.x}px`);
|
|
2598
|
+
(_toastRef_current1 = toastRef.current) == null ? void 0 : _toastRef_current1.style.setProperty("--swipe-amount-y", `${swipeAmount.y}px`);
|
|
2599
|
+
}
|
|
2600
|
+
}, closeButton && !toast2.jsx && toastType !== "loading" ? /* @__PURE__ */ React__default.default.createElement("button", {
|
|
2601
|
+
"aria-label": closeButtonAriaLabel,
|
|
2602
|
+
"data-disabled": disabled,
|
|
2603
|
+
"data-close-button": true,
|
|
2604
|
+
onClick: disabled || !dismissible ? () => {
|
|
2605
|
+
} : () => {
|
|
2606
|
+
deleteToast();
|
|
2607
|
+
toast2.onDismiss == null ? void 0 : toast2.onDismiss.call(toast2, toast2);
|
|
2608
|
+
},
|
|
2609
|
+
className: cn(classNames == null ? void 0 : classNames.closeButton, toast2 == null ? void 0 : (_toast_classNames2 = toast2.classNames) == null ? void 0 : _toast_classNames2.closeButton)
|
|
2610
|
+
}, (_icons_close = icons == null ? void 0 : icons.close) != null ? _icons_close : CloseIcon) : null, (toastType || toast2.icon || toast2.promise) && toast2.icon !== null && ((icons == null ? void 0 : icons[toastType]) !== null || toast2.icon) ? /* @__PURE__ */ React__default.default.createElement("div", {
|
|
2611
|
+
"data-icon": "",
|
|
2612
|
+
className: cn(classNames == null ? void 0 : classNames.icon, toast2 == null ? void 0 : (_toast_classNames3 = toast2.classNames) == null ? void 0 : _toast_classNames3.icon)
|
|
2613
|
+
}, toast2.promise || toast2.type === "loading" && !toast2.icon ? toast2.icon || getLoadingIcon() : null, toast2.type !== "loading" ? icon : null) : null, /* @__PURE__ */ React__default.default.createElement("div", {
|
|
2614
|
+
"data-content": "",
|
|
2615
|
+
className: cn(classNames == null ? void 0 : classNames.content, toast2 == null ? void 0 : (_toast_classNames4 = toast2.classNames) == null ? void 0 : _toast_classNames4.content)
|
|
2616
|
+
}, /* @__PURE__ */ React__default.default.createElement("div", {
|
|
2617
|
+
"data-title": "",
|
|
2618
|
+
className: cn(classNames == null ? void 0 : classNames.title, toast2 == null ? void 0 : (_toast_classNames5 = toast2.classNames) == null ? void 0 : _toast_classNames5.title)
|
|
2619
|
+
}, toast2.jsx ? toast2.jsx : typeof toast2.title === "function" ? toast2.title() : toast2.title), toast2.description ? /* @__PURE__ */ React__default.default.createElement("div", {
|
|
2620
|
+
"data-description": "",
|
|
2621
|
+
className: cn(descriptionClassName, toastDescriptionClassname, classNames == null ? void 0 : classNames.description, toast2 == null ? void 0 : (_toast_classNames6 = toast2.classNames) == null ? void 0 : _toast_classNames6.description)
|
|
2622
|
+
}, typeof toast2.description === "function" ? toast2.description() : toast2.description) : null), /* @__PURE__ */ React__default.default.isValidElement(toast2.cancel) ? toast2.cancel : toast2.cancel && isAction(toast2.cancel) ? /* @__PURE__ */ React__default.default.createElement("button", {
|
|
2623
|
+
"data-button": true,
|
|
2624
|
+
"data-cancel": true,
|
|
2625
|
+
style: toast2.cancelButtonStyle || cancelButtonStyle,
|
|
2626
|
+
onClick: (event) => {
|
|
2627
|
+
if (!isAction(toast2.cancel)) return;
|
|
2628
|
+
if (!dismissible) return;
|
|
2629
|
+
toast2.cancel.onClick == null ? void 0 : toast2.cancel.onClick.call(toast2.cancel, event);
|
|
2630
|
+
deleteToast();
|
|
2631
|
+
},
|
|
2632
|
+
className: cn(classNames == null ? void 0 : classNames.cancelButton, toast2 == null ? void 0 : (_toast_classNames7 = toast2.classNames) == null ? void 0 : _toast_classNames7.cancelButton)
|
|
2633
|
+
}, toast2.cancel.label) : null, /* @__PURE__ */ React__default.default.isValidElement(toast2.action) ? toast2.action : toast2.action && isAction(toast2.action) ? /* @__PURE__ */ React__default.default.createElement("button", {
|
|
2634
|
+
"data-button": true,
|
|
2635
|
+
"data-action": true,
|
|
2636
|
+
style: toast2.actionButtonStyle || actionButtonStyle,
|
|
2637
|
+
onClick: (event) => {
|
|
2638
|
+
if (!isAction(toast2.action)) return;
|
|
2639
|
+
toast2.action.onClick == null ? void 0 : toast2.action.onClick.call(toast2.action, event);
|
|
2640
|
+
if (event.defaultPrevented) return;
|
|
2641
|
+
deleteToast();
|
|
2642
|
+
},
|
|
2643
|
+
className: cn(classNames == null ? void 0 : classNames.actionButton, toast2 == null ? void 0 : (_toast_classNames8 = toast2.classNames) == null ? void 0 : _toast_classNames8.actionButton)
|
|
2644
|
+
}, toast2.action.label) : null);
|
|
2645
|
+
};
|
|
2646
|
+
function getDocumentDirection() {
|
|
2647
|
+
if (typeof window === "undefined") return "ltr";
|
|
2648
|
+
if (typeof document === "undefined") return "ltr";
|
|
2649
|
+
const dirAttribute = document.documentElement.getAttribute("dir");
|
|
2650
|
+
if (dirAttribute === "auto" || !dirAttribute) {
|
|
2651
|
+
return window.getComputedStyle(document.documentElement).direction;
|
|
2652
|
+
}
|
|
2653
|
+
return dirAttribute;
|
|
2654
|
+
}
|
|
2655
|
+
function assignOffset(defaultOffset, mobileOffset) {
|
|
2656
|
+
const styles = {};
|
|
2657
|
+
[
|
|
2658
|
+
defaultOffset,
|
|
2659
|
+
mobileOffset
|
|
2660
|
+
].forEach((offset, index) => {
|
|
2661
|
+
const isMobile = index === 1;
|
|
2662
|
+
const prefix = isMobile ? "--mobile-offset" : "--offset";
|
|
2663
|
+
const defaultValue = isMobile ? MOBILE_VIEWPORT_OFFSET : VIEWPORT_OFFSET;
|
|
2664
|
+
function assignAll(offset2) {
|
|
2665
|
+
[
|
|
2666
|
+
"top",
|
|
2667
|
+
"right",
|
|
2668
|
+
"bottom",
|
|
2669
|
+
"left"
|
|
2670
|
+
].forEach((key) => {
|
|
2671
|
+
styles[`${prefix}-${key}`] = typeof offset2 === "number" ? `${offset2}px` : offset2;
|
|
2672
|
+
});
|
|
2673
|
+
}
|
|
2674
|
+
if (typeof offset === "number" || typeof offset === "string") {
|
|
2675
|
+
assignAll(offset);
|
|
2676
|
+
} else if (typeof offset === "object") {
|
|
2677
|
+
[
|
|
2678
|
+
"top",
|
|
2679
|
+
"right",
|
|
2680
|
+
"bottom",
|
|
2681
|
+
"left"
|
|
2682
|
+
].forEach((key) => {
|
|
2683
|
+
if (offset[key] === void 0) {
|
|
2684
|
+
styles[`${prefix}-${key}`] = defaultValue;
|
|
2685
|
+
} else {
|
|
2686
|
+
styles[`${prefix}-${key}`] = typeof offset[key] === "number" ? `${offset[key]}px` : offset[key];
|
|
2687
|
+
}
|
|
2688
|
+
});
|
|
2689
|
+
} else {
|
|
2690
|
+
assignAll(defaultValue);
|
|
1576
2691
|
}
|
|
1577
|
-
};
|
|
1578
|
-
return trigger(start, {
|
|
1579
|
-
asChild,
|
|
1580
|
-
children: children ?? "Add to my plan",
|
|
1581
|
-
className,
|
|
1582
|
-
disabled: checkout.isLoading
|
|
1583
2692
|
});
|
|
1584
|
-
|
|
2693
|
+
return styles;
|
|
2694
|
+
}
|
|
2695
|
+
var Toaster = /* @__PURE__ */ React__default.default.forwardRef(function Toaster2(props, ref) {
|
|
2696
|
+
const { id, invert, position = "bottom-right", hotkey = [
|
|
2697
|
+
"altKey",
|
|
2698
|
+
"KeyT"
|
|
2699
|
+
], expand, closeButton, className, offset, mobileOffset, theme = "light", richColors, duration, style, visibleToasts = VISIBLE_TOASTS_AMOUNT, toastOptions, dir = getDocumentDirection(), gap = GAP, icons, containerAriaLabel = "Notifications" } = props;
|
|
2700
|
+
const [toasts, setToasts] = React__default.default.useState([]);
|
|
2701
|
+
const filteredToasts = React__default.default.useMemo(() => {
|
|
2702
|
+
if (id) {
|
|
2703
|
+
return toasts.filter((toast2) => toast2.toasterId === id);
|
|
2704
|
+
}
|
|
2705
|
+
return toasts.filter((toast2) => !toast2.toasterId);
|
|
2706
|
+
}, [
|
|
2707
|
+
toasts,
|
|
2708
|
+
id
|
|
2709
|
+
]);
|
|
2710
|
+
const possiblePositions = React__default.default.useMemo(() => {
|
|
2711
|
+
return Array.from(new Set([
|
|
2712
|
+
position
|
|
2713
|
+
].concat(filteredToasts.filter((toast2) => toast2.position).map((toast2) => toast2.position))));
|
|
2714
|
+
}, [
|
|
2715
|
+
filteredToasts,
|
|
2716
|
+
position
|
|
2717
|
+
]);
|
|
2718
|
+
const [heights, setHeights] = React__default.default.useState([]);
|
|
2719
|
+
const [expanded, setExpanded] = React__default.default.useState(false);
|
|
2720
|
+
const [interacting, setInteracting] = React__default.default.useState(false);
|
|
2721
|
+
const [actualTheme, setActualTheme] = React__default.default.useState(theme !== "system" ? theme : typeof window !== "undefined" ? window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light" : "light");
|
|
2722
|
+
const listRef = React__default.default.useRef(null);
|
|
2723
|
+
const hotkeyLabel = hotkey.join("+").replace(/Key/g, "").replace(/Digit/g, "");
|
|
2724
|
+
const lastFocusedElementRef = React__default.default.useRef(null);
|
|
2725
|
+
const isFocusWithinRef = React__default.default.useRef(false);
|
|
2726
|
+
const removeToast = React__default.default.useCallback((toastToRemove) => {
|
|
2727
|
+
setToasts((toasts2) => {
|
|
2728
|
+
var _toasts_find;
|
|
2729
|
+
if (!((_toasts_find = toasts2.find((toast2) => toast2.id === toastToRemove.id)) == null ? void 0 : _toasts_find.delete)) {
|
|
2730
|
+
ToastState.dismiss(toastToRemove.id);
|
|
2731
|
+
}
|
|
2732
|
+
return toasts2.filter(({ id: id2 }) => id2 !== toastToRemove.id);
|
|
2733
|
+
});
|
|
2734
|
+
}, []);
|
|
2735
|
+
React__default.default.useEffect(() => {
|
|
2736
|
+
return ToastState.subscribe((toast2) => {
|
|
2737
|
+
if (toast2.dismiss) {
|
|
2738
|
+
requestAnimationFrame(() => {
|
|
2739
|
+
setToasts((toasts2) => toasts2.map((t) => t.id === toast2.id ? {
|
|
2740
|
+
...t,
|
|
2741
|
+
delete: true
|
|
2742
|
+
} : t));
|
|
2743
|
+
});
|
|
2744
|
+
return;
|
|
2745
|
+
}
|
|
2746
|
+
setTimeout(() => {
|
|
2747
|
+
ReactDOM__default.default.flushSync(() => {
|
|
2748
|
+
setToasts((toasts2) => {
|
|
2749
|
+
const indexOfExistingToast = toasts2.findIndex((t) => t.id === toast2.id);
|
|
2750
|
+
if (indexOfExistingToast !== -1) {
|
|
2751
|
+
return [
|
|
2752
|
+
...toasts2.slice(0, indexOfExistingToast),
|
|
2753
|
+
{
|
|
2754
|
+
...toasts2[indexOfExistingToast],
|
|
2755
|
+
...toast2
|
|
2756
|
+
},
|
|
2757
|
+
...toasts2.slice(indexOfExistingToast + 1)
|
|
2758
|
+
];
|
|
2759
|
+
}
|
|
2760
|
+
return [
|
|
2761
|
+
toast2,
|
|
2762
|
+
...toasts2
|
|
2763
|
+
];
|
|
2764
|
+
});
|
|
2765
|
+
});
|
|
2766
|
+
});
|
|
2767
|
+
});
|
|
2768
|
+
}, [
|
|
2769
|
+
toasts
|
|
2770
|
+
]);
|
|
2771
|
+
React__default.default.useEffect(() => {
|
|
2772
|
+
if (theme !== "system") {
|
|
2773
|
+
setActualTheme(theme);
|
|
2774
|
+
return;
|
|
2775
|
+
}
|
|
2776
|
+
if (theme === "system") {
|
|
2777
|
+
if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) {
|
|
2778
|
+
setActualTheme("dark");
|
|
2779
|
+
} else {
|
|
2780
|
+
setActualTheme("light");
|
|
2781
|
+
}
|
|
2782
|
+
}
|
|
2783
|
+
if (typeof window === "undefined") return;
|
|
2784
|
+
const darkMediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
2785
|
+
try {
|
|
2786
|
+
darkMediaQuery.addEventListener("change", ({ matches }) => {
|
|
2787
|
+
if (matches) {
|
|
2788
|
+
setActualTheme("dark");
|
|
2789
|
+
} else {
|
|
2790
|
+
setActualTheme("light");
|
|
2791
|
+
}
|
|
2792
|
+
});
|
|
2793
|
+
} catch (error) {
|
|
2794
|
+
darkMediaQuery.addListener(({ matches }) => {
|
|
2795
|
+
try {
|
|
2796
|
+
if (matches) {
|
|
2797
|
+
setActualTheme("dark");
|
|
2798
|
+
} else {
|
|
2799
|
+
setActualTheme("light");
|
|
2800
|
+
}
|
|
2801
|
+
} catch (e) {
|
|
2802
|
+
console.error(e);
|
|
2803
|
+
}
|
|
2804
|
+
});
|
|
2805
|
+
}
|
|
2806
|
+
}, [
|
|
2807
|
+
theme
|
|
2808
|
+
]);
|
|
2809
|
+
React__default.default.useEffect(() => {
|
|
2810
|
+
if (toasts.length <= 1) {
|
|
2811
|
+
setExpanded(false);
|
|
2812
|
+
}
|
|
2813
|
+
}, [
|
|
2814
|
+
toasts
|
|
2815
|
+
]);
|
|
2816
|
+
React__default.default.useEffect(() => {
|
|
2817
|
+
const handleKeyDown = (event) => {
|
|
2818
|
+
var _listRef_current;
|
|
2819
|
+
const isHotkeyPressed = hotkey.every((key) => event[key] || event.code === key);
|
|
2820
|
+
if (isHotkeyPressed) {
|
|
2821
|
+
var _listRef_current1;
|
|
2822
|
+
setExpanded(true);
|
|
2823
|
+
(_listRef_current1 = listRef.current) == null ? void 0 : _listRef_current1.focus();
|
|
2824
|
+
}
|
|
2825
|
+
if (event.code === "Escape" && (document.activeElement === listRef.current || ((_listRef_current = listRef.current) == null ? void 0 : _listRef_current.contains(document.activeElement)))) {
|
|
2826
|
+
setExpanded(false);
|
|
2827
|
+
}
|
|
2828
|
+
};
|
|
2829
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
2830
|
+
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
2831
|
+
}, [
|
|
2832
|
+
hotkey
|
|
2833
|
+
]);
|
|
2834
|
+
React__default.default.useEffect(() => {
|
|
2835
|
+
if (listRef.current) {
|
|
2836
|
+
return () => {
|
|
2837
|
+
if (lastFocusedElementRef.current) {
|
|
2838
|
+
lastFocusedElementRef.current.focus({
|
|
2839
|
+
preventScroll: true
|
|
2840
|
+
});
|
|
2841
|
+
lastFocusedElementRef.current = null;
|
|
2842
|
+
isFocusWithinRef.current = false;
|
|
2843
|
+
}
|
|
2844
|
+
};
|
|
2845
|
+
}
|
|
2846
|
+
}, [
|
|
2847
|
+
listRef.current
|
|
2848
|
+
]);
|
|
2849
|
+
return (
|
|
2850
|
+
// Remove item from normal navigation flow, only available via hotkey
|
|
2851
|
+
/* @__PURE__ */ React__default.default.createElement("section", {
|
|
2852
|
+
ref,
|
|
2853
|
+
"aria-label": `${containerAriaLabel} ${hotkeyLabel}`,
|
|
2854
|
+
tabIndex: -1,
|
|
2855
|
+
"aria-live": "polite",
|
|
2856
|
+
"aria-relevant": "additions text",
|
|
2857
|
+
"aria-atomic": "false",
|
|
2858
|
+
suppressHydrationWarning: true
|
|
2859
|
+
}, possiblePositions.map((position2, index) => {
|
|
2860
|
+
var _heights_;
|
|
2861
|
+
const [y, x] = position2.split("-");
|
|
2862
|
+
if (!filteredToasts.length) return null;
|
|
2863
|
+
return /* @__PURE__ */ React__default.default.createElement("ol", {
|
|
2864
|
+
key: position2,
|
|
2865
|
+
dir: dir === "auto" ? getDocumentDirection() : dir,
|
|
2866
|
+
tabIndex: -1,
|
|
2867
|
+
ref: listRef,
|
|
2868
|
+
className,
|
|
2869
|
+
"data-sonner-toaster": true,
|
|
2870
|
+
"data-sonner-theme": actualTheme,
|
|
2871
|
+
"data-y-position": y,
|
|
2872
|
+
"data-x-position": x,
|
|
2873
|
+
style: {
|
|
2874
|
+
"--front-toast-height": `${((_heights_ = heights[0]) == null ? void 0 : _heights_.height) || 0}px`,
|
|
2875
|
+
"--width": `${TOAST_WIDTH}px`,
|
|
2876
|
+
"--gap": `${gap}px`,
|
|
2877
|
+
...style,
|
|
2878
|
+
...assignOffset(offset, mobileOffset)
|
|
2879
|
+
},
|
|
2880
|
+
onBlur: (event) => {
|
|
2881
|
+
if (isFocusWithinRef.current && !event.currentTarget.contains(event.relatedTarget)) {
|
|
2882
|
+
isFocusWithinRef.current = false;
|
|
2883
|
+
if (lastFocusedElementRef.current) {
|
|
2884
|
+
lastFocusedElementRef.current.focus({
|
|
2885
|
+
preventScroll: true
|
|
2886
|
+
});
|
|
2887
|
+
lastFocusedElementRef.current = null;
|
|
2888
|
+
}
|
|
2889
|
+
}
|
|
2890
|
+
},
|
|
2891
|
+
onFocus: (event) => {
|
|
2892
|
+
const isNotDismissible = event.target instanceof HTMLElement && event.target.dataset.dismissible === "false";
|
|
2893
|
+
if (isNotDismissible) return;
|
|
2894
|
+
if (!isFocusWithinRef.current) {
|
|
2895
|
+
isFocusWithinRef.current = true;
|
|
2896
|
+
lastFocusedElementRef.current = event.relatedTarget;
|
|
2897
|
+
}
|
|
2898
|
+
},
|
|
2899
|
+
onMouseEnter: () => setExpanded(true),
|
|
2900
|
+
onMouseMove: () => setExpanded(true),
|
|
2901
|
+
onMouseLeave: () => {
|
|
2902
|
+
if (!interacting) {
|
|
2903
|
+
setExpanded(false);
|
|
2904
|
+
}
|
|
2905
|
+
},
|
|
2906
|
+
onDragEnd: () => setExpanded(false),
|
|
2907
|
+
onPointerDown: (event) => {
|
|
2908
|
+
const isNotDismissible = event.target instanceof HTMLElement && event.target.dataset.dismissible === "false";
|
|
2909
|
+
if (isNotDismissible) return;
|
|
2910
|
+
setInteracting(true);
|
|
2911
|
+
},
|
|
2912
|
+
onPointerUp: () => setInteracting(false)
|
|
2913
|
+
}, filteredToasts.filter((toast2) => !toast2.position && index === 0 || toast2.position === position2).map((toast2, index2) => {
|
|
2914
|
+
var _toastOptions_duration, _toastOptions_closeButton;
|
|
2915
|
+
return /* @__PURE__ */ React__default.default.createElement(Toast, {
|
|
2916
|
+
key: toast2.id,
|
|
2917
|
+
icons,
|
|
2918
|
+
index: index2,
|
|
2919
|
+
toast: toast2,
|
|
2920
|
+
defaultRichColors: richColors,
|
|
2921
|
+
duration: (_toastOptions_duration = toastOptions == null ? void 0 : toastOptions.duration) != null ? _toastOptions_duration : duration,
|
|
2922
|
+
className: toastOptions == null ? void 0 : toastOptions.className,
|
|
2923
|
+
descriptionClassName: toastOptions == null ? void 0 : toastOptions.descriptionClassName,
|
|
2924
|
+
invert,
|
|
2925
|
+
visibleToasts,
|
|
2926
|
+
closeButton: (_toastOptions_closeButton = toastOptions == null ? void 0 : toastOptions.closeButton) != null ? _toastOptions_closeButton : closeButton,
|
|
2927
|
+
interacting,
|
|
2928
|
+
position: position2,
|
|
2929
|
+
style: toastOptions == null ? void 0 : toastOptions.style,
|
|
2930
|
+
unstyled: toastOptions == null ? void 0 : toastOptions.unstyled,
|
|
2931
|
+
classNames: toastOptions == null ? void 0 : toastOptions.classNames,
|
|
2932
|
+
cancelButtonStyle: toastOptions == null ? void 0 : toastOptions.cancelButtonStyle,
|
|
2933
|
+
actionButtonStyle: toastOptions == null ? void 0 : toastOptions.actionButtonStyle,
|
|
2934
|
+
closeButtonAriaLabel: toastOptions == null ? void 0 : toastOptions.closeButtonAriaLabel,
|
|
2935
|
+
removeToast,
|
|
2936
|
+
toasts: filteredToasts.filter((t) => t.position == toast2.position),
|
|
2937
|
+
heights: heights.filter((h) => h.position == toast2.position),
|
|
2938
|
+
setHeights,
|
|
2939
|
+
expandByDefault: expand,
|
|
2940
|
+
gap,
|
|
2941
|
+
expanded,
|
|
2942
|
+
swipeDirections: props.swipeDirections
|
|
2943
|
+
});
|
|
2944
|
+
}));
|
|
2945
|
+
}))
|
|
2946
|
+
);
|
|
2947
|
+
});
|
|
1585
2948
|
function FunnelToaster(props = {}) {
|
|
1586
|
-
return
|
|
2949
|
+
return React.createElement(Toaster, { position: "top-center", ...props });
|
|
1587
2950
|
}
|
|
1588
2951
|
function renderToaster(toaster) {
|
|
1589
2952
|
if (toaster === false) return null;
|
|
1590
|
-
return
|
|
2953
|
+
return /* @__PURE__ */ jsxRuntime.jsx(FunnelToaster, { ...toaster === true ? {} : toaster });
|
|
1591
2954
|
}
|
|
1592
2955
|
|
|
1593
2956
|
// src/state/namespaces.ts
|
|
@@ -1611,7 +2974,8 @@ var Namespace = class {
|
|
|
1611
2974
|
const out = {};
|
|
1612
2975
|
const all = this.store.getState();
|
|
1613
2976
|
for (const key in all) {
|
|
1614
|
-
if (key.startsWith(this.dot))
|
|
2977
|
+
if (key.startsWith(this.dot))
|
|
2978
|
+
out[key.slice(this.dot.length)] = all[key];
|
|
1615
2979
|
}
|
|
1616
2980
|
return out;
|
|
1617
2981
|
}
|
|
@@ -1653,13 +3017,14 @@ function createFunnel(store, context, tracker, experiments = []) {
|
|
|
1653
3017
|
})
|
|
1654
3018
|
};
|
|
1655
3019
|
}
|
|
1656
|
-
var FunnelCtx =
|
|
3020
|
+
var FunnelCtx = React.createContext(null);
|
|
1657
3021
|
function FunnelProvider({
|
|
1658
3022
|
config,
|
|
1659
3023
|
sessionValues,
|
|
3024
|
+
deferSessionValues = false,
|
|
1660
3025
|
tracker,
|
|
1661
3026
|
context: contextOpts,
|
|
1662
|
-
|
|
3027
|
+
offerings,
|
|
1663
3028
|
experiments,
|
|
1664
3029
|
checkout,
|
|
1665
3030
|
messages,
|
|
@@ -1669,10 +3034,15 @@ function FunnelProvider({
|
|
|
1669
3034
|
children
|
|
1670
3035
|
}) {
|
|
1671
3036
|
const resolvedTracker = tracker ?? FALLBACK_TRACKER;
|
|
1672
|
-
const [value] =
|
|
3037
|
+
const [value] = React.useState(() => {
|
|
1673
3038
|
const s = store ?? createFunnelStore(
|
|
1674
3039
|
{ responses: config?.responses, data: config?.data },
|
|
1675
|
-
|
|
3040
|
+
// FLASH-FREE HYDRATION: with deferSessionValues the initial render is seeded with
|
|
3041
|
+
// DEFAULTS ONLY, so the client's first render is byte-identical to the build-time
|
|
3042
|
+
// DEFAULT-state prerender it hydrates against (clean match). The visitor's saved
|
|
3043
|
+
// answers arrive as a post-hydration setMany below. Without the flag, sessionValues
|
|
3044
|
+
// seed the store at construction (SSR + non-prerendered paths).
|
|
3045
|
+
deferSessionValues ? void 0 : sessionValues
|
|
1676
3046
|
);
|
|
1677
3047
|
const ctx = buildContext({
|
|
1678
3048
|
funnelId: config?.id,
|
|
@@ -1686,65 +3056,90 @@ function FunnelProvider({
|
|
|
1686
3056
|
getVisitorId: () => ctx.identity.visitorId
|
|
1687
3057
|
});
|
|
1688
3058
|
const busTracker = withBus(resolvedTracker, bus);
|
|
3059
|
+
const emailForCheckout = () => {
|
|
3060
|
+
const value2 = s.get("user.email");
|
|
3061
|
+
return typeof value2 === "string" && /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(value2) ? value2 : void 0;
|
|
3062
|
+
};
|
|
3063
|
+
const catalog = chunkRKPRMTSU_cjs.buildCatalog(offerings ?? []);
|
|
1689
3064
|
return {
|
|
1690
3065
|
funnel: createFunnel(s, ctx, busTracker, experiments ?? []),
|
|
1691
3066
|
store: s,
|
|
1692
3067
|
context: ctx,
|
|
1693
|
-
catalog
|
|
1694
|
-
driver
|
|
3068
|
+
catalog,
|
|
3069
|
+
// Slot → catalog-key resolution wraps the email-enriching driver, so the wire charges the real
|
|
3070
|
+
// catalog key while pages + analytics keep the durable SLOT identity (req.product).
|
|
3071
|
+
driver: driverWithCatalog(
|
|
3072
|
+
driverWithEmail(
|
|
3073
|
+
checkout ?? createMockDriver(),
|
|
3074
|
+
emailForCheckout
|
|
3075
|
+
),
|
|
3076
|
+
catalog
|
|
3077
|
+
),
|
|
1695
3078
|
tracker: busTracker,
|
|
1696
3079
|
bus
|
|
1697
3080
|
};
|
|
1698
3081
|
});
|
|
1699
|
-
|
|
1700
|
-
|
|
3082
|
+
React.useEffect(() => attachBus(value.bus), [value.bus]);
|
|
3083
|
+
React.useEffect(() => {
|
|
3084
|
+
if (!deferSessionValues || !sessionValues) return;
|
|
3085
|
+
const updates = {};
|
|
3086
|
+
for (const [key, v] of Object.entries(sessionValues))
|
|
3087
|
+
if (v !== void 0) updates[key] = v;
|
|
3088
|
+
if (Object.keys(updates).length > 0) value.store.setMany(updates);
|
|
3089
|
+
}, []);
|
|
3090
|
+
React.useEffect(() => {
|
|
1701
3091
|
value.tracker.setAcquisition?.(buildAcquisition(value.context));
|
|
1702
3092
|
}, [value]);
|
|
1703
|
-
const tree =
|
|
3093
|
+
const tree = React.createElement(TrackerProvider, {
|
|
1704
3094
|
tracker: value.tracker,
|
|
1705
|
-
children:
|
|
3095
|
+
children: React.createElement(CheckoutDriverProvider, {
|
|
1706
3096
|
driver: value.driver,
|
|
1707
|
-
children:
|
|
3097
|
+
children: React.createElement(CatalogProvider, {
|
|
1708
3098
|
catalog: value.catalog,
|
|
1709
|
-
children:
|
|
3099
|
+
children: React.createElement(FunnelCtx.Provider, {
|
|
1710
3100
|
value,
|
|
1711
|
-
children:
|
|
3101
|
+
children: React.createElement(LocaleProvider, {
|
|
1712
3102
|
config: config?.locales,
|
|
1713
3103
|
catalog: messages,
|
|
1714
3104
|
detected: value.context.locale.locale,
|
|
1715
3105
|
override: localeOverride,
|
|
1716
|
-
children:
|
|
3106
|
+
children: React.createElement(ModalRuntime, { children })
|
|
1717
3107
|
})
|
|
1718
3108
|
})
|
|
1719
3109
|
})
|
|
1720
3110
|
})
|
|
1721
3111
|
});
|
|
1722
|
-
return
|
|
3112
|
+
return React.createElement(React.Fragment, null, tree, renderToaster(toaster));
|
|
1723
3113
|
}
|
|
1724
3114
|
var FALLBACK_TRACKER = createConsoleTracker({ silent: true });
|
|
1725
3115
|
function useFunnelCtx() {
|
|
1726
|
-
const ctx =
|
|
3116
|
+
const ctx = React.useContext(FunnelCtx);
|
|
1727
3117
|
if (!ctx) {
|
|
1728
|
-
throw new Error(
|
|
3118
|
+
throw new Error(
|
|
3119
|
+
"useFunnel/useResponse/\u2026 must be used inside <FunnelProvider>"
|
|
3120
|
+
);
|
|
1729
3121
|
}
|
|
1730
3122
|
return ctx;
|
|
1731
3123
|
}
|
|
1732
3124
|
function useFunnel() {
|
|
1733
3125
|
return useFunnelCtx().funnel;
|
|
1734
3126
|
}
|
|
3127
|
+
function useBusInternal() {
|
|
3128
|
+
return React.useContext(FunnelCtx)?.bus ?? null;
|
|
3129
|
+
}
|
|
1735
3130
|
function useStoreKey(store, key) {
|
|
1736
|
-
const subscribe =
|
|
3131
|
+
const subscribe = React.useCallback(
|
|
1737
3132
|
(cb) => store.subscribe(cb, { keys: [key] }),
|
|
1738
3133
|
[store, key]
|
|
1739
3134
|
);
|
|
1740
|
-
const getSnapshot =
|
|
1741
|
-
return
|
|
3135
|
+
const getSnapshot = React.useCallback(() => store.get(key), [store, key]);
|
|
3136
|
+
return React.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
1742
3137
|
}
|
|
1743
3138
|
function makeFieldHook(ns) {
|
|
1744
3139
|
return function useNamespaceField(key) {
|
|
1745
3140
|
const { funnel, store } = useFunnelCtx();
|
|
1746
3141
|
const value = useStoreKey(store, `${NAMESPACE_PREFIX[ns]}.${key}`);
|
|
1747
|
-
const set =
|
|
3142
|
+
const set = React.useCallback(
|
|
1748
3143
|
(v) => funnel[ns].set({ [key]: v }),
|
|
1749
3144
|
[funnel, key]
|
|
1750
3145
|
);
|
|
@@ -1760,10 +3155,12 @@ function useField(path) {
|
|
|
1760
3155
|
const key = path.slice(dot + 1);
|
|
1761
3156
|
const { funnel, store } = useFunnelCtx();
|
|
1762
3157
|
if (!(ns in NAMESPACE_PREFIX)) {
|
|
1763
|
-
throw new Error(
|
|
3158
|
+
throw new Error(
|
|
3159
|
+
`useField: "${path}" is not a writable namespace (user/responses/data)`
|
|
3160
|
+
);
|
|
1764
3161
|
}
|
|
1765
3162
|
const value = useStoreKey(store, `${NAMESPACE_PREFIX[ns]}.${key}`);
|
|
1766
|
-
const set =
|
|
3163
|
+
const set = React.useCallback(
|
|
1767
3164
|
(v) => funnel[ns].set({ [key]: v }),
|
|
1768
3165
|
[funnel, ns, key]
|
|
1769
3166
|
);
|
|
@@ -1789,11 +3186,404 @@ function useSystem() {
|
|
|
1789
3186
|
}
|
|
1790
3187
|
function useContextValue(path) {
|
|
1791
3188
|
const ctx = useContextObject();
|
|
1792
|
-
return
|
|
1793
|
-
() => path.split(".").reduce(
|
|
3189
|
+
return React.useMemo(
|
|
3190
|
+
() => path.split(".").reduce(
|
|
3191
|
+
(acc, k) => acc?.[k],
|
|
3192
|
+
ctx
|
|
3193
|
+
),
|
|
1794
3194
|
[ctx, path]
|
|
1795
3195
|
);
|
|
1796
3196
|
}
|
|
3197
|
+
|
|
3198
|
+
// src/tracking/snapshotPersistence.ts
|
|
3199
|
+
function createSnapshotPersistence(opts) {
|
|
3200
|
+
const baseDelay = opts.baseDelayMs ?? 1e3;
|
|
3201
|
+
const maxDelay = opts.maxDelayMs ?? 3e4;
|
|
3202
|
+
const setT = opts.timers?.setTimeout ?? ((fn, ms) => setTimeout(fn, ms));
|
|
3203
|
+
const clearT = opts.timers?.clearTimeout ?? ((h) => clearTimeout(h));
|
|
3204
|
+
const rand = opts.random ?? Math.random;
|
|
3205
|
+
let pending = null;
|
|
3206
|
+
let seq = 0;
|
|
3207
|
+
let pendingSeq = 0;
|
|
3208
|
+
let inFlight = false;
|
|
3209
|
+
let attempt = 0;
|
|
3210
|
+
let retryTimer = null;
|
|
3211
|
+
let disposed = false;
|
|
3212
|
+
let fatalSeq = -1;
|
|
3213
|
+
const clearRetry = () => {
|
|
3214
|
+
if (retryTimer !== null) {
|
|
3215
|
+
clearT(retryTimer);
|
|
3216
|
+
retryTimer = null;
|
|
3217
|
+
}
|
|
3218
|
+
};
|
|
3219
|
+
const buildBody = (snapshot, s) => JSON.stringify({ sessionId: opts.getSessionId(), userData: snapshot, seq: s });
|
|
3220
|
+
const trySend = () => {
|
|
3221
|
+
if (disposed || inFlight || pending === null) return;
|
|
3222
|
+
if (retryTimer !== null) return;
|
|
3223
|
+
const sessionId = opts.getSessionId();
|
|
3224
|
+
if (!sessionId) return;
|
|
3225
|
+
if (typeof navigator !== "undefined" && navigator.onLine === false) return;
|
|
3226
|
+
if (pendingSeq === fatalSeq) return;
|
|
3227
|
+
const snapshot = pending;
|
|
3228
|
+
const sentSeq = pendingSeq;
|
|
3229
|
+
const body = buildBody(snapshot, sentSeq);
|
|
3230
|
+
inFlight = true;
|
|
3231
|
+
void fetch(opts.url, {
|
|
3232
|
+
method: "POST",
|
|
3233
|
+
headers: { "content-type": "application/json" },
|
|
3234
|
+
body,
|
|
3235
|
+
keepalive: true
|
|
3236
|
+
}).then((res) => {
|
|
3237
|
+
inFlight = false;
|
|
3238
|
+
if (disposed) return;
|
|
3239
|
+
if (res.ok) {
|
|
3240
|
+
attempt = 0;
|
|
3241
|
+
if (pendingSeq === sentSeq) pending = null;
|
|
3242
|
+
else trySend();
|
|
3243
|
+
return;
|
|
3244
|
+
}
|
|
3245
|
+
if (res.status >= 400 && res.status < 500) {
|
|
3246
|
+
if (process.env.NODE_ENV !== "production") {
|
|
3247
|
+
console.warn(
|
|
3248
|
+
`[appfunnel] snapshot persist rejected ${res.status} (dropping snapshot; check session/payload)`
|
|
3249
|
+
);
|
|
3250
|
+
}
|
|
3251
|
+
fatalSeq = sentSeq;
|
|
3252
|
+
if (pendingSeq === sentSeq) pending = null;
|
|
3253
|
+
else trySend();
|
|
3254
|
+
return;
|
|
3255
|
+
}
|
|
3256
|
+
scheduleRetry();
|
|
3257
|
+
}).catch(() => {
|
|
3258
|
+
inFlight = false;
|
|
3259
|
+
if (disposed) return;
|
|
3260
|
+
scheduleRetry();
|
|
3261
|
+
});
|
|
3262
|
+
};
|
|
3263
|
+
const scheduleRetry = () => {
|
|
3264
|
+
if (disposed || pending === null) return;
|
|
3265
|
+
const exp = Math.min(maxDelay, baseDelay * 2 ** attempt);
|
|
3266
|
+
const delay2 = Math.round(exp * (0.5 + rand() * 0.5));
|
|
3267
|
+
attempt++;
|
|
3268
|
+
clearRetry();
|
|
3269
|
+
retryTimer = setT(() => {
|
|
3270
|
+
retryTimer = null;
|
|
3271
|
+
trySend();
|
|
3272
|
+
}, delay2);
|
|
3273
|
+
};
|
|
3274
|
+
const push = (snapshot) => {
|
|
3275
|
+
if (disposed) return;
|
|
3276
|
+
pending = snapshot;
|
|
3277
|
+
pendingSeq = ++seq;
|
|
3278
|
+
if (fatalSeq !== -1 && pendingSeq !== fatalSeq) fatalSeq = -1;
|
|
3279
|
+
if (retryTimer === null) trySend();
|
|
3280
|
+
};
|
|
3281
|
+
const flushBeacon = () => {
|
|
3282
|
+
if (disposed || pending === null) return;
|
|
3283
|
+
const sessionId = opts.getSessionId();
|
|
3284
|
+
if (!sessionId) return;
|
|
3285
|
+
const body = buildBody(pending, pendingSeq);
|
|
3286
|
+
let delivered = false;
|
|
3287
|
+
if (typeof navigator !== "undefined" && typeof navigator.sendBeacon === "function") {
|
|
3288
|
+
try {
|
|
3289
|
+
delivered = navigator.sendBeacon(
|
|
3290
|
+
opts.url,
|
|
3291
|
+
new Blob([body], { type: "application/json" })
|
|
3292
|
+
);
|
|
3293
|
+
} catch {
|
|
3294
|
+
delivered = false;
|
|
3295
|
+
}
|
|
3296
|
+
}
|
|
3297
|
+
if (!delivered) {
|
|
3298
|
+
try {
|
|
3299
|
+
void fetch(opts.url, {
|
|
3300
|
+
method: "POST",
|
|
3301
|
+
headers: { "content-type": "application/json" },
|
|
3302
|
+
body,
|
|
3303
|
+
keepalive: true
|
|
3304
|
+
}).catch(() => {
|
|
3305
|
+
});
|
|
3306
|
+
delivered = true;
|
|
3307
|
+
} catch {
|
|
3308
|
+
delivered = false;
|
|
3309
|
+
}
|
|
3310
|
+
}
|
|
3311
|
+
if (delivered) pending = null;
|
|
3312
|
+
};
|
|
3313
|
+
const onHidden = () => {
|
|
3314
|
+
if (typeof document !== "undefined" && document.visibilityState === "hidden") {
|
|
3315
|
+
flushBeacon();
|
|
3316
|
+
}
|
|
3317
|
+
};
|
|
3318
|
+
const onPageHide = () => flushBeacon();
|
|
3319
|
+
const onOnline = () => trySend();
|
|
3320
|
+
if (typeof window !== "undefined" && typeof window.addEventListener === "function") {
|
|
3321
|
+
try {
|
|
3322
|
+
document.addEventListener("visibilitychange", onHidden);
|
|
3323
|
+
window.addEventListener("pagehide", onPageHide);
|
|
3324
|
+
window.addEventListener("online", onOnline);
|
|
3325
|
+
} catch {
|
|
3326
|
+
}
|
|
3327
|
+
}
|
|
3328
|
+
const dispose = () => {
|
|
3329
|
+
disposed = true;
|
|
3330
|
+
clearRetry();
|
|
3331
|
+
pending = null;
|
|
3332
|
+
if (typeof window !== "undefined" && typeof window.removeEventListener === "function") {
|
|
3333
|
+
try {
|
|
3334
|
+
document.removeEventListener("visibilitychange", onHidden);
|
|
3335
|
+
window.removeEventListener("pagehide", onPageHide);
|
|
3336
|
+
window.removeEventListener("online", onOnline);
|
|
3337
|
+
} catch {
|
|
3338
|
+
}
|
|
3339
|
+
}
|
|
3340
|
+
};
|
|
3341
|
+
return { push, flushBeacon, dispose };
|
|
3342
|
+
}
|
|
3343
|
+
|
|
3344
|
+
// src/tracking/platformTracker.ts
|
|
3345
|
+
function validClickIds(acq) {
|
|
3346
|
+
return Object.entries(acq.clickIds ?? {}).filter(
|
|
3347
|
+
(e) => typeof e[1] === "string" && e[1] !== ""
|
|
3348
|
+
);
|
|
3349
|
+
}
|
|
3350
|
+
function acquisitionQueryBag(acq) {
|
|
3351
|
+
const q = {};
|
|
3352
|
+
const utm = acq.utm ?? {};
|
|
3353
|
+
for (const [k, v] of Object.entries({
|
|
3354
|
+
utm_source: utm.source,
|
|
3355
|
+
utm_medium: utm.medium,
|
|
3356
|
+
utm_campaign: utm.campaign,
|
|
3357
|
+
utm_term: utm.term,
|
|
3358
|
+
utm_content: utm.content
|
|
3359
|
+
})) if (v) q[k] = v;
|
|
3360
|
+
for (const [k, v] of validClickIds(acq)) q[k] = v;
|
|
3361
|
+
if (acq.cookies?.fbp) q._fbp = acq.cookies.fbp;
|
|
3362
|
+
if (acq.cookies?.fbc) q._fbc = acq.cookies.fbc;
|
|
3363
|
+
return q;
|
|
3364
|
+
}
|
|
3365
|
+
function acquisitionAdAttribution(acq) {
|
|
3366
|
+
const a = {};
|
|
3367
|
+
for (const [k, v] of validClickIds(acq)) a[k] = v;
|
|
3368
|
+
if (acq.cookies?.fbp) a.fbp = acq.cookies.fbp;
|
|
3369
|
+
if (acq.cookies?.fbc) a.fbc = acq.cookies.fbc;
|
|
3370
|
+
else if (acq.clickIds?.fbclid) a.fbc = `fb.1.${Date.now()}.${acq.clickIds.fbclid}`;
|
|
3371
|
+
return a;
|
|
3372
|
+
}
|
|
3373
|
+
function createPlatformTracker(cfg) {
|
|
3374
|
+
if (typeof window === "undefined" || cfg.mode === "test") return createConsoleTracker();
|
|
3375
|
+
const base = cfg.apiBase.replace(/\/+$/, "");
|
|
3376
|
+
const url = `${base}/campaign/${encodeURIComponent(cfg.campaignId)}/event`;
|
|
3377
|
+
const landingUrl = window.location.href;
|
|
3378
|
+
const sidKey = `af_sid:${cfg.campaignId}`;
|
|
3379
|
+
let sessionId = null;
|
|
3380
|
+
try {
|
|
3381
|
+
sessionId = window.sessionStorage.getItem(sidKey);
|
|
3382
|
+
} catch {
|
|
3383
|
+
}
|
|
3384
|
+
if (!sessionId && cfg.sessionId) sessionId = cfg.sessionId;
|
|
3385
|
+
let acquisition = null;
|
|
3386
|
+
let acquisitionSent = false;
|
|
3387
|
+
let chain = Promise.resolve();
|
|
3388
|
+
const send = async (event, data, userData) => {
|
|
3389
|
+
let mergedUserData = landingUrl ? { landingUrl, ...userData } : userData;
|
|
3390
|
+
let adAttribution;
|
|
3391
|
+
let attachingAcquisition = false;
|
|
3392
|
+
if (acquisition && !acquisitionSent) {
|
|
3393
|
+
attachingAcquisition = true;
|
|
3394
|
+
const query = acquisitionQueryBag(acquisition);
|
|
3395
|
+
if (Object.keys(query).length) mergedUserData = { ...mergedUserData, query };
|
|
3396
|
+
const ad = acquisitionAdAttribution(acquisition);
|
|
3397
|
+
if (Object.keys(ad).length) adAttribution = ad;
|
|
3398
|
+
}
|
|
3399
|
+
const body = {
|
|
3400
|
+
campaignId: cfg.campaignId,
|
|
3401
|
+
funnelId: cfg.funnelId,
|
|
3402
|
+
event,
|
|
3403
|
+
sessionId: sessionId ?? void 0,
|
|
3404
|
+
visitorId: cfg.visitorId ?? void 0,
|
|
3405
|
+
// Campaign route stamp (campaign-routing pivot): ingestion reads this ONLY at session create
|
|
3406
|
+
// (WebFunnelSession.campaignRouteId, snapshot semantics) — carried on every event but ignored
|
|
3407
|
+
// once the session exists. Undefined ⇒ default funnel / no route matched.
|
|
3408
|
+
campaignRouteId: cfg.route?.id ?? void 0,
|
|
3409
|
+
eventId: typeof data?.eventId === "string" ? data.eventId : void 0,
|
|
3410
|
+
data: data ?? {},
|
|
3411
|
+
metadata: {
|
|
3412
|
+
language: typeof navigator !== "undefined" ? navigator.language : void 0,
|
|
3413
|
+
screen: typeof window.screen !== "undefined" ? `${window.screen.width}x${window.screen.height}` : void 0,
|
|
3414
|
+
timezone: (() => {
|
|
3415
|
+
try {
|
|
3416
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
3417
|
+
} catch {
|
|
3418
|
+
return void 0;
|
|
3419
|
+
}
|
|
3420
|
+
})(),
|
|
3421
|
+
referrer: typeof document !== "undefined" && document.referrer ? document.referrer : void 0
|
|
3422
|
+
},
|
|
3423
|
+
...mergedUserData ? { userData: mergedUserData } : {},
|
|
3424
|
+
...adAttribution ? { adAttribution } : {}
|
|
3425
|
+
};
|
|
3426
|
+
const res = await fetch(url, {
|
|
3427
|
+
method: "POST",
|
|
3428
|
+
headers: { "content-type": "application/json" },
|
|
3429
|
+
body: JSON.stringify(body),
|
|
3430
|
+
keepalive: true
|
|
3431
|
+
// survives page.exit / unload
|
|
3432
|
+
});
|
|
3433
|
+
if (attachingAcquisition) acquisitionSent = true;
|
|
3434
|
+
const json = await res.json().catch(() => null);
|
|
3435
|
+
if (json?.sessionId && json.sessionId !== sessionId) {
|
|
3436
|
+
sessionId = json.sessionId;
|
|
3437
|
+
try {
|
|
3438
|
+
window.sessionStorage.setItem(sidKey, sessionId);
|
|
3439
|
+
} catch {
|
|
3440
|
+
}
|
|
3441
|
+
}
|
|
3442
|
+
};
|
|
3443
|
+
const enqueue = (event, data, userData) => {
|
|
3444
|
+
const task = () => send(event, data, userData).catch((e) => {
|
|
3445
|
+
if (process.env.NODE_ENV !== "production") console.warn(`[appfunnel] track failed (${event}):`, e);
|
|
3446
|
+
});
|
|
3447
|
+
if (!sessionId) {
|
|
3448
|
+
chain = chain.then(task);
|
|
3449
|
+
} else {
|
|
3450
|
+
void task();
|
|
3451
|
+
}
|
|
3452
|
+
};
|
|
3453
|
+
const VARS_DEBOUNCE_MS = 800;
|
|
3454
|
+
const dataUrl = `${base}/campaign/${encodeURIComponent(cfg.campaignId)}/event/session/data`;
|
|
3455
|
+
let pendingVars = null;
|
|
3456
|
+
let varsTimer = null;
|
|
3457
|
+
const persistence = createSnapshotPersistence({
|
|
3458
|
+
url: dataUrl,
|
|
3459
|
+
getSessionId: () => sessionId
|
|
3460
|
+
});
|
|
3461
|
+
const flushVars = () => {
|
|
3462
|
+
if (varsTimer) {
|
|
3463
|
+
clearTimeout(varsTimer);
|
|
3464
|
+
varsTimer = null;
|
|
3465
|
+
}
|
|
3466
|
+
if (!pendingVars) return;
|
|
3467
|
+
const snapshot = pendingVars;
|
|
3468
|
+
pendingVars = null;
|
|
3469
|
+
persistence.push(snapshot);
|
|
3470
|
+
if (!sessionId) chain = chain.then(() => {
|
|
3471
|
+
persistence.push(snapshot);
|
|
3472
|
+
});
|
|
3473
|
+
};
|
|
3474
|
+
const drainOnExit = () => {
|
|
3475
|
+
if (pendingVars) {
|
|
3476
|
+
flushVars();
|
|
3477
|
+
persistence.flushBeacon();
|
|
3478
|
+
}
|
|
3479
|
+
};
|
|
3480
|
+
try {
|
|
3481
|
+
window.addEventListener("visibilitychange", drainOnExit);
|
|
3482
|
+
window.addEventListener("pagehide", drainOnExit);
|
|
3483
|
+
} catch {
|
|
3484
|
+
}
|
|
3485
|
+
if (cfg.experiment?.id && cfg.experiment.variant) {
|
|
3486
|
+
enqueue("experiment.exposure", exposurePayload(cfg.experiment.id, cfg.experiment.variant, cfg.experiment.version));
|
|
3487
|
+
}
|
|
3488
|
+
return {
|
|
3489
|
+
track: (event, data, userData) => enqueue(event, data, userData),
|
|
3490
|
+
identify: (email, eventId) => enqueue("user.registered", { email, ...eventId ? { eventId } : {} }),
|
|
3491
|
+
setVariables: (variables) => {
|
|
3492
|
+
pendingVars = variables;
|
|
3493
|
+
if (varsTimer) clearTimeout(varsTimer);
|
|
3494
|
+
varsTimer = setTimeout(flushVars, VARS_DEBOUNCE_MS);
|
|
3495
|
+
},
|
|
3496
|
+
setAcquisition: (acq) => {
|
|
3497
|
+
acquisition = acq;
|
|
3498
|
+
}
|
|
3499
|
+
};
|
|
3500
|
+
}
|
|
3501
|
+
|
|
3502
|
+
// src/flow/mount.tsx
|
|
3503
|
+
var AssetsContext = React.createContext({});
|
|
3504
|
+
function AssetsProvider({
|
|
3505
|
+
assets,
|
|
3506
|
+
children
|
|
3507
|
+
}) {
|
|
3508
|
+
return React.createElement(AssetsContext.Provider, { value: assets ?? {} }, children);
|
|
3509
|
+
}
|
|
3510
|
+
function useAssets() {
|
|
3511
|
+
return React.useContext(AssetsContext);
|
|
3512
|
+
}
|
|
3513
|
+
function useAsset(name) {
|
|
3514
|
+
return React.useContext(AssetsContext)[name];
|
|
3515
|
+
}
|
|
3516
|
+
function createFunnelTree({
|
|
3517
|
+
config,
|
|
3518
|
+
pages,
|
|
3519
|
+
layout,
|
|
3520
|
+
checkoutDriver,
|
|
3521
|
+
messages
|
|
3522
|
+
}) {
|
|
3523
|
+
return function tree(opts) {
|
|
3524
|
+
const mode = opts.mode ?? "live";
|
|
3525
|
+
const embedEnabled = opts.embed ?? false;
|
|
3526
|
+
const tracker = opts.tracking ? createPlatformTracker({
|
|
3527
|
+
...opts.tracking,
|
|
3528
|
+
visitorId: opts.visitorId,
|
|
3529
|
+
sessionId: opts.sessionId,
|
|
3530
|
+
mode
|
|
3531
|
+
}) : void 0;
|
|
3532
|
+
const checkout = checkoutDriver && opts.tracking ? checkoutDriver({
|
|
3533
|
+
apiBase: opts.tracking.apiBase,
|
|
3534
|
+
campaignId: opts.tracking.campaignId,
|
|
3535
|
+
funnelId: opts.tracking.funnelId,
|
|
3536
|
+
mode,
|
|
3537
|
+
sessionId: opts.sessionId,
|
|
3538
|
+
visitorId: opts.visitorId
|
|
3539
|
+
}) : void 0;
|
|
3540
|
+
const layoutWithResume = function LayoutWithCheckoutResume({
|
|
3541
|
+
children
|
|
3542
|
+
}) {
|
|
3543
|
+
return React.createElement(
|
|
3544
|
+
React.Fragment,
|
|
3545
|
+
null,
|
|
3546
|
+
React.createElement(CheckoutResume, null),
|
|
3547
|
+
embedEnabled ? React.createElement(EmbedBridge, null) : null,
|
|
3548
|
+
layout ? React.createElement(layout, null, children) : children
|
|
3549
|
+
);
|
|
3550
|
+
};
|
|
3551
|
+
return React.createElement(AssetsProvider, {
|
|
3552
|
+
assets: opts.assets,
|
|
3553
|
+
children: React.createElement(FunnelProvider, {
|
|
3554
|
+
config,
|
|
3555
|
+
offerings: opts.offerings ?? [],
|
|
3556
|
+
messages: opts.messages ?? messages,
|
|
3557
|
+
locale: opts.locale,
|
|
3558
|
+
tracker,
|
|
3559
|
+
checkout,
|
|
3560
|
+
sessionValues: opts.sessionValues,
|
|
3561
|
+
deferSessionValues: opts.deferSessionValues,
|
|
3562
|
+
experiments: opts.experiments,
|
|
3563
|
+
context: {
|
|
3564
|
+
mode,
|
|
3565
|
+
visitorId: opts.visitorId ?? null,
|
|
3566
|
+
customerId: opts.customerId ?? null,
|
|
3567
|
+
sessionId: opts.sessionId ?? null
|
|
3568
|
+
},
|
|
3569
|
+
children: React.createElement(FunnelView, {
|
|
3570
|
+
pages,
|
|
3571
|
+
layout: layoutWithResume,
|
|
3572
|
+
initialKey: opts.initialKey,
|
|
3573
|
+
// FLASH-FREE HYDRATION (Option B, FIX 1): trust the SERVER-resolved initialKey instead of
|
|
3574
|
+
// re-running the entry guard. The CLIENT sets this via deferSessionValues (its deferred
|
|
3575
|
+
// snapshot is empty, so re-running the guard would bounce a qualified deep-link and
|
|
3576
|
+
// mismatch the markup). The build-time PRERENDER sets `trustInitialKey` explicitly so a
|
|
3577
|
+
// guarded page prerenders its OWN default-state markup (the renderer picks target-vs-bounce
|
|
3578
|
+
// at serve time). Live SSR fallback leaves both off → the guard runs against real values.
|
|
3579
|
+
trustInitialKey: opts.trustInitialKey ?? opts.deferSessionValues,
|
|
3580
|
+
prefetch: "off"
|
|
3581
|
+
// renderer background-modulepreloads all chunks
|
|
3582
|
+
})
|
|
3583
|
+
})
|
|
3584
|
+
});
|
|
3585
|
+
};
|
|
3586
|
+
}
|
|
1797
3587
|
function Choice({
|
|
1798
3588
|
bind,
|
|
1799
3589
|
options,
|
|
@@ -1806,7 +3596,7 @@ function Choice({
|
|
|
1806
3596
|
return options.map((opt) => {
|
|
1807
3597
|
const o = typeof opt === "string" ? { value: opt } : opt;
|
|
1808
3598
|
const selected = value === o.value;
|
|
1809
|
-
return
|
|
3599
|
+
return React.createElement(
|
|
1810
3600
|
"button",
|
|
1811
3601
|
{
|
|
1812
3602
|
key: o.value,
|
|
@@ -1825,7 +3615,7 @@ function Choice({
|
|
|
1825
3615
|
}
|
|
1826
3616
|
function Next({ children, onClick, ...rest }) {
|
|
1827
3617
|
const { next } = useNavigation();
|
|
1828
|
-
return
|
|
3618
|
+
return React.createElement(
|
|
1829
3619
|
"button",
|
|
1830
3620
|
{
|
|
1831
3621
|
type: "button",
|
|
@@ -1841,7 +3631,7 @@ function Next({ children, onClick, ...rest }) {
|
|
|
1841
3631
|
function Back({ children, onClick, ...rest }) {
|
|
1842
3632
|
const { back, canGoBack } = useNavigation();
|
|
1843
3633
|
if (!canGoBack) return null;
|
|
1844
|
-
return
|
|
3634
|
+
return React.createElement(
|
|
1845
3635
|
"button",
|
|
1846
3636
|
{
|
|
1847
3637
|
type: "button",
|
|
@@ -1894,7 +3684,7 @@ function Script({
|
|
|
1894
3684
|
onLoad,
|
|
1895
3685
|
onError
|
|
1896
3686
|
}) {
|
|
1897
|
-
|
|
3687
|
+
React.useEffect(() => {
|
|
1898
3688
|
if (typeof document === "undefined") return;
|
|
1899
3689
|
const key = id ?? src;
|
|
1900
3690
|
if (injected.has(key)) {
|
|
@@ -1933,159 +3723,202 @@ function Script({
|
|
|
1933
3723
|
return null;
|
|
1934
3724
|
}
|
|
1935
3725
|
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
return
|
|
1939
|
-
}
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
function
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
}
|
|
2023
|
-
}
|
|
2024
|
-
|
|
2025
|
-
|
|
3726
|
+
Object.defineProperty(exports, "assignVariant", {
|
|
3727
|
+
enumerable: true,
|
|
3728
|
+
get: function () { return chunkRKPRMTSU_cjs.assignVariant; }
|
|
3729
|
+
});
|
|
3730
|
+
Object.defineProperty(exports, "bucketingSeed", {
|
|
3731
|
+
enumerable: true,
|
|
3732
|
+
get: function () { return chunkRKPRMTSU_cjs.bucketingSeed; }
|
|
3733
|
+
});
|
|
3734
|
+
Object.defineProperty(exports, "buildCatalog", {
|
|
3735
|
+
enumerable: true,
|
|
3736
|
+
get: function () { return chunkRKPRMTSU_cjs.buildCatalog; }
|
|
3737
|
+
});
|
|
3738
|
+
Object.defineProperty(exports, "compileManifest", {
|
|
3739
|
+
enumerable: true,
|
|
3740
|
+
get: function () { return chunkRKPRMTSU_cjs.compileManifest; }
|
|
3741
|
+
});
|
|
3742
|
+
Object.defineProperty(exports, "currencyExponent", {
|
|
3743
|
+
enumerable: true,
|
|
3744
|
+
get: function () { return chunkRKPRMTSU_cjs.currencyExponent; }
|
|
3745
|
+
});
|
|
3746
|
+
Object.defineProperty(exports, "defineFunnel", {
|
|
3747
|
+
enumerable: true,
|
|
3748
|
+
get: function () { return chunkRKPRMTSU_cjs.defineFunnel; }
|
|
3749
|
+
});
|
|
3750
|
+
Object.defineProperty(exports, "definePage", {
|
|
3751
|
+
enumerable: true,
|
|
3752
|
+
get: function () { return chunkRKPRMTSU_cjs.definePage; }
|
|
3753
|
+
});
|
|
3754
|
+
Object.defineProperty(exports, "entryGuard", {
|
|
3755
|
+
enumerable: true,
|
|
3756
|
+
get: function () { return chunkRKPRMTSU_cjs.entryGuard; }
|
|
3757
|
+
});
|
|
3758
|
+
Object.defineProperty(exports, "evaluateCondition", {
|
|
3759
|
+
enumerable: true,
|
|
3760
|
+
get: function () { return chunkRKPRMTSU_cjs.evaluateCondition; }
|
|
3761
|
+
});
|
|
3762
|
+
Object.defineProperty(exports, "evaluateGate", {
|
|
3763
|
+
enumerable: true,
|
|
3764
|
+
get: function () { return chunkRKPRMTSU_cjs.evaluateGate; }
|
|
3765
|
+
});
|
|
3766
|
+
Object.defineProperty(exports, "expectedPathLength", {
|
|
3767
|
+
enumerable: true,
|
|
3768
|
+
get: function () { return chunkRKPRMTSU_cjs.expectedPathLength; }
|
|
3769
|
+
});
|
|
3770
|
+
Object.defineProperty(exports, "fnv1a", {
|
|
3771
|
+
enumerable: true,
|
|
3772
|
+
get: function () { return chunkRKPRMTSU_cjs.fnv1a; }
|
|
3773
|
+
});
|
|
3774
|
+
Object.defineProperty(exports, "formatMoney", {
|
|
3775
|
+
enumerable: true,
|
|
3776
|
+
get: function () { return chunkRKPRMTSU_cjs.formatMoney; }
|
|
3777
|
+
});
|
|
3778
|
+
Object.defineProperty(exports, "formatOffering", {
|
|
3779
|
+
enumerable: true,
|
|
3780
|
+
get: function () { return chunkRKPRMTSU_cjs.formatOffering; }
|
|
3781
|
+
});
|
|
3782
|
+
Object.defineProperty(exports, "groupProgress", {
|
|
3783
|
+
enumerable: true,
|
|
3784
|
+
get: function () { return chunkRKPRMTSU_cjs.groupProgress; }
|
|
3785
|
+
});
|
|
3786
|
+
Object.defineProperty(exports, "hashToUnit", {
|
|
3787
|
+
enumerable: true,
|
|
3788
|
+
get: function () { return chunkRKPRMTSU_cjs.hashToUnit; }
|
|
3789
|
+
});
|
|
3790
|
+
Object.defineProperty(exports, "isRtl", {
|
|
3791
|
+
enumerable: true,
|
|
3792
|
+
get: function () { return chunkRKPRMTSU_cjs.isRtl; }
|
|
3793
|
+
});
|
|
3794
|
+
Object.defineProperty(exports, "isVariantKey", {
|
|
3795
|
+
enumerable: true,
|
|
3796
|
+
get: function () { return chunkRKPRMTSU_cjs.isVariantKey; }
|
|
3797
|
+
});
|
|
3798
|
+
Object.defineProperty(exports, "nextPage", {
|
|
3799
|
+
enumerable: true,
|
|
3800
|
+
get: function () { return chunkRKPRMTSU_cjs.nextPage; }
|
|
3801
|
+
});
|
|
3802
|
+
Object.defineProperty(exports, "outgoingKeys", {
|
|
3803
|
+
enumerable: true,
|
|
3804
|
+
get: function () { return chunkRKPRMTSU_cjs.outgoingKeys; }
|
|
3805
|
+
});
|
|
3806
|
+
Object.defineProperty(exports, "pageMeta", {
|
|
3807
|
+
enumerable: true,
|
|
3808
|
+
get: function () { return chunkRKPRMTSU_cjs.pageMeta; }
|
|
3809
|
+
});
|
|
3810
|
+
Object.defineProperty(exports, "parseSlotKey", {
|
|
3811
|
+
enumerable: true,
|
|
3812
|
+
get: function () { return chunkRKPRMTSU_cjs.parseSlotKey; }
|
|
3813
|
+
});
|
|
3814
|
+
Object.defineProperty(exports, "pickByWeight", {
|
|
3815
|
+
enumerable: true,
|
|
3816
|
+
get: function () { return chunkRKPRMTSU_cjs.pickByWeight; }
|
|
3817
|
+
});
|
|
3818
|
+
Object.defineProperty(exports, "resolveExperiments", {
|
|
3819
|
+
enumerable: true,
|
|
3820
|
+
get: function () { return chunkRKPRMTSU_cjs.resolveExperiments; }
|
|
3821
|
+
});
|
|
3822
|
+
Object.defineProperty(exports, "resolveLocale", {
|
|
3823
|
+
enumerable: true,
|
|
3824
|
+
get: function () { return chunkRKPRMTSU_cjs.resolveLocale; }
|
|
3825
|
+
});
|
|
3826
|
+
Object.defineProperty(exports, "resolveOffering", {
|
|
3827
|
+
enumerable: true,
|
|
3828
|
+
get: function () { return chunkRKPRMTSU_cjs.resolveOffering; }
|
|
3829
|
+
});
|
|
3830
|
+
Object.defineProperty(exports, "resolveRoute", {
|
|
3831
|
+
enumerable: true,
|
|
3832
|
+
get: function () { return chunkRKPRMTSU_cjs.resolveRoute; }
|
|
3833
|
+
});
|
|
3834
|
+
Object.defineProperty(exports, "INLINE_SURFACES", {
|
|
3835
|
+
enumerable: true,
|
|
3836
|
+
get: function () { return chunk7JLOF6CJ_cjs.INLINE_SURFACES; }
|
|
3837
|
+
});
|
|
3838
|
+
Object.defineProperty(exports, "PROVIDER_PROFILES", {
|
|
3839
|
+
enumerable: true,
|
|
3840
|
+
get: function () { return chunk7JLOF6CJ_cjs.PROVIDER_PROFILES; }
|
|
3841
|
+
});
|
|
3842
|
+
Object.defineProperty(exports, "checkoutError", {
|
|
3843
|
+
enumerable: true,
|
|
3844
|
+
get: function () { return chunk7JLOF6CJ_cjs.checkoutError; }
|
|
3845
|
+
});
|
|
3846
|
+
Object.defineProperty(exports, "isInlineSurface", {
|
|
3847
|
+
enumerable: true,
|
|
3848
|
+
get: function () { return chunk7JLOF6CJ_cjs.isInlineSurface; }
|
|
3849
|
+
});
|
|
3850
|
+
Object.defineProperty(exports, "isMerchantOfRecord", {
|
|
3851
|
+
enumerable: true,
|
|
3852
|
+
get: function () { return chunk7JLOF6CJ_cjs.isMerchantOfRecord; }
|
|
3853
|
+
});
|
|
3854
|
+
Object.defineProperty(exports, "isOrchestrator", {
|
|
3855
|
+
enumerable: true,
|
|
3856
|
+
get: function () { return chunk7JLOF6CJ_cjs.isOrchestrator; }
|
|
3857
|
+
});
|
|
3858
|
+
Object.defineProperty(exports, "isRenderableSurface", {
|
|
3859
|
+
enumerable: true,
|
|
3860
|
+
get: function () { return chunk7JLOF6CJ_cjs.isRenderableSurface; }
|
|
3861
|
+
});
|
|
3862
|
+
Object.defineProperty(exports, "renderableSurfaces", {
|
|
3863
|
+
enumerable: true,
|
|
3864
|
+
get: function () { return chunk7JLOF6CJ_cjs.renderableSurfaces; }
|
|
3865
|
+
});
|
|
3866
|
+
Object.defineProperty(exports, "resolveSurface", {
|
|
3867
|
+
enumerable: true,
|
|
3868
|
+
get: function () { return chunk7JLOF6CJ_cjs.resolveSurface; }
|
|
3869
|
+
});
|
|
3870
|
+
Object.defineProperty(exports, "surfacesFor", {
|
|
3871
|
+
enumerable: true,
|
|
3872
|
+
get: function () { return chunk7JLOF6CJ_cjs.surfacesFor; }
|
|
3873
|
+
});
|
|
3874
|
+
Object.defineProperty(exports, "validateCheckout", {
|
|
3875
|
+
enumerable: true,
|
|
3876
|
+
get: function () { return chunk7JLOF6CJ_cjs.validateCheckout; }
|
|
3877
|
+
});
|
|
3878
|
+
Object.defineProperty(exports, "validateUpsell", {
|
|
2026
3879
|
enumerable: true,
|
|
2027
|
-
get: function () { return
|
|
3880
|
+
get: function () { return chunk7JLOF6CJ_cjs.validateUpsell; }
|
|
2028
3881
|
});
|
|
3882
|
+
exports.AssetsContext = AssetsContext;
|
|
3883
|
+
exports.AssetsProvider = AssetsProvider;
|
|
2029
3884
|
exports.Back = Back;
|
|
2030
3885
|
exports.Checkout = Checkout;
|
|
3886
|
+
exports.CheckoutResume = CheckoutResume;
|
|
2031
3887
|
exports.Choice = Choice;
|
|
2032
3888
|
exports.EVENT_CATALOG = EVENT_CATALOG;
|
|
2033
3889
|
exports.FunnelProvider = FunnelProvider;
|
|
2034
3890
|
exports.FunnelToaster = FunnelToaster;
|
|
2035
3891
|
exports.FunnelView = FunnelView;
|
|
2036
|
-
exports.INLINE_SURFACES = INLINE_SURFACES;
|
|
2037
3892
|
exports.Modal = Modal;
|
|
2038
3893
|
exports.Next = Next;
|
|
2039
|
-
exports.PROVIDER_PROFILES = PROVIDER_PROFILES;
|
|
2040
3894
|
exports.Script = Script;
|
|
2041
3895
|
exports.Sheet = Sheet;
|
|
2042
3896
|
exports.Upsell = Upsell;
|
|
2043
3897
|
exports.VariableStore = VariableStore;
|
|
2044
|
-
exports.assignVariant = assignVariant;
|
|
2045
3898
|
exports.attachBus = attachBus;
|
|
2046
|
-
exports.bucketingSeed = bucketingSeed;
|
|
2047
3899
|
exports.buildAcquisition = buildAcquisition;
|
|
2048
|
-
exports.
|
|
2049
|
-
exports.checkoutError = checkoutError;
|
|
2050
|
-
exports.compileManifest = compileManifest;
|
|
3900
|
+
exports.buildContext = buildContext;
|
|
2051
3901
|
exports.createBus = createBus;
|
|
2052
3902
|
exports.createConsoleTracker = createConsoleTracker;
|
|
2053
3903
|
exports.createFunnelStore = createFunnelStore;
|
|
3904
|
+
exports.createFunnelTree = createFunnelTree;
|
|
2054
3905
|
exports.createMockDriver = createMockDriver;
|
|
2055
|
-
exports.
|
|
3906
|
+
exports.createPlatformTracker = createPlatformTracker;
|
|
2056
3907
|
exports.defineModal = defineModal;
|
|
2057
|
-
exports.
|
|
2058
|
-
exports.entryGuard = entryGuard;
|
|
2059
|
-
exports.evaluateCondition = evaluateCondition;
|
|
2060
|
-
exports.evaluateGate = evaluateGate;
|
|
2061
|
-
exports.expectedPathLength = expectedPathLength;
|
|
2062
|
-
exports.fnv1a = fnv1a;
|
|
2063
|
-
exports.formatProduct = formatProduct;
|
|
2064
|
-
exports.hashToUnit = hashToUnit;
|
|
3908
|
+
exports.driverWithEmail = driverWithEmail;
|
|
2065
3909
|
exports.hideModal = hideModal;
|
|
2066
3910
|
exports.integrationEvents = integrationEvents;
|
|
2067
|
-
exports.isInlineSurface = isInlineSurface;
|
|
2068
3911
|
exports.isIntegrationEvent = isIntegrationEvent;
|
|
2069
|
-
exports.isMerchantOfRecord = isMerchantOfRecord;
|
|
2070
|
-
exports.isOrchestrator = isOrchestrator;
|
|
2071
|
-
exports.isRtl = isRtl;
|
|
2072
3912
|
exports.isTrackableEvent = isTrackableEvent;
|
|
2073
|
-
exports.isVariantKey = isVariantKey;
|
|
2074
3913
|
exports.newEventId = newEventId;
|
|
2075
|
-
exports.nextPage = nextPage;
|
|
2076
|
-
exports.outgoingKeys = outgoingKeys;
|
|
2077
|
-
exports.pageMeta = pageMeta;
|
|
2078
|
-
exports.parseSlotKey = parseSlotKey;
|
|
2079
|
-
exports.pickByWeight = pickByWeight;
|
|
2080
3914
|
exports.registerModal = registerModal;
|
|
2081
3915
|
exports.removeModal = removeModal;
|
|
2082
|
-
exports.resolveExperiments = resolveExperiments;
|
|
2083
|
-
exports.resolveLocale = resolveLocale;
|
|
2084
|
-
exports.resolveProduct = resolveProduct;
|
|
2085
|
-
exports.resolveRoute = resolveRoute;
|
|
2086
3916
|
exports.showModal = showModal;
|
|
2087
|
-
exports.
|
|
3917
|
+
exports.toast = toast;
|
|
2088
3918
|
exports.unregisterModal = unregisterModal;
|
|
3919
|
+
exports.useActiveLocale = useActiveLocale;
|
|
3920
|
+
exports.useAsset = useAsset;
|
|
3921
|
+
exports.useAssets = useAssets;
|
|
2089
3922
|
exports.useCheckout = useCheckout;
|
|
2090
3923
|
exports.useClickIds = useClickIds;
|
|
2091
3924
|
exports.useContextValue = useContextValue;
|
|
@@ -2094,21 +3927,19 @@ exports.useDevice = useDevice;
|
|
|
2094
3927
|
exports.useExperiment = useExperiment;
|
|
2095
3928
|
exports.useField = useField;
|
|
2096
3929
|
exports.useFunnel = useFunnel;
|
|
3930
|
+
exports.useGroupProgress = useGroupProgress;
|
|
2097
3931
|
exports.useLocale = useLocale;
|
|
2098
3932
|
exports.useModal = useModal;
|
|
2099
3933
|
exports.useNavigation = useNavigation;
|
|
3934
|
+
exports.useOffering = useOffering;
|
|
3935
|
+
exports.useOfferings = useOfferings;
|
|
2100
3936
|
exports.usePage = usePage;
|
|
2101
|
-
exports.useProduct = useProduct;
|
|
2102
|
-
exports.useProducts = useProducts;
|
|
2103
3937
|
exports.useResponse = useResponse;
|
|
2104
3938
|
exports.useSystem = useSystem;
|
|
2105
3939
|
exports.useTracker = useTracker;
|
|
2106
3940
|
exports.useTranslation = useTranslation;
|
|
2107
3941
|
exports.useUserAttribute = useUserAttribute;
|
|
2108
3942
|
exports.useUtm = useUtm;
|
|
2109
|
-
exports.validateCheckout = validateCheckout;
|
|
2110
|
-
exports.validateExperiments = validateExperiments;
|
|
2111
|
-
exports.validateUpsell = validateUpsell;
|
|
2112
3943
|
exports.withBus = withBus;
|
|
2113
3944
|
//# sourceMappingURL=index.cjs.map
|
|
2114
3945
|
//# sourceMappingURL=index.cjs.map
|