@appfunnel-dev/sdk 2.0.0-canary.0 → 2.0.0-canary.10
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-VV7TFC64.cjs +106 -0
- package/dist/chunk-VV7TFC64.cjs.map +1 -0
- package/dist/chunk-VW2HVPR4.js +446 -0
- package/dist/chunk-VW2HVPR4.js.map +1 -0
- package/dist/chunk-WYUDL4FI.cjs +8 -0
- package/dist/chunk-WYUDL4FI.cjs.map +1 -0
- package/dist/chunk-Y4YNJ2EX.cjs +476 -0
- package/dist/chunk-Y4YNJ2EX.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 +2654 -843
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +351 -1005
- package/dist/index.d.ts +351 -1005
- package/dist/index.js +2386 -704
- package/dist/index.js.map +1 -1
- package/dist/manifest-B3Tdab0M.d.cts +920 -0
- package/dist/manifest-B3Tdab0M.d.ts +920 -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 chunkY4YNJ2EX_cjs = require('./chunk-Y4YNJ2EX.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(() => chunkY4YNJ2EX_cjs.resolveLocale(config, detected, override));
|
|
508
|
+
React.useEffect(() => {
|
|
509
|
+
setLocale(chunkY4YNJ2EX_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,336 +578,140 @@ function useTranslation() {
|
|
|
503
578
|
fmt,
|
|
504
579
|
locale,
|
|
505
580
|
setLocale,
|
|
506
|
-
dir: isRtl(locale) ? "rtl" : "ltr",
|
|
581
|
+
dir: chunkY4YNJ2EX_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 ? chunkY4YNJ2EX_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) => chunkY4YNJ2EX_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 ExperimentContext =
|
|
711
|
+
var NavContext = React.createContext(null);
|
|
712
|
+
var ExperimentContext = React.createContext({});
|
|
834
713
|
function useExperiment(id) {
|
|
835
|
-
return
|
|
714
|
+
return React.useContext(ExperimentContext)[id];
|
|
836
715
|
}
|
|
837
716
|
function pageContextFor(currentKey, slug, index, total, startedAt) {
|
|
838
717
|
const denom = Math.max(total, index + 1);
|
|
@@ -845,9 +724,43 @@ function pageContextFor(currentKey, slug, index, total, startedAt) {
|
|
|
845
724
|
startedAt
|
|
846
725
|
};
|
|
847
726
|
}
|
|
727
|
+
var now = () => typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
728
|
+
function usePageExitTimer(tracker, currentKey) {
|
|
729
|
+
const timer = React.useRef(null);
|
|
730
|
+
const emitExit = React.useCallback(() => {
|
|
731
|
+
const t = timer.current;
|
|
732
|
+
if (!t) return;
|
|
733
|
+
const at = now();
|
|
734
|
+
const hidden = t.hidden + (t.hiddenSince != null ? at - t.hiddenSince : 0);
|
|
735
|
+
const durationMs = Math.round(at - t.enteredAt);
|
|
736
|
+
tracker.track("page.exit", { pageId: t.key, durationMs, activeMs: Math.max(0, Math.round(durationMs - hidden)) });
|
|
737
|
+
}, [tracker]);
|
|
738
|
+
React.useEffect(() => {
|
|
739
|
+
if (typeof document === "undefined") return;
|
|
740
|
+
const onVis = () => {
|
|
741
|
+
const t = timer.current;
|
|
742
|
+
if (!t) return;
|
|
743
|
+
if (document.visibilityState === "hidden") t.hiddenSince = now();
|
|
744
|
+
else if (t.hiddenSince != null) {
|
|
745
|
+
t.hidden += now() - t.hiddenSince;
|
|
746
|
+
t.hiddenSince = null;
|
|
747
|
+
}
|
|
748
|
+
};
|
|
749
|
+
document.addEventListener("visibilitychange", onVis);
|
|
750
|
+
return () => document.removeEventListener("visibilitychange", onVis);
|
|
751
|
+
}, []);
|
|
752
|
+
React.useEffect(() => {
|
|
753
|
+
if (!currentKey) return;
|
|
754
|
+
if (timer.current && timer.current.key !== currentKey) emitExit();
|
|
755
|
+
const hidden = typeof document !== "undefined" && document.visibilityState === "hidden";
|
|
756
|
+
timer.current = { key: currentKey, enteredAt: now(), hidden: 0, hiddenSince: hidden ? now() : null };
|
|
757
|
+
}, [currentKey, emitExit]);
|
|
758
|
+
React.useEffect(() => () => emitExit(), [emitExit]);
|
|
759
|
+
}
|
|
848
760
|
function FunnelView({
|
|
849
761
|
pages,
|
|
850
762
|
initialKey,
|
|
763
|
+
trustInitialKey = false,
|
|
851
764
|
layout,
|
|
852
765
|
fallback,
|
|
853
766
|
prefetch: prefetchMode = "auto",
|
|
@@ -855,140 +768,123 @@ function FunnelView({
|
|
|
855
768
|
}) {
|
|
856
769
|
const funnel = useFunnel();
|
|
857
770
|
const tracker = useTrackerRef();
|
|
858
|
-
const components =
|
|
771
|
+
const components = React.useMemo(() => {
|
|
859
772
|
const map = /* @__PURE__ */ new Map();
|
|
860
773
|
for (const p of pages) {
|
|
861
774
|
if (p.Component) map.set(p.key, p.Component);
|
|
862
775
|
else if (p.load) {
|
|
863
776
|
const load = p.load;
|
|
864
|
-
map.set(p.key,
|
|
777
|
+
map.set(p.key, React.lazy(() => Promise.resolve(load()).then(
|
|
865
778
|
(m) => typeof m === "function" ? { default: m } : m
|
|
866
779
|
)));
|
|
867
780
|
}
|
|
868
781
|
}
|
|
869
782
|
return map;
|
|
870
783
|
}, [pages]);
|
|
871
|
-
const loaders =
|
|
784
|
+
const loaders = React.useMemo(
|
|
872
785
|
() => new Map(pages.filter((p) => p.load).map((p) => [p.key, p.load])),
|
|
873
786
|
[pages]
|
|
874
787
|
);
|
|
875
|
-
const
|
|
876
|
-
|
|
877
|
-
|
|
788
|
+
const seedRef = React.useRef(null);
|
|
789
|
+
if (seedRef.current === null) seedRef.current = chunkY4YNJ2EX_cjs.bucketingSeed(funnel.context);
|
|
790
|
+
const seed = seedRef.current;
|
|
791
|
+
const experiments = React.useMemo(
|
|
792
|
+
() => chunkY4YNJ2EX_cjs.resolveExperiments(pages, funnel.experiments, seed),
|
|
878
793
|
[pages, funnel.experiments, seed]
|
|
879
794
|
);
|
|
880
|
-
const
|
|
881
|
-
const
|
|
882
|
-
|
|
795
|
+
const toSlot = React.useCallback((k) => experiments.slotOf[k] ?? k, [experiments]);
|
|
796
|
+
const toRenderKey = React.useCallback((k) => experiments.render[k] ?? k, [experiments]);
|
|
797
|
+
const pageByKey = React.useMemo(() => new Map(pages.map((p) => [p.key, p])), [pages]);
|
|
798
|
+
const flowPages = React.useMemo(
|
|
799
|
+
() => experiments.activeKeys.map((k) => pageByKey.get(k)).filter((p) => p !== void 0),
|
|
883
800
|
[experiments, pageByKey]
|
|
884
801
|
);
|
|
885
|
-
const flow =
|
|
886
|
-
() => flowPages.map((p) =>
|
|
887
|
-
|
|
802
|
+
const flow = React.useMemo(
|
|
803
|
+
() => flowPages.map((p) => {
|
|
804
|
+
const servedVariant = experiments.render[p.key];
|
|
805
|
+
const variantNext = servedVariant ? pageByKey.get(servedVariant)?.meta?.next : void 0;
|
|
806
|
+
return variantNext ? { key: p.key, meta: { ...p.meta ?? {}, next: variantNext } } : { key: p.key, meta: p.meta };
|
|
807
|
+
}),
|
|
808
|
+
[flowPages, experiments, pageByKey]
|
|
888
809
|
);
|
|
889
|
-
const [history, setHistory] =
|
|
810
|
+
const [history, setHistory] = React.useState(() => {
|
|
890
811
|
const start = flowPages[0]?.key;
|
|
891
|
-
if (!initialKey || initialKey === start) return [start]
|
|
892
|
-
const target =
|
|
812
|
+
if (!initialKey || initialKey === start) return start ? [start] : [];
|
|
813
|
+
const target = toSlot(initialKey);
|
|
893
814
|
const page2 = flowPages.find((p) => p.key === target);
|
|
894
|
-
|
|
895
|
-
const
|
|
896
|
-
|
|
815
|
+
if (trustInitialKey) return page2 ? [target] : start ? [start] : [];
|
|
816
|
+
const guard = chunkY4YNJ2EX_cjs.entryGuard(page2?.meta);
|
|
817
|
+
const ok = page2 && (!guard || chunkY4YNJ2EX_cjs.evaluateGate(guard, funnel.snapshot()));
|
|
818
|
+
const first = ok ? target : start;
|
|
819
|
+
return first ? [first] : [];
|
|
897
820
|
});
|
|
898
821
|
const currentKey = history[history.length - 1];
|
|
899
822
|
const index = history.length - 1;
|
|
900
823
|
const startKey = history[0];
|
|
901
|
-
const total =
|
|
902
|
-
() => expectedPathLength(flow, startKey, funnel.snapshot()),
|
|
824
|
+
const total = React.useMemo(
|
|
825
|
+
() => chunkY4YNJ2EX_cjs.expectedPathLength(flow, startKey, funnel.snapshot()),
|
|
903
826
|
// currentKey in deps: re-trace as each step's answers settle the branch.
|
|
904
827
|
[flow, startKey, currentKey, funnel]
|
|
905
828
|
);
|
|
906
829
|
const slug = pageByKey.get(currentKey)?.meta?.slug ?? currentKey;
|
|
907
|
-
const page =
|
|
830
|
+
const page = React.useMemo(
|
|
908
831
|
() => pageContextFor(currentKey, slug, index, total, funnel.context.system.now),
|
|
909
832
|
[currentKey, slug, index, total, funnel.context.system.now]
|
|
910
833
|
);
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
834
|
+
React.useEffect(() => {
|
|
835
|
+
funnel.context.page = page;
|
|
836
|
+
}, [funnel, page]);
|
|
837
|
+
const started = React.useRef(false);
|
|
838
|
+
React.useEffect(() => {
|
|
914
839
|
if (started.current) return;
|
|
915
840
|
started.current = true;
|
|
916
841
|
tracker.track("funnel.start", {});
|
|
917
842
|
}, [tracker]);
|
|
918
|
-
|
|
843
|
+
React.useEffect(() => {
|
|
919
844
|
if (!currentKey) return;
|
|
920
845
|
tracker.track("page.view", { pageId: currentKey, pageKey: currentKey, isInitial: index === 0, eventId: newEventId() });
|
|
921
846
|
}, [tracker, currentKey, index]);
|
|
922
|
-
|
|
847
|
+
React.useEffect(() => {
|
|
923
848
|
if (currentKey && onNavigate) onNavigate(currentKey, slug);
|
|
924
849
|
}, [onNavigate, currentKey, slug]);
|
|
925
|
-
const exposed =
|
|
926
|
-
|
|
850
|
+
const exposed = React.useRef(/* @__PURE__ */ new Set());
|
|
851
|
+
React.useEffect(() => {
|
|
927
852
|
const expId = experiments.experimentOf[currentKey];
|
|
928
853
|
if (!expId || exposed.current.has(expId)) return;
|
|
929
854
|
exposed.current.add(expId);
|
|
930
|
-
tracker.track("experiment.exposure",
|
|
855
|
+
tracker.track("experiment.exposure", exposurePayload(expId, experiments.assignments[expId], experiments.versionOf[expId]));
|
|
931
856
|
}, [tracker, currentKey, experiments]);
|
|
932
|
-
|
|
933
|
-
const
|
|
934
|
-
const
|
|
935
|
-
const
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
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 next = react.useCallback(() => {
|
|
964
|
-
const target = nextPage(flow, currentKey, funnel.snapshot());
|
|
965
|
-
const resolved = target ? experiments.slotOf[target] ?? target : target;
|
|
966
|
-
if (resolved) setHistory((h) => [...h, resolved]);
|
|
967
|
-
}, [flow, currentKey, funnel, experiments]);
|
|
968
|
-
const back = react.useCallback(
|
|
969
|
-
() => setHistory((h) => h.length > 1 ? h.slice(0, -1) : h),
|
|
857
|
+
usePageExitTimer(tracker, currentKey);
|
|
858
|
+
const [isNavigating, startNav] = React.useTransition();
|
|
859
|
+
const next = React.useCallback(() => {
|
|
860
|
+
const target = chunkY4YNJ2EX_cjs.nextPage(flow, currentKey, funnel.snapshot());
|
|
861
|
+
const resolved = target ? toSlot(target) : target;
|
|
862
|
+
if (resolved) startNav(() => setHistory((h) => [...h, resolved]));
|
|
863
|
+
}, [flow, currentKey, funnel, toSlot]);
|
|
864
|
+
const back = React.useCallback(
|
|
865
|
+
() => startNav(() => setHistory((h) => h.length > 1 ? h.slice(0, -1) : h)),
|
|
970
866
|
[]
|
|
971
867
|
);
|
|
972
|
-
const go =
|
|
973
|
-
(key) => setHistory((h) => [...h,
|
|
974
|
-
[
|
|
868
|
+
const go = React.useCallback(
|
|
869
|
+
(key) => startNav(() => setHistory((h) => [...h, toSlot(key)])),
|
|
870
|
+
[toSlot]
|
|
975
871
|
);
|
|
976
|
-
const prefetched =
|
|
977
|
-
const prefetch =
|
|
872
|
+
const prefetched = React.useRef(/* @__PURE__ */ new Set());
|
|
873
|
+
const prefetch = React.useCallback(
|
|
978
874
|
(key) => {
|
|
979
|
-
const target =
|
|
875
|
+
const target = toRenderKey(key);
|
|
980
876
|
const load = loaders.get(target);
|
|
981
877
|
if (!load || prefetched.current.has(target)) return;
|
|
982
878
|
prefetched.current.add(target);
|
|
983
879
|
load().catch(() => prefetched.current.delete(target));
|
|
984
880
|
},
|
|
985
|
-
[loaders,
|
|
881
|
+
[loaders, toRenderKey]
|
|
986
882
|
);
|
|
987
|
-
const nextCandidates =
|
|
988
|
-
() => [...new Set(outgoingKeys(flow, currentKey).map(
|
|
989
|
-
[flow, currentKey,
|
|
883
|
+
const nextCandidates = React.useMemo(
|
|
884
|
+
() => [...new Set(chunkY4YNJ2EX_cjs.outgoingKeys(flow, currentKey).map(toRenderKey))],
|
|
885
|
+
[flow, currentKey, toRenderKey]
|
|
990
886
|
);
|
|
991
|
-
|
|
887
|
+
React.useEffect(() => {
|
|
992
888
|
if (prefetchMode !== "auto") return;
|
|
993
889
|
for (const key of nextCandidates) prefetch(key);
|
|
994
890
|
}, [prefetchMode, nextCandidates, prefetch]);
|
|
@@ -999,38 +895,160 @@ function FunnelView({
|
|
|
999
895
|
go,
|
|
1000
896
|
prefetch,
|
|
1001
897
|
nextCandidates,
|
|
1002
|
-
canGoBack: history.length > 1
|
|
898
|
+
canGoBack: history.length > 1,
|
|
899
|
+
isNavigating
|
|
1003
900
|
};
|
|
1004
|
-
const
|
|
901
|
+
const bus = useBusInternal();
|
|
902
|
+
const navRef = React.useRef(value);
|
|
903
|
+
navRef.current = value;
|
|
904
|
+
React.useEffect(() => {
|
|
905
|
+
if (!bus) return;
|
|
906
|
+
const control = {
|
|
907
|
+
goTo: (key) => {
|
|
908
|
+
if (!components.has(toRenderKey(key))) return false;
|
|
909
|
+
navRef.current.go(key);
|
|
910
|
+
return true;
|
|
911
|
+
},
|
|
912
|
+
goNext: () => navRef.current.next(),
|
|
913
|
+
goPrevious: () => navRef.current.back(),
|
|
914
|
+
snapshot: () => {
|
|
915
|
+
const v = navRef.current;
|
|
916
|
+
return {
|
|
917
|
+
key: v.page.key,
|
|
918
|
+
index: v.page.index,
|
|
919
|
+
total: v.page.total,
|
|
920
|
+
progressPercentage: v.page.progressPercentage,
|
|
921
|
+
canGoBack: v.canGoBack,
|
|
922
|
+
nextCandidates: v.nextCandidates
|
|
923
|
+
};
|
|
924
|
+
}
|
|
925
|
+
};
|
|
926
|
+
return bus.registerNavigation(control);
|
|
927
|
+
}, [bus]);
|
|
928
|
+
React.useEffect(() => {
|
|
929
|
+
const v = navRef.current;
|
|
930
|
+
bus?.emit("navigation", {
|
|
931
|
+
key: v.page.key,
|
|
932
|
+
index: v.page.index,
|
|
933
|
+
total: v.page.total,
|
|
934
|
+
progressPercentage: v.page.progressPercentage,
|
|
935
|
+
canGoBack: v.canGoBack,
|
|
936
|
+
nextCandidates: v.nextCandidates
|
|
937
|
+
});
|
|
938
|
+
}, [bus, currentKey]);
|
|
939
|
+
const renderKey = toRenderKey(currentKey);
|
|
1005
940
|
const Current = components.get(renderKey);
|
|
1006
|
-
const pageEl =
|
|
1007
|
-
const content = layout ?
|
|
1008
|
-
return
|
|
941
|
+
const pageEl = React.createElement(React.Suspense, { fallback: fallback ?? null }, Current ? React.createElement(Current) : null);
|
|
942
|
+
const content = layout ? React.createElement(layout, null, pageEl) : pageEl;
|
|
943
|
+
return React.createElement(
|
|
1009
944
|
ExperimentContext.Provider,
|
|
1010
945
|
{ value: experiments.assignments },
|
|
1011
|
-
|
|
946
|
+
React.createElement(NavContext.Provider, { value }, content)
|
|
1012
947
|
);
|
|
1013
948
|
}
|
|
1014
949
|
function useNavigation() {
|
|
1015
|
-
const ctx =
|
|
950
|
+
const ctx = React.useContext(NavContext);
|
|
1016
951
|
if (!ctx) throw new Error("useNavigation must be used inside <FunnelView>");
|
|
1017
952
|
return ctx;
|
|
1018
953
|
}
|
|
954
|
+
var EMBED_CAPABILITIES = {
|
|
955
|
+
methods: ["nav.snapshot", "nav.goTo", "nav.goNext", "nav.goPrevious", "screenshot"],
|
|
956
|
+
events: ["navigation"]
|
|
957
|
+
};
|
|
958
|
+
function EmbedBridge() {
|
|
959
|
+
const bus = useBusInternal();
|
|
960
|
+
React.useEffect(() => {
|
|
961
|
+
if (!bus || typeof window === "undefined" || window.parent === window) return;
|
|
962
|
+
const parent = window.parent;
|
|
963
|
+
let disposed = false;
|
|
964
|
+
const send = (msg) => {
|
|
965
|
+
try {
|
|
966
|
+
parent.postMessage({ af: chunkWYUDL4FI_cjs.AF_PROTOCOL, source: "af-preview", ...msg }, "*");
|
|
967
|
+
} catch {
|
|
968
|
+
}
|
|
969
|
+
};
|
|
970
|
+
const hello = () => send({ kind: "hello", role: "funnel", protocol: chunkWYUDL4FI_cjs.AF_PROTOCOL, capabilities: EMBED_CAPABILITIES });
|
|
971
|
+
const sendSnapshot = () => {
|
|
972
|
+
const s = bus.navigation();
|
|
973
|
+
if (s) send({ kind: "event", event: "navigation", data: s });
|
|
974
|
+
};
|
|
975
|
+
async function invoke(method, params) {
|
|
976
|
+
switch (method) {
|
|
977
|
+
case "nav.snapshot":
|
|
978
|
+
return bus.navigation();
|
|
979
|
+
case "nav.goTo": {
|
|
980
|
+
const key = params?.key;
|
|
981
|
+
if (typeof key !== "string" || !bus.goTo(key)) throw { code: "bad_params", message: `unknown page key: ${String(key)}` };
|
|
982
|
+
return void 0;
|
|
983
|
+
}
|
|
984
|
+
case "nav.goNext":
|
|
985
|
+
bus.goNext();
|
|
986
|
+
return void 0;
|
|
987
|
+
case "nav.goPrevious":
|
|
988
|
+
bus.goPrevious();
|
|
989
|
+
return void 0;
|
|
990
|
+
case "screenshot":
|
|
991
|
+
return await captureScreenshot(params ?? {});
|
|
992
|
+
default:
|
|
993
|
+
throw { code: "unknown_method", message: `unknown method: ${method}` };
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
async function onMsg(e) {
|
|
997
|
+
if (e.source !== parent) return;
|
|
998
|
+
const d = e.data;
|
|
999
|
+
if (!d || typeof d !== "object" || d.af !== chunkWYUDL4FI_cjs.AF_PROTOCOL || d.source !== "af-host") return;
|
|
1000
|
+
if (d.kind === "hello") {
|
|
1001
|
+
hello();
|
|
1002
|
+
sendSnapshot();
|
|
1003
|
+
return;
|
|
1004
|
+
}
|
|
1005
|
+
if (d.kind !== "request" || typeof d.id !== "string") return;
|
|
1006
|
+
try {
|
|
1007
|
+
const result = await invoke(d.method, d.params);
|
|
1008
|
+
if (!disposed) send({ kind: "response", id: d.id, ok: true, result });
|
|
1009
|
+
} catch (err) {
|
|
1010
|
+
const e2 = err;
|
|
1011
|
+
const error = e2 && typeof e2.code === "string" ? { code: e2.code, message: String(e2.message ?? e2.code) } : { code: "failed", message: String(e2?.message ?? err) };
|
|
1012
|
+
if (!disposed) send({ kind: "response", id: d.id, ok: false, error });
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
window.addEventListener("message", onMsg);
|
|
1016
|
+
const offNav = bus.on("navigation", (data) => {
|
|
1017
|
+
if (!disposed) send({ kind: "event", event: "navigation", data });
|
|
1018
|
+
});
|
|
1019
|
+
hello();
|
|
1020
|
+
sendSnapshot();
|
|
1021
|
+
return () => {
|
|
1022
|
+
disposed = true;
|
|
1023
|
+
offNav();
|
|
1024
|
+
window.removeEventListener("message", onMsg);
|
|
1025
|
+
};
|
|
1026
|
+
}, [bus]);
|
|
1027
|
+
return null;
|
|
1028
|
+
}
|
|
1019
1029
|
function usePage() {
|
|
1020
1030
|
return useNavigation().page;
|
|
1021
1031
|
}
|
|
1022
|
-
var symModalId = /* @__PURE__ */ Symbol("
|
|
1032
|
+
var symModalId = /* @__PURE__ */ Symbol("AppfunnelModalId");
|
|
1023
1033
|
var initialState = {};
|
|
1024
|
-
var ModalStateContext =
|
|
1025
|
-
var ModalIdContext =
|
|
1034
|
+
var ModalStateContext = React.createContext(initialState);
|
|
1035
|
+
var ModalIdContext = React.createContext(null);
|
|
1036
|
+
var ModalDispatchContext = React.createContext(null);
|
|
1026
1037
|
var REGISTRY = {};
|
|
1027
1038
|
var ALREADY_MOUNTED = {};
|
|
1028
1039
|
var modalCallbacks = {};
|
|
1029
1040
|
var hideCallbacks = {};
|
|
1030
1041
|
var uid = 0;
|
|
1031
1042
|
var getUid = () => `_af_modal_${uid++}`;
|
|
1032
|
-
var
|
|
1033
|
-
|
|
1043
|
+
var runtimeStack = [];
|
|
1044
|
+
var dispatch = (action) => {
|
|
1045
|
+
const top = runtimeStack[runtimeStack.length - 1];
|
|
1046
|
+
if (!top) {
|
|
1047
|
+
throw new Error(
|
|
1048
|
+
"No modal dispatch \u2014 render inside <FunnelProvider> (which mounts the modal runtime)."
|
|
1049
|
+
);
|
|
1050
|
+
}
|
|
1051
|
+
top(action);
|
|
1034
1052
|
};
|
|
1035
1053
|
var modalReducer = (state = initialState, action) => {
|
|
1036
1054
|
const { modalId, args, flags } = action.payload;
|
|
@@ -1073,10 +1091,10 @@ function registerModal(id, comp, props) {
|
|
|
1073
1091
|
function unregisterModal(id) {
|
|
1074
1092
|
delete REGISTRY[id];
|
|
1075
1093
|
}
|
|
1076
|
-
function
|
|
1094
|
+
function showModalVia(d, modal, args) {
|
|
1077
1095
|
const id = idOf(modal);
|
|
1078
1096
|
if (typeof modal !== "string" && !REGISTRY[id]) registerModal(id, modal);
|
|
1079
|
-
|
|
1097
|
+
d({ type: "show", payload: { modalId: id, args } });
|
|
1080
1098
|
if (!modalCallbacks[id]) {
|
|
1081
1099
|
let res;
|
|
1082
1100
|
let rej;
|
|
@@ -1088,9 +1106,12 @@ function showModal(modal, args) {
|
|
|
1088
1106
|
}
|
|
1089
1107
|
return modalCallbacks[id].promise;
|
|
1090
1108
|
}
|
|
1091
|
-
function
|
|
1109
|
+
function showModal(modal, args) {
|
|
1110
|
+
return showModalVia(dispatch, modal, args);
|
|
1111
|
+
}
|
|
1112
|
+
function hideModalVia(d, modal) {
|
|
1092
1113
|
const id = idOf(modal);
|
|
1093
|
-
|
|
1114
|
+
d({ type: "hide", payload: { modalId: id } });
|
|
1094
1115
|
delete modalCallbacks[id];
|
|
1095
1116
|
if (!hideCallbacks[id]) {
|
|
1096
1117
|
let res;
|
|
@@ -1103,41 +1124,64 @@ function hideModal(modal) {
|
|
|
1103
1124
|
}
|
|
1104
1125
|
return hideCallbacks[id].promise;
|
|
1105
1126
|
}
|
|
1106
|
-
function
|
|
1127
|
+
function hideModal(modal) {
|
|
1128
|
+
return hideModalVia(dispatch, modal);
|
|
1129
|
+
}
|
|
1130
|
+
function removeModalVia(d, modal) {
|
|
1107
1131
|
const id = idOf(modal);
|
|
1108
|
-
|
|
1132
|
+
d({ type: "remove", payload: { modalId: id } });
|
|
1109
1133
|
delete modalCallbacks[id];
|
|
1110
1134
|
delete hideCallbacks[id];
|
|
1111
1135
|
}
|
|
1112
|
-
function
|
|
1113
|
-
dispatch
|
|
1136
|
+
function removeModal(modal) {
|
|
1137
|
+
removeModalVia(dispatch, modal);
|
|
1138
|
+
}
|
|
1139
|
+
function setFlags(d, id, flags) {
|
|
1140
|
+
d({ type: "set-flags", payload: { modalId: id, flags } });
|
|
1114
1141
|
}
|
|
1115
1142
|
function useModal(modal, args) {
|
|
1116
|
-
const modals =
|
|
1117
|
-
const contextId =
|
|
1143
|
+
const modals = React.useContext(ModalStateContext);
|
|
1144
|
+
const contextId = React.useContext(ModalIdContext);
|
|
1145
|
+
const d = React.useContext(ModalDispatchContext) ?? dispatch;
|
|
1118
1146
|
const isComponent = !!modal && typeof modal !== "string";
|
|
1119
1147
|
const id = modal ? idOf(modal) : contextId;
|
|
1120
|
-
if (!id)
|
|
1121
|
-
|
|
1122
|
-
|
|
1148
|
+
if (!id)
|
|
1149
|
+
throw new Error(
|
|
1150
|
+
"useModal: no modal id (call inside a defineModal component, or pass an id/component)."
|
|
1151
|
+
);
|
|
1152
|
+
React.useEffect(() => {
|
|
1153
|
+
if (isComponent && !REGISTRY[id])
|
|
1154
|
+
registerModal(id, modal, args);
|
|
1123
1155
|
}, [isComponent, id, modal, args]);
|
|
1124
1156
|
const info = modals[id];
|
|
1125
|
-
const show =
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1157
|
+
const show = React.useCallback(
|
|
1158
|
+
(a) => showModalVia(d, id, a),
|
|
1159
|
+
[d, id]
|
|
1160
|
+
);
|
|
1161
|
+
const hide = React.useCallback(() => hideModalVia(d, id), [d, id]);
|
|
1162
|
+
const remove = React.useCallback(() => removeModalVia(d, id), [d, id]);
|
|
1163
|
+
const resolve = React.useCallback(
|
|
1164
|
+
(v) => {
|
|
1165
|
+
modalCallbacks[id]?.resolve(v);
|
|
1166
|
+
delete modalCallbacks[id];
|
|
1167
|
+
},
|
|
1168
|
+
[id]
|
|
1169
|
+
);
|
|
1170
|
+
const reject = React.useCallback(
|
|
1171
|
+
(v) => {
|
|
1172
|
+
modalCallbacks[id]?.reject(v);
|
|
1173
|
+
delete modalCallbacks[id];
|
|
1174
|
+
},
|
|
1175
|
+
[id]
|
|
1176
|
+
);
|
|
1177
|
+
const resolveHide = React.useCallback(
|
|
1178
|
+
(v) => {
|
|
1179
|
+
hideCallbacks[id]?.resolve(v);
|
|
1180
|
+
delete hideCallbacks[id];
|
|
1181
|
+
},
|
|
1182
|
+
[id]
|
|
1183
|
+
);
|
|
1184
|
+
return React.useMemo(
|
|
1141
1185
|
() => ({
|
|
1142
1186
|
id,
|
|
1143
1187
|
args: info?.args,
|
|
@@ -1150,53 +1194,90 @@ function useModal(modal, args) {
|
|
|
1150
1194
|
reject,
|
|
1151
1195
|
resolveHide
|
|
1152
1196
|
}),
|
|
1153
|
-
[
|
|
1197
|
+
[
|
|
1198
|
+
id,
|
|
1199
|
+
info?.args,
|
|
1200
|
+
info?.visible,
|
|
1201
|
+
info?.keepMounted,
|
|
1202
|
+
show,
|
|
1203
|
+
hide,
|
|
1204
|
+
remove,
|
|
1205
|
+
resolve,
|
|
1206
|
+
reject,
|
|
1207
|
+
resolveHide
|
|
1208
|
+
]
|
|
1154
1209
|
);
|
|
1155
1210
|
}
|
|
1156
1211
|
function defineModal(Comp) {
|
|
1157
|
-
return function Wrapped({
|
|
1212
|
+
return function Wrapped({
|
|
1213
|
+
id,
|
|
1214
|
+
defaultVisible,
|
|
1215
|
+
keepMounted,
|
|
1216
|
+
...props
|
|
1217
|
+
}) {
|
|
1158
1218
|
const { args, show } = useModal(id);
|
|
1159
|
-
const modals =
|
|
1219
|
+
const modals = React.useContext(ModalStateContext);
|
|
1220
|
+
const d = React.useContext(ModalDispatchContext) ?? dispatch;
|
|
1160
1221
|
const shouldMount = !!modals[id];
|
|
1161
|
-
|
|
1222
|
+
React.useEffect(() => {
|
|
1162
1223
|
if (defaultVisible) show();
|
|
1163
1224
|
ALREADY_MOUNTED[id] = true;
|
|
1164
1225
|
return () => {
|
|
1165
1226
|
delete ALREADY_MOUNTED[id];
|
|
1166
1227
|
};
|
|
1167
1228
|
}, [id, show, defaultVisible]);
|
|
1168
|
-
|
|
1169
|
-
if (keepMounted) setFlags(id, { keepMounted: true });
|
|
1170
|
-
}, [id, keepMounted]);
|
|
1229
|
+
React.useEffect(() => {
|
|
1230
|
+
if (keepMounted) setFlags(d, id, { keepMounted: true });
|
|
1231
|
+
}, [d, id, keepMounted]);
|
|
1171
1232
|
const delayVisible = modals[id]?.delayVisible;
|
|
1172
|
-
|
|
1233
|
+
React.useEffect(() => {
|
|
1173
1234
|
if (delayVisible) show(args);
|
|
1174
1235
|
}, [delayVisible, args, show]);
|
|
1175
1236
|
if (!shouldMount) return null;
|
|
1176
|
-
return
|
|
1237
|
+
return React.createElement(
|
|
1177
1238
|
ModalIdContext.Provider,
|
|
1178
1239
|
{ value: id },
|
|
1179
|
-
|
|
1240
|
+
React.createElement(Comp, { ...props, ...args })
|
|
1180
1241
|
);
|
|
1181
1242
|
};
|
|
1182
1243
|
}
|
|
1183
1244
|
function ModalRoot() {
|
|
1184
|
-
const modals =
|
|
1245
|
+
const modals = React.useContext(ModalStateContext);
|
|
1185
1246
|
const toRender = Object.keys(modals).filter((id) => REGISTRY[id]).map((id) => ({ id, ...REGISTRY[id] }));
|
|
1186
|
-
return
|
|
1247
|
+
return React.createElement(
|
|
1187
1248
|
"div",
|
|
1188
1249
|
{ "data-appfunnel-modal-root": "" },
|
|
1189
|
-
...toRender.map(
|
|
1250
|
+
...toRender.map(
|
|
1251
|
+
(t) => React.createElement(t.comp, { key: t.id, id: t.id, ...t.props })
|
|
1252
|
+
)
|
|
1190
1253
|
);
|
|
1191
1254
|
}
|
|
1192
1255
|
function ModalRuntime({ children }) {
|
|
1193
|
-
const [state, localDispatch] =
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1256
|
+
const [state, localDispatch] = React.useReducer(modalReducer, initialState);
|
|
1257
|
+
React.useEffect(() => {
|
|
1258
|
+
runtimeStack.push(localDispatch);
|
|
1259
|
+
if (runtimeStack.length > 1) {
|
|
1260
|
+
try {
|
|
1261
|
+
console.warn(
|
|
1262
|
+
`[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.`
|
|
1263
|
+
);
|
|
1264
|
+
} catch {
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
return () => {
|
|
1268
|
+
const i = runtimeStack.indexOf(localDispatch);
|
|
1269
|
+
if (i !== -1) runtimeStack.splice(i, 1);
|
|
1270
|
+
};
|
|
1271
|
+
}, []);
|
|
1272
|
+
return React.createElement(
|
|
1273
|
+
ModalDispatchContext.Provider,
|
|
1274
|
+
{ value: localDispatch },
|
|
1275
|
+
React.createElement(
|
|
1276
|
+
ModalStateContext.Provider,
|
|
1277
|
+
{ value: state },
|
|
1278
|
+
children,
|
|
1279
|
+
React.createElement(ModalRoot, null)
|
|
1280
|
+
)
|
|
1200
1281
|
);
|
|
1201
1282
|
}
|
|
1202
1283
|
|
|
@@ -1223,7 +1304,7 @@ function OverlayShell({
|
|
|
1223
1304
|
panel,
|
|
1224
1305
|
children
|
|
1225
1306
|
}) {
|
|
1226
|
-
|
|
1307
|
+
React.useEffect(() => {
|
|
1227
1308
|
if (!open || typeof document === "undefined") return;
|
|
1228
1309
|
const onKey = (e) => {
|
|
1229
1310
|
if (e.key === "Escape") onClose();
|
|
@@ -1237,7 +1318,7 @@ function OverlayShell({
|
|
|
1237
1318
|
};
|
|
1238
1319
|
}, [open, onClose]);
|
|
1239
1320
|
if (!open || typeof document === "undefined") return null;
|
|
1240
|
-
const overlay =
|
|
1321
|
+
const overlay = React.createElement(
|
|
1241
1322
|
"div",
|
|
1242
1323
|
{
|
|
1243
1324
|
role: "dialog",
|
|
@@ -1246,19 +1327,19 @@ function OverlayShell({
|
|
|
1246
1327
|
"data-appfunnel-overlay": "",
|
|
1247
1328
|
style: { position: "fixed", inset: 0, zIndex: 1e3, display: "flex", ...container }
|
|
1248
1329
|
},
|
|
1249
|
-
|
|
1330
|
+
React.createElement("div", {
|
|
1250
1331
|
"data-appfunnel-overlay-backdrop": "",
|
|
1251
1332
|
className: backdropClassName,
|
|
1252
1333
|
onClick: dismissOnBackdrop ? onClose : void 0,
|
|
1253
1334
|
style: { position: "absolute", inset: 0, background: "rgba(0,0,0,0.5)" }
|
|
1254
1335
|
}),
|
|
1255
|
-
|
|
1336
|
+
React.createElement(
|
|
1256
1337
|
"div",
|
|
1257
1338
|
{ "data-appfunnel-overlay-panel": "", className, style: { position: "relative", background: "#fff", ...panel } },
|
|
1258
1339
|
children
|
|
1259
1340
|
)
|
|
1260
1341
|
);
|
|
1261
|
-
return
|
|
1342
|
+
return ReactDOM.createPortal(overlay, document.body);
|
|
1262
1343
|
}
|
|
1263
1344
|
var SHEET_LAYOUT = {
|
|
1264
1345
|
bottom: { container: { justifyContent: "flex-end", alignItems: "stretch" }, panel: { borderTopLeftRadius: 16, borderTopRightRadius: 16, padding: 16, maxHeight: "90vh", overflow: "auto" } },
|
|
@@ -1269,7 +1350,7 @@ var SHEET_LAYOUT = {
|
|
|
1269
1350
|
function Sheet(props) {
|
|
1270
1351
|
const control = useModalControl();
|
|
1271
1352
|
const layout = SHEET_LAYOUT[props.side ?? "bottom"];
|
|
1272
|
-
return
|
|
1353
|
+
return React.createElement(OverlayShell, {
|
|
1273
1354
|
...control,
|
|
1274
1355
|
className: props.className,
|
|
1275
1356
|
backdropClassName: props.backdropClassName,
|
|
@@ -1282,7 +1363,7 @@ function Sheet(props) {
|
|
|
1282
1363
|
}
|
|
1283
1364
|
function Modal(props) {
|
|
1284
1365
|
const control = useModalControl();
|
|
1285
|
-
return
|
|
1366
|
+
return React.createElement(OverlayShell, {
|
|
1286
1367
|
...control,
|
|
1287
1368
|
className: props.className,
|
|
1288
1369
|
backdropClassName: props.backdropClassName,
|
|
@@ -1295,263 +1376,412 @@ function Modal(props) {
|
|
|
1295
1376
|
}
|
|
1296
1377
|
|
|
1297
1378
|
// src/commerce/checkout.tsx
|
|
1298
|
-
var INLINE_SURFACES = /* @__PURE__ */ new Set([
|
|
1299
|
-
"express",
|
|
1300
|
-
"element",
|
|
1301
|
-
"embedded"
|
|
1302
|
-
]);
|
|
1303
|
-
function isInlineSurface(surface) {
|
|
1304
|
-
return INLINE_SURFACES.has(surface);
|
|
1305
|
-
}
|
|
1306
|
-
var ON = { purchase: true, wallets: true, offSession: "reliable" };
|
|
1307
|
-
var MANAGED = { purchase: true, wallets: true, offSession: "conditional" };
|
|
1308
|
-
var ALL_UPSELLS = ["subscription", "one-time", "upgrade"];
|
|
1309
|
-
var PROVIDER_PROFILES = {
|
|
1310
|
-
// PSP; you hold the PaymentMethod and fire your own off-session PaymentIntent on
|
|
1311
|
-
// the Elements surfaces (reliable). **Managed** Checkout (`embedded`/`redirect`)
|
|
1312
|
-
// upsells too, but as a direct charge against Stripe's vaulted card → conditional.
|
|
1313
|
-
// No provider "popup" surface.
|
|
1314
|
-
stripe: {
|
|
1315
|
-
isMoR: false,
|
|
1316
|
-
offSessionUpsells: ALL_UPSELLS,
|
|
1317
|
-
tokenModel: "merchant",
|
|
1318
|
-
orchestrator: false,
|
|
1319
|
-
surfaces: { express: ON, element: ON, sheet: ON, embedded: MANAGED, redirect: MANAGED }
|
|
1320
|
-
},
|
|
1321
|
-
// Merchant-of-Record; card entry is always Paddle's frame → no own element/sheet/
|
|
1322
|
-
// express. Off-session via subscription one-time charge / collection_mode, but
|
|
1323
|
-
// **no 2nd concurrent subscription** (upgrade + one-time only), provider-initiated.
|
|
1324
|
-
paddle: {
|
|
1325
|
-
isMoR: true,
|
|
1326
|
-
offSessionUpsells: ["one-time", "upgrade"],
|
|
1327
|
-
tokenModel: "provider",
|
|
1328
|
-
orchestrator: false,
|
|
1329
|
-
surfaces: { embedded: ON, popup: ON, redirect: ON }
|
|
1330
|
-
},
|
|
1331
|
-
// Merchant-of-Record; iframe-only (no raw card field). Off-session works
|
|
1332
|
-
// (provider-initiated, async via webhooks). `sheet` = our chrome hosting their embed.
|
|
1333
|
-
whop: {
|
|
1334
|
-
isMoR: true,
|
|
1335
|
-
offSessionUpsells: ALL_UPSELLS,
|
|
1336
|
-
tokenModel: "provider",
|
|
1337
|
-
orchestrator: false,
|
|
1338
|
-
surfaces: { embedded: ON, redirect: ON, sheet: ON }
|
|
1339
|
-
},
|
|
1340
|
-
// Pure orchestrator (PSP-side); Headless renders express/element/sheet/embedded.
|
|
1341
|
-
// No hosted redirect page, no popup checkout. Off-session is PSP-conditional.
|
|
1342
|
-
primer: {
|
|
1343
|
-
isMoR: false,
|
|
1344
|
-
offSessionUpsells: ALL_UPSELLS,
|
|
1345
|
-
tokenModel: "merchant",
|
|
1346
|
-
orchestrator: true,
|
|
1347
|
-
surfaces: { express: ON, element: ON, sheet: ON, embedded: ON }
|
|
1348
|
-
},
|
|
1349
|
-
// PSP **and** orchestrator (own acquiring + connectors routing across Stripe/Adyen/
|
|
1350
|
-
// PayPal). One Payment Form (element/sheet/embedded) + hosted page + wallet button.
|
|
1351
|
-
// Strong native off-session (Recurring API). No popup.
|
|
1352
|
-
solidgate: {
|
|
1353
|
-
isMoR: false,
|
|
1354
|
-
offSessionUpsells: ALL_UPSELLS,
|
|
1355
|
-
tokenModel: "merchant",
|
|
1356
|
-
orchestrator: true,
|
|
1357
|
-
surfaces: { express: ON, element: ON, sheet: ON, embedded: ON, redirect: ON }
|
|
1358
|
-
}
|
|
1359
|
-
};
|
|
1360
|
-
function surfacesFor(provider) {
|
|
1361
|
-
return Object.keys(PROVIDER_PROFILES[provider]?.surfaces ?? {});
|
|
1362
|
-
}
|
|
1363
|
-
function isMerchantOfRecord(provider) {
|
|
1364
|
-
return PROVIDER_PROFILES[provider]?.isMoR ?? false;
|
|
1365
|
-
}
|
|
1366
|
-
function isOrchestrator(provider) {
|
|
1367
|
-
return PROVIDER_PROFILES[provider]?.orchestrator ?? false;
|
|
1368
|
-
}
|
|
1369
|
-
function validateCheckout(provider, surface) {
|
|
1370
|
-
const cap = PROVIDER_PROFILES[provider]?.surfaces[surface];
|
|
1371
|
-
if (!cap) return { ok: false, reason: `${provider} has no '${surface}' surface` };
|
|
1372
|
-
return cap.purchase ? { ok: true } : { ok: false, reason: `'${surface}' can't take a purchase on ${provider}` };
|
|
1373
|
-
}
|
|
1374
|
-
function validateUpsell(provider, paywallSurface, kind = "subscription") {
|
|
1375
|
-
const profile = PROVIDER_PROFILES[provider];
|
|
1376
|
-
if (!profile) return { ok: false, reason: `unknown provider '${provider}'` };
|
|
1377
|
-
if (!profile.offSessionUpsells.includes(kind)) {
|
|
1378
|
-
const can = profile.offSessionUpsells.length ? ` (it supports: ${profile.offSessionUpsells.join(", ")})` : "";
|
|
1379
|
-
return { ok: false, reason: `${provider} can't charge a '${kind}' upsell off-session${can}` };
|
|
1380
|
-
}
|
|
1381
|
-
const cap = profile.surfaces[paywallSurface];
|
|
1382
|
-
if (!cap) return { ok: false, reason: `${provider} has no '${paywallSurface}' surface to upsell after` };
|
|
1383
|
-
if (cap.offSession === "none") {
|
|
1384
|
-
return { ok: false, reason: `a purchase on '${paywallSurface}' retains no chargeable method for an upsell` };
|
|
1385
|
-
}
|
|
1386
|
-
if (cap.offSession === "conditional") {
|
|
1387
|
-
return { ok: true, note: `off-session upsell after ${provider} '${paywallSurface}' (managed checkout) may decline \u2014 prefer element/sheet/express` };
|
|
1388
|
-
}
|
|
1389
|
-
return { ok: true };
|
|
1390
|
-
}
|
|
1391
|
-
function checkoutError(category, message, extra = {}) {
|
|
1392
|
-
const retryable = extra.retryable ?? (category === "authentication_required" || category === "requires_payment_method" || category === "processing_error");
|
|
1393
|
-
return { category, message, declineCode: extra.declineCode, retryable };
|
|
1394
|
-
}
|
|
1395
1379
|
function createMockDriver(provider = "stripe", options = {}) {
|
|
1396
|
-
const success = options.result ?? {
|
|
1397
|
-
|
|
1380
|
+
const success = options.result ?? {
|
|
1381
|
+
ok: true,
|
|
1382
|
+
amountMinor: 0,
|
|
1383
|
+
currency: "USD"
|
|
1384
|
+
};
|
|
1385
|
+
const offError = typeof options.failOffSession === "object" ? options.failOffSession : chunk7JLOF6CJ_cjs.checkoutError(
|
|
1386
|
+
"authentication_required",
|
|
1387
|
+
"off-session charge declined (mock)"
|
|
1388
|
+
);
|
|
1398
1389
|
return {
|
|
1399
1390
|
provider,
|
|
1400
1391
|
start: (req) => {
|
|
1401
|
-
if (options.failOffSession && !req.surface)
|
|
1392
|
+
if (options.failOffSession && !req.surface)
|
|
1393
|
+
return Promise.resolve({ ok: false, error: offError });
|
|
1402
1394
|
return Promise.resolve(success);
|
|
1403
1395
|
},
|
|
1404
|
-
renderInline: (req, cb) =>
|
|
1396
|
+
renderInline: (req, cb) => React.createElement(
|
|
1405
1397
|
"button",
|
|
1406
1398
|
{
|
|
1407
1399
|
type: "button",
|
|
1408
1400
|
"data-mock-surface": req.surface,
|
|
1409
|
-
onClick: () =>
|
|
1401
|
+
onClick: () => {
|
|
1402
|
+
cb.onStart?.();
|
|
1403
|
+
if (success.ok) cb.onSuccess(success);
|
|
1404
|
+
else
|
|
1405
|
+
cb.onError(
|
|
1406
|
+
success.error ?? chunk7JLOF6CJ_cjs.checkoutError(
|
|
1407
|
+
"processing_error",
|
|
1408
|
+
"failed (mock)"
|
|
1409
|
+
)
|
|
1410
|
+
);
|
|
1411
|
+
}
|
|
1410
1412
|
},
|
|
1411
1413
|
`Pay (mock ${req.surface})`
|
|
1412
|
-
)
|
|
1414
|
+
),
|
|
1415
|
+
resume: () => Promise.resolve(success)
|
|
1416
|
+
};
|
|
1417
|
+
}
|
|
1418
|
+
function driverWithEmail(driver, getEmail) {
|
|
1419
|
+
const enrich = (req) => req.email !== void 0 ? req : { ...req, email: getEmail() };
|
|
1420
|
+
return {
|
|
1421
|
+
...driver,
|
|
1422
|
+
start: (req) => driver.start(enrich(req)),
|
|
1423
|
+
renderInline: driver.renderInline ? (req, cb) => driver.renderInline(enrich(req), cb) : void 0
|
|
1424
|
+
};
|
|
1425
|
+
}
|
|
1426
|
+
function driverWithCatalog(driver, catalog) {
|
|
1427
|
+
const enrich = (req) => req.catalogKey !== void 0 ? req : { ...req, catalogKey: catalog.get(req.product)?.catalogKey };
|
|
1428
|
+
return {
|
|
1429
|
+
...driver,
|
|
1430
|
+
start: (req) => driver.start(enrich(req)),
|
|
1431
|
+
renderInline: driver.renderInline ? (req, cb) => driver.renderInline(enrich(req), cb) : void 0
|
|
1413
1432
|
};
|
|
1414
1433
|
}
|
|
1415
|
-
var DriverContext =
|
|
1434
|
+
var DriverContext = React.createContext(null);
|
|
1416
1435
|
function CheckoutDriverProvider({
|
|
1417
1436
|
driver,
|
|
1418
1437
|
children
|
|
1419
1438
|
}) {
|
|
1420
|
-
return
|
|
1439
|
+
return React.createElement(DriverContext.Provider, { value: driver }, children);
|
|
1421
1440
|
}
|
|
1422
1441
|
function useDriver() {
|
|
1423
|
-
return
|
|
1442
|
+
return React.useContext(DriverContext) ?? FALLBACK_DRIVER;
|
|
1424
1443
|
}
|
|
1425
1444
|
var FALLBACK_DRIVER = createMockDriver();
|
|
1426
1445
|
function useCheckout(opts = {}) {
|
|
1427
1446
|
const driver = useDriver();
|
|
1428
1447
|
const tracker = useTrackerRef();
|
|
1448
|
+
const catalog = useCatalog();
|
|
1429
1449
|
const nav = useNavigation();
|
|
1430
|
-
const [status, setStatus] =
|
|
1431
|
-
const [error, setError] =
|
|
1450
|
+
const [status, setStatus] = React.useState("idle");
|
|
1451
|
+
const [error, setError] = React.useState(null);
|
|
1432
1452
|
const intent = opts.intent ?? "purchase";
|
|
1433
1453
|
const kind = opts.kind;
|
|
1434
1454
|
const advance = opts.advanceOnSuccess ?? true;
|
|
1435
|
-
const
|
|
1436
|
-
(
|
|
1455
|
+
const productFacts = React.useCallback(
|
|
1456
|
+
(product) => {
|
|
1457
|
+
const resolved = product ? catalog.get(product) : void 0;
|
|
1458
|
+
return {
|
|
1459
|
+
productId: resolved?.catalogKey ?? product,
|
|
1460
|
+
amount: resolved ? resolved.priceMinor / 100 : void 0,
|
|
1461
|
+
currency: resolved?.currency,
|
|
1462
|
+
isSubscription: resolved ? resolved.period !== "one-time" : kind === "subscription" || kind === "upgrade"
|
|
1463
|
+
};
|
|
1464
|
+
},
|
|
1465
|
+
[catalog, kind]
|
|
1466
|
+
);
|
|
1467
|
+
const recovering = React.useRef(false);
|
|
1468
|
+
const failRef = React.useRef(
|
|
1469
|
+
() => {
|
|
1470
|
+
}
|
|
1471
|
+
);
|
|
1472
|
+
const attemptRef = React.useRef(async () => ({ ok: false }));
|
|
1473
|
+
const succeed = React.useCallback(
|
|
1474
|
+
(result, product) => {
|
|
1437
1475
|
setStatus("success");
|
|
1476
|
+
const { productId, isSubscription } = productFacts(product);
|
|
1477
|
+
const amount = result.amountMinor != null ? result.amountMinor / 100 : void 0;
|
|
1438
1478
|
tracker.track("purchase.complete", {
|
|
1439
|
-
amount
|
|
1479
|
+
amount,
|
|
1440
1480
|
currency: result.currency,
|
|
1481
|
+
productId,
|
|
1441
1482
|
eventId: result.eventId
|
|
1442
1483
|
// server-minted (browser↔CAPI dedup); undefined if none
|
|
1443
1484
|
});
|
|
1485
|
+
if (isSubscription) {
|
|
1486
|
+
tracker.track("subscription.created", {
|
|
1487
|
+
amount,
|
|
1488
|
+
currency: result.currency,
|
|
1489
|
+
productId,
|
|
1490
|
+
eventId: result.eventId
|
|
1491
|
+
});
|
|
1492
|
+
}
|
|
1444
1493
|
opts.onSuccess?.(result);
|
|
1445
1494
|
if (advance) nav.next();
|
|
1446
1495
|
},
|
|
1447
|
-
[tracker, nav, advance, opts]
|
|
1448
|
-
);
|
|
1449
|
-
const fail = react.useCallback(
|
|
1450
|
-
(err, product) => {
|
|
1451
|
-
setStatus(err.category === "authentication_required" ? "requires_action" : "error");
|
|
1452
|
-
setError(err);
|
|
1453
|
-
tracker.track("checkout.failed", {
|
|
1454
|
-
category: err.category,
|
|
1455
|
-
declineCode: err.declineCode,
|
|
1456
|
-
productId: product,
|
|
1457
|
-
intent
|
|
1458
|
-
});
|
|
1459
|
-
opts.onError?.(err);
|
|
1460
|
-
},
|
|
1461
|
-
[tracker, intent, opts]
|
|
1496
|
+
[tracker, nav, advance, opts, productFacts]
|
|
1462
1497
|
);
|
|
1463
|
-
const
|
|
1498
|
+
const succeedRef = React.useRef(succeed);
|
|
1499
|
+
succeedRef.current = succeed;
|
|
1500
|
+
const begin = React.useCallback(
|
|
1464
1501
|
(product, surface) => {
|
|
1465
1502
|
setStatus("loading");
|
|
1466
1503
|
setError(null);
|
|
1467
|
-
|
|
1504
|
+
const { productId, amount, currency } = productFacts(product);
|
|
1505
|
+
tracker.track("checkout.start", {
|
|
1506
|
+
productId,
|
|
1507
|
+
amount,
|
|
1508
|
+
currency,
|
|
1509
|
+
surface,
|
|
1510
|
+
eventId: newEventId()
|
|
1511
|
+
});
|
|
1468
1512
|
},
|
|
1469
|
-
[tracker]
|
|
1513
|
+
[tracker, productFacts]
|
|
1514
|
+
);
|
|
1515
|
+
const paymentAdded = React.useCallback(
|
|
1516
|
+
(product) => {
|
|
1517
|
+
const { productId, amount, currency } = productFacts(product);
|
|
1518
|
+
tracker.track("checkout.payment_added", {
|
|
1519
|
+
productId,
|
|
1520
|
+
amount,
|
|
1521
|
+
currency,
|
|
1522
|
+
eventId: newEventId()
|
|
1523
|
+
});
|
|
1524
|
+
},
|
|
1525
|
+
[tracker, productFacts]
|
|
1470
1526
|
);
|
|
1471
|
-
const
|
|
1472
|
-
const
|
|
1527
|
+
const hostInSheet = (surface) => surface === "sheet" || chunk7JLOF6CJ_cjs.isInlineSurface(surface) && !!chunk7JLOF6CJ_cjs.PROVIDER_PROFILES[driver.provider]?.surfaces.sheet;
|
|
1528
|
+
const startRecovery = (product, surface) => {
|
|
1529
|
+
recovering.current = true;
|
|
1530
|
+
if (hostInSheet(surface)) {
|
|
1531
|
+
begin(product, "sheet");
|
|
1532
|
+
void showModal(CheckoutSheet, {
|
|
1533
|
+
product,
|
|
1534
|
+
surface: "sheet",
|
|
1535
|
+
intent,
|
|
1536
|
+
kind,
|
|
1537
|
+
onSuccess: (r) => succeedRef.current(r, product),
|
|
1538
|
+
onError: (e) => failRef.current(e, product),
|
|
1539
|
+
onPaymentAdded: () => paymentAdded(product)
|
|
1540
|
+
});
|
|
1541
|
+
} else {
|
|
1542
|
+
void attemptRef.current(product, surface);
|
|
1543
|
+
}
|
|
1544
|
+
};
|
|
1545
|
+
failRef.current = (err, product) => {
|
|
1546
|
+
setStatus(
|
|
1547
|
+
err.category === "authentication_required" ? "requires_action" : "error"
|
|
1548
|
+
);
|
|
1549
|
+
setError(err);
|
|
1550
|
+
tracker.track("checkout.failed", {
|
|
1551
|
+
category: err.category,
|
|
1552
|
+
declineCode: err.declineCode,
|
|
1553
|
+
productId: product,
|
|
1554
|
+
intent
|
|
1555
|
+
});
|
|
1556
|
+
opts.onError?.(err);
|
|
1557
|
+
if (err.category === "canceled" && !opts.onFailed?.canceled) return;
|
|
1558
|
+
if (product && !recovering.current && opts.recoverySurface && err.category !== "canceled") {
|
|
1559
|
+
startRecovery(product, opts.recoverySurface);
|
|
1560
|
+
return;
|
|
1561
|
+
}
|
|
1562
|
+
let route = opts.onFailed?.[err.category] ?? (err.category === "canceled" ? void 0 : opts.onFailed?.default);
|
|
1563
|
+
if (route && typeof route === "object" && "fallback" in route && recovering.current) {
|
|
1564
|
+
const dflt = opts.onFailed?.default;
|
|
1565
|
+
route = dflt && dflt !== route && !(typeof dflt === "object" && "fallback" in dflt) ? dflt : void 0;
|
|
1566
|
+
}
|
|
1567
|
+
if (!route) return;
|
|
1568
|
+
if (typeof route === "function") route(err);
|
|
1569
|
+
else if ("go" in route) nav.go(route.go);
|
|
1570
|
+
else if ("modal" in route) {
|
|
1571
|
+
const props = {
|
|
1572
|
+
error: err,
|
|
1573
|
+
product,
|
|
1574
|
+
go: (pageKey) => nav.go(pageKey),
|
|
1575
|
+
retry: (surface) => {
|
|
1576
|
+
if (!product) return;
|
|
1577
|
+
recovering.current = true;
|
|
1578
|
+
if (!surface) {
|
|
1579
|
+
if (intent === "upsell")
|
|
1580
|
+
void attemptRef.current(product);
|
|
1581
|
+
else
|
|
1582
|
+
startRecovery(
|
|
1583
|
+
product,
|
|
1584
|
+
chunk7JLOF6CJ_cjs.PROVIDER_PROFILES[driver.provider]?.surfaces.sheet ? "sheet" : "popup"
|
|
1585
|
+
);
|
|
1586
|
+
return;
|
|
1587
|
+
}
|
|
1588
|
+
startRecovery(product, surface);
|
|
1589
|
+
}
|
|
1590
|
+
};
|
|
1591
|
+
void showModal(
|
|
1592
|
+
route.modal,
|
|
1593
|
+
props
|
|
1594
|
+
);
|
|
1595
|
+
} else if ("fallback" in route) {
|
|
1596
|
+
if (product && !recovering.current)
|
|
1597
|
+
startRecovery(product, route.fallback);
|
|
1598
|
+
}
|
|
1599
|
+
};
|
|
1600
|
+
attemptRef.current = async (product, surface) => {
|
|
1601
|
+
begin(product, surface);
|
|
1602
|
+
try {
|
|
1603
|
+
const result = await driver.start({
|
|
1604
|
+
product,
|
|
1605
|
+
intent,
|
|
1606
|
+
surface,
|
|
1607
|
+
kind
|
|
1608
|
+
});
|
|
1609
|
+
if (result.ok) succeedRef.current(result, product);
|
|
1610
|
+
else
|
|
1611
|
+
failRef.current(
|
|
1612
|
+
result.error ?? chunk7JLOF6CJ_cjs.checkoutError("unknown", "Checkout failed"),
|
|
1613
|
+
product
|
|
1614
|
+
);
|
|
1615
|
+
return result;
|
|
1616
|
+
} catch (e) {
|
|
1617
|
+
const err = chunk7JLOF6CJ_cjs.checkoutError(
|
|
1618
|
+
"processing_error",
|
|
1619
|
+
e instanceof Error ? e.message : "Checkout failed"
|
|
1620
|
+
);
|
|
1621
|
+
failRef.current(err, product);
|
|
1622
|
+
return { ok: false, error: err };
|
|
1623
|
+
}
|
|
1624
|
+
};
|
|
1625
|
+
const open = React.useCallback(
|
|
1473
1626
|
async (product, surface) => {
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
const result = await driver.start({ product, intent, surface, kind });
|
|
1477
|
-
if (result.ok) succeed(result);
|
|
1478
|
-
else fail(result.error ?? checkoutError("unknown", "Checkout failed"), product);
|
|
1479
|
-
return result;
|
|
1480
|
-
} catch (e) {
|
|
1481
|
-
const err = checkoutError("processing_error", e instanceof Error ? e.message : "Checkout failed");
|
|
1482
|
-
fail(err, product);
|
|
1483
|
-
return { ok: false, error: err };
|
|
1484
|
-
}
|
|
1627
|
+
recovering.current = false;
|
|
1628
|
+
return attemptRef.current(product, surface);
|
|
1485
1629
|
},
|
|
1486
|
-
[
|
|
1630
|
+
[]
|
|
1487
1631
|
);
|
|
1488
|
-
const callbacksFor =
|
|
1632
|
+
const callbacksFor = React.useCallback(
|
|
1489
1633
|
(product, surface) => ({
|
|
1490
|
-
onStart: () =>
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1634
|
+
onStart: () => {
|
|
1635
|
+
recovering.current = false;
|
|
1636
|
+
begin(product, surface);
|
|
1637
|
+
},
|
|
1638
|
+
onSuccess: (result) => succeedRef.current(result, product),
|
|
1639
|
+
onError: (err) => failRef.current(err, product),
|
|
1640
|
+
onPaymentAdded: () => paymentAdded(product)
|
|
1494
1641
|
}),
|
|
1495
|
-
[begin,
|
|
1642
|
+
[begin, paymentAdded]
|
|
1496
1643
|
);
|
|
1497
|
-
const reset =
|
|
1644
|
+
const reset = React.useCallback(() => {
|
|
1645
|
+
recovering.current = false;
|
|
1498
1646
|
setStatus("idle");
|
|
1499
1647
|
setError(null);
|
|
1500
1648
|
}, []);
|
|
1501
|
-
return {
|
|
1649
|
+
return {
|
|
1650
|
+
open,
|
|
1651
|
+
status,
|
|
1652
|
+
error,
|
|
1653
|
+
isLoading: status === "loading",
|
|
1654
|
+
reset,
|
|
1655
|
+
callbacksFor
|
|
1656
|
+
};
|
|
1502
1657
|
}
|
|
1503
|
-
var CheckoutSheet = defineModal(({ product, surface, intent, onSuccess, onError }) => {
|
|
1658
|
+
var CheckoutSheet = defineModal(({ product, surface, intent, kind, onSuccess, onError, onPaymentAdded }) => {
|
|
1504
1659
|
const driver = useDriver();
|
|
1505
1660
|
const modal = useModal();
|
|
1661
|
+
const resolved = React.useRef(false);
|
|
1662
|
+
const onErrorRef = React.useRef(onError);
|
|
1663
|
+
onErrorRef.current = onError;
|
|
1664
|
+
const hostable = !!driver.renderInline;
|
|
1665
|
+
React.useEffect(() => {
|
|
1666
|
+
if (!hostable) {
|
|
1667
|
+
resolved.current = true;
|
|
1668
|
+
onErrorRef.current(
|
|
1669
|
+
chunk7JLOF6CJ_cjs.checkoutError(
|
|
1670
|
+
"processing_error",
|
|
1671
|
+
"This checkout driver has no inline surface to host in the sheet"
|
|
1672
|
+
)
|
|
1673
|
+
);
|
|
1674
|
+
void modal.hide();
|
|
1675
|
+
modal.remove();
|
|
1676
|
+
return;
|
|
1677
|
+
}
|
|
1678
|
+
return () => {
|
|
1679
|
+
if (!resolved.current) {
|
|
1680
|
+
onErrorRef.current(
|
|
1681
|
+
chunk7JLOF6CJ_cjs.checkoutError(
|
|
1682
|
+
"canceled",
|
|
1683
|
+
"Checkout was dismissed before completing"
|
|
1684
|
+
)
|
|
1685
|
+
);
|
|
1686
|
+
}
|
|
1687
|
+
};
|
|
1688
|
+
}, []);
|
|
1506
1689
|
if (!driver.renderInline) return null;
|
|
1507
|
-
|
|
1690
|
+
const close = () => {
|
|
1691
|
+
resolved.current = true;
|
|
1692
|
+
void modal.hide();
|
|
1693
|
+
modal.remove();
|
|
1694
|
+
};
|
|
1695
|
+
return React.createElement(Sheet, {
|
|
1508
1696
|
children: driver.renderInline(
|
|
1509
|
-
{ product, intent, surface },
|
|
1697
|
+
{ product, intent, surface, kind },
|
|
1510
1698
|
{
|
|
1511
1699
|
onSuccess: (r) => {
|
|
1512
|
-
|
|
1700
|
+
close();
|
|
1513
1701
|
onSuccess(r);
|
|
1514
1702
|
},
|
|
1515
1703
|
onError: (e) => {
|
|
1516
|
-
|
|
1704
|
+
close();
|
|
1517
1705
|
onError(e);
|
|
1518
|
-
}
|
|
1706
|
+
},
|
|
1707
|
+
onPaymentAdded
|
|
1519
1708
|
}
|
|
1520
1709
|
)
|
|
1521
1710
|
});
|
|
1522
1711
|
});
|
|
1523
|
-
function trigger(onClick, {
|
|
1524
|
-
|
|
1712
|
+
function trigger(onClick, {
|
|
1713
|
+
asChild,
|
|
1714
|
+
children,
|
|
1715
|
+
className,
|
|
1716
|
+
disabled
|
|
1717
|
+
}) {
|
|
1718
|
+
if (asChild && React.isValidElement(children)) {
|
|
1525
1719
|
const child = children;
|
|
1526
|
-
return
|
|
1720
|
+
return React.cloneElement(child, { onClick });
|
|
1721
|
+
}
|
|
1722
|
+
return React.createElement(
|
|
1723
|
+
"button",
|
|
1724
|
+
{ type: "button", className, disabled, onClick },
|
|
1725
|
+
children ?? "Continue"
|
|
1726
|
+
);
|
|
1727
|
+
}
|
|
1728
|
+
function detectDeviceClass() {
|
|
1729
|
+
if (typeof window === "undefined") return "desktop";
|
|
1730
|
+
const ua = navigator.userAgent?.toLowerCase() ?? "";
|
|
1731
|
+
if (/mobile|android(?!.*tablet)|iphone|ipod/.test(ua)) return "mobile";
|
|
1732
|
+
try {
|
|
1733
|
+
if (window.matchMedia?.("(pointer: coarse)").matches && window.innerWidth > 0 && window.innerWidth < 768) {
|
|
1734
|
+
return "mobile";
|
|
1735
|
+
}
|
|
1736
|
+
} catch {
|
|
1527
1737
|
}
|
|
1528
|
-
return
|
|
1738
|
+
return "desktop";
|
|
1529
1739
|
}
|
|
1530
1740
|
function Checkout({
|
|
1531
|
-
|
|
1532
|
-
surface,
|
|
1741
|
+
offering,
|
|
1742
|
+
surface: surfacePreference,
|
|
1533
1743
|
asChild,
|
|
1534
1744
|
children,
|
|
1535
1745
|
className,
|
|
1536
1746
|
...options
|
|
1537
1747
|
}) {
|
|
1748
|
+
const product = offering;
|
|
1538
1749
|
const driver = useDriver();
|
|
1750
|
+
const [surface] = React.useState(
|
|
1751
|
+
() => chunk7JLOF6CJ_cjs.resolveSurface(driver.provider, surfacePreference, detectDeviceClass())
|
|
1752
|
+
);
|
|
1539
1753
|
const checkout = useCheckout(options);
|
|
1540
1754
|
const intent = options.intent ?? "purchase";
|
|
1541
|
-
if (isInlineSurface(surface)) {
|
|
1542
|
-
return driver.renderInline ?
|
|
1755
|
+
if (chunk7JLOF6CJ_cjs.isInlineSurface(surface)) {
|
|
1756
|
+
return driver.renderInline ? React.createElement(
|
|
1543
1757
|
"div",
|
|
1544
1758
|
{ key: product, className },
|
|
1545
|
-
driver.renderInline(
|
|
1759
|
+
driver.renderInline(
|
|
1760
|
+
{ product, intent, surface },
|
|
1761
|
+
checkout.callbacksFor(product, surface)
|
|
1762
|
+
)
|
|
1546
1763
|
) : null;
|
|
1547
1764
|
}
|
|
1548
1765
|
if (surface === "sheet") {
|
|
1549
1766
|
const cb = checkout.callbacksFor(product, surface);
|
|
1550
1767
|
const open = () => {
|
|
1551
1768
|
cb.onStart();
|
|
1552
|
-
showModal(CheckoutSheet, {
|
|
1769
|
+
showModal(CheckoutSheet, {
|
|
1770
|
+
product,
|
|
1771
|
+
surface,
|
|
1772
|
+
intent,
|
|
1773
|
+
kind: options.kind,
|
|
1774
|
+
onSuccess: cb.onSuccess,
|
|
1775
|
+
onError: cb.onError,
|
|
1776
|
+
onPaymentAdded: cb.onPaymentAdded
|
|
1777
|
+
});
|
|
1553
1778
|
};
|
|
1554
|
-
return trigger(open, {
|
|
1779
|
+
return trigger(open, {
|
|
1780
|
+
asChild,
|
|
1781
|
+
children,
|
|
1782
|
+
className,
|
|
1783
|
+
disabled: checkout.isLoading
|
|
1784
|
+
});
|
|
1555
1785
|
}
|
|
1556
1786
|
return trigger(() => checkout.open(product, surface), {
|
|
1557
1787
|
asChild,
|
|
@@ -1560,32 +1790,1150 @@ function Checkout({
|
|
|
1560
1790
|
disabled: checkout.isLoading
|
|
1561
1791
|
});
|
|
1562
1792
|
}
|
|
1563
|
-
function Upsell({
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1793
|
+
function Upsell({
|
|
1794
|
+
offering,
|
|
1795
|
+
fallback,
|
|
1796
|
+
asChild,
|
|
1797
|
+
children,
|
|
1798
|
+
className,
|
|
1799
|
+
...options
|
|
1800
|
+
}) {
|
|
1801
|
+
const product = offering;
|
|
1802
|
+
const checkout = useCheckout({
|
|
1803
|
+
...options,
|
|
1804
|
+
intent: "upsell",
|
|
1805
|
+
recoverySurface: fallback ?? options.recoverySurface
|
|
1806
|
+
});
|
|
1807
|
+
return trigger(
|
|
1808
|
+
() => void checkout.open(product),
|
|
1809
|
+
{
|
|
1810
|
+
asChild,
|
|
1811
|
+
children: children ?? "Add to my plan",
|
|
1812
|
+
className,
|
|
1813
|
+
disabled: checkout.isLoading
|
|
1814
|
+
}
|
|
1815
|
+
);
|
|
1816
|
+
}
|
|
1817
|
+
var consumedResumes = /* @__PURE__ */ new Set();
|
|
1818
|
+
function CheckoutResume(options = {}) {
|
|
1819
|
+
const driver = useDriver();
|
|
1820
|
+
const checkout = useCheckout(options);
|
|
1821
|
+
const callbacksRef = React.useRef(checkout.callbacksFor);
|
|
1822
|
+
callbacksRef.current = checkout.callbacksFor;
|
|
1823
|
+
React.useEffect(() => {
|
|
1824
|
+
if (typeof window === "undefined" || !driver.resume) return;
|
|
1825
|
+
const url = new URL(window.location.href);
|
|
1826
|
+
const correlationId = url.searchParams.get("af_checkout");
|
|
1827
|
+
if (!correlationId || consumedResumes.has(correlationId)) return;
|
|
1828
|
+
consumedResumes.add(correlationId);
|
|
1829
|
+
void driver.resume(correlationId).then((result) => {
|
|
1830
|
+
const cb = callbacksRef.current();
|
|
1831
|
+
if (result.ok) {
|
|
1832
|
+
url.searchParams.delete("af_checkout");
|
|
1833
|
+
window.history.replaceState(
|
|
1834
|
+
window.history.state,
|
|
1835
|
+
"",
|
|
1836
|
+
url.toString()
|
|
1837
|
+
);
|
|
1838
|
+
cb.onSuccess(result);
|
|
1839
|
+
} else {
|
|
1840
|
+
cb.onError(
|
|
1841
|
+
result.error ?? chunk7JLOF6CJ_cjs.checkoutError(
|
|
1842
|
+
"unknown",
|
|
1843
|
+
"The checkout could not be verified"
|
|
1844
|
+
)
|
|
1845
|
+
);
|
|
1846
|
+
}
|
|
1847
|
+
});
|
|
1848
|
+
}, []);
|
|
1849
|
+
return null;
|
|
1850
|
+
}
|
|
1851
|
+
function __insertCSS(code) {
|
|
1852
|
+
if (typeof document == "undefined") return;
|
|
1853
|
+
let head = document.head || document.getElementsByTagName("head")[0];
|
|
1854
|
+
let style = document.createElement("style");
|
|
1855
|
+
style.type = "text/css";
|
|
1856
|
+
head.appendChild(style);
|
|
1857
|
+
style.styleSheet ? style.styleSheet.cssText = code : style.appendChild(document.createTextNode(code));
|
|
1858
|
+
}
|
|
1859
|
+
var getAsset = (type) => {
|
|
1860
|
+
switch (type) {
|
|
1861
|
+
case "success":
|
|
1862
|
+
return SuccessIcon;
|
|
1863
|
+
case "info":
|
|
1864
|
+
return InfoIcon;
|
|
1865
|
+
case "warning":
|
|
1866
|
+
return WarningIcon;
|
|
1867
|
+
case "error":
|
|
1868
|
+
return ErrorIcon;
|
|
1869
|
+
default:
|
|
1870
|
+
return null;
|
|
1871
|
+
}
|
|
1872
|
+
};
|
|
1873
|
+
var bars = Array(12).fill(0);
|
|
1874
|
+
var Loader = ({ visible, className }) => {
|
|
1875
|
+
return /* @__PURE__ */ React__default.default.createElement("div", {
|
|
1876
|
+
className: [
|
|
1877
|
+
"sonner-loading-wrapper",
|
|
1878
|
+
className
|
|
1879
|
+
].filter(Boolean).join(" "),
|
|
1880
|
+
"data-visible": visible
|
|
1881
|
+
}, /* @__PURE__ */ React__default.default.createElement("div", {
|
|
1882
|
+
className: "sonner-spinner"
|
|
1883
|
+
}, bars.map((_, i) => /* @__PURE__ */ React__default.default.createElement("div", {
|
|
1884
|
+
className: "sonner-loading-bar",
|
|
1885
|
+
key: `spinner-bar-${i}`
|
|
1886
|
+
}))));
|
|
1887
|
+
};
|
|
1888
|
+
var SuccessIcon = /* @__PURE__ */ React__default.default.createElement("svg", {
|
|
1889
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1890
|
+
viewBox: "0 0 20 20",
|
|
1891
|
+
fill: "currentColor",
|
|
1892
|
+
height: "20",
|
|
1893
|
+
width: "20"
|
|
1894
|
+
}, /* @__PURE__ */ React__default.default.createElement("path", {
|
|
1895
|
+
fillRule: "evenodd",
|
|
1896
|
+
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",
|
|
1897
|
+
clipRule: "evenodd"
|
|
1898
|
+
}));
|
|
1899
|
+
var WarningIcon = /* @__PURE__ */ React__default.default.createElement("svg", {
|
|
1900
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1901
|
+
viewBox: "0 0 24 24",
|
|
1902
|
+
fill: "currentColor",
|
|
1903
|
+
height: "20",
|
|
1904
|
+
width: "20"
|
|
1905
|
+
}, /* @__PURE__ */ React__default.default.createElement("path", {
|
|
1906
|
+
fillRule: "evenodd",
|
|
1907
|
+
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",
|
|
1908
|
+
clipRule: "evenodd"
|
|
1909
|
+
}));
|
|
1910
|
+
var InfoIcon = /* @__PURE__ */ React__default.default.createElement("svg", {
|
|
1911
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1912
|
+
viewBox: "0 0 20 20",
|
|
1913
|
+
fill: "currentColor",
|
|
1914
|
+
height: "20",
|
|
1915
|
+
width: "20"
|
|
1916
|
+
}, /* @__PURE__ */ React__default.default.createElement("path", {
|
|
1917
|
+
fillRule: "evenodd",
|
|
1918
|
+
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",
|
|
1919
|
+
clipRule: "evenodd"
|
|
1920
|
+
}));
|
|
1921
|
+
var ErrorIcon = /* @__PURE__ */ React__default.default.createElement("svg", {
|
|
1922
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1923
|
+
viewBox: "0 0 20 20",
|
|
1924
|
+
fill: "currentColor",
|
|
1925
|
+
height: "20",
|
|
1926
|
+
width: "20"
|
|
1927
|
+
}, /* @__PURE__ */ React__default.default.createElement("path", {
|
|
1928
|
+
fillRule: "evenodd",
|
|
1929
|
+
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",
|
|
1930
|
+
clipRule: "evenodd"
|
|
1931
|
+
}));
|
|
1932
|
+
var CloseIcon = /* @__PURE__ */ React__default.default.createElement("svg", {
|
|
1933
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1934
|
+
width: "12",
|
|
1935
|
+
height: "12",
|
|
1936
|
+
viewBox: "0 0 24 24",
|
|
1937
|
+
fill: "none",
|
|
1938
|
+
stroke: "currentColor",
|
|
1939
|
+
strokeWidth: "1.5",
|
|
1940
|
+
strokeLinecap: "round",
|
|
1941
|
+
strokeLinejoin: "round"
|
|
1942
|
+
}, /* @__PURE__ */ React__default.default.createElement("line", {
|
|
1943
|
+
x1: "18",
|
|
1944
|
+
y1: "6",
|
|
1945
|
+
x2: "6",
|
|
1946
|
+
y2: "18"
|
|
1947
|
+
}), /* @__PURE__ */ React__default.default.createElement("line", {
|
|
1948
|
+
x1: "6",
|
|
1949
|
+
y1: "6",
|
|
1950
|
+
x2: "18",
|
|
1951
|
+
y2: "18"
|
|
1952
|
+
}));
|
|
1953
|
+
var useIsDocumentHidden = () => {
|
|
1954
|
+
const [isDocumentHidden, setIsDocumentHidden] = React__default.default.useState(document.hidden);
|
|
1955
|
+
React__default.default.useEffect(() => {
|
|
1956
|
+
const callback = () => {
|
|
1957
|
+
setIsDocumentHidden(document.hidden);
|
|
1958
|
+
};
|
|
1959
|
+
document.addEventListener("visibilitychange", callback);
|
|
1960
|
+
return () => window.removeEventListener("visibilitychange", callback);
|
|
1961
|
+
}, []);
|
|
1962
|
+
return isDocumentHidden;
|
|
1963
|
+
};
|
|
1964
|
+
var toastsCounter = 1;
|
|
1965
|
+
var Observer = class {
|
|
1966
|
+
constructor() {
|
|
1967
|
+
this.subscribe = (subscriber) => {
|
|
1968
|
+
this.subscribers.push(subscriber);
|
|
1969
|
+
return () => {
|
|
1970
|
+
const index = this.subscribers.indexOf(subscriber);
|
|
1971
|
+
this.subscribers.splice(index, 1);
|
|
1972
|
+
};
|
|
1973
|
+
};
|
|
1974
|
+
this.publish = (data) => {
|
|
1975
|
+
this.subscribers.forEach((subscriber) => subscriber(data));
|
|
1976
|
+
};
|
|
1977
|
+
this.addToast = (data) => {
|
|
1978
|
+
this.publish(data);
|
|
1979
|
+
this.toasts = [
|
|
1980
|
+
...this.toasts,
|
|
1981
|
+
data
|
|
1982
|
+
];
|
|
1983
|
+
};
|
|
1984
|
+
this.create = (data) => {
|
|
1985
|
+
var _data_id;
|
|
1986
|
+
const { message, ...rest } = data;
|
|
1987
|
+
const id = typeof (data == null ? void 0 : data.id) === "number" || ((_data_id = data.id) == null ? void 0 : _data_id.length) > 0 ? data.id : toastsCounter++;
|
|
1988
|
+
const alreadyExists = this.toasts.find((toast2) => {
|
|
1989
|
+
return toast2.id === id;
|
|
1990
|
+
});
|
|
1991
|
+
const dismissible = data.dismissible === void 0 ? true : data.dismissible;
|
|
1992
|
+
if (this.dismissedToasts.has(id)) {
|
|
1993
|
+
this.dismissedToasts.delete(id);
|
|
1994
|
+
}
|
|
1995
|
+
if (alreadyExists) {
|
|
1996
|
+
this.toasts = this.toasts.map((toast2) => {
|
|
1997
|
+
if (toast2.id === id) {
|
|
1998
|
+
this.publish({
|
|
1999
|
+
...toast2,
|
|
2000
|
+
...data,
|
|
2001
|
+
id,
|
|
2002
|
+
title: message
|
|
2003
|
+
});
|
|
2004
|
+
return {
|
|
2005
|
+
...toast2,
|
|
2006
|
+
...data,
|
|
2007
|
+
id,
|
|
2008
|
+
dismissible,
|
|
2009
|
+
title: message
|
|
2010
|
+
};
|
|
2011
|
+
}
|
|
2012
|
+
return toast2;
|
|
2013
|
+
});
|
|
2014
|
+
} else {
|
|
2015
|
+
this.addToast({
|
|
2016
|
+
title: message,
|
|
2017
|
+
...rest,
|
|
2018
|
+
dismissible,
|
|
2019
|
+
id
|
|
2020
|
+
});
|
|
2021
|
+
}
|
|
2022
|
+
return id;
|
|
2023
|
+
};
|
|
2024
|
+
this.dismiss = (id) => {
|
|
2025
|
+
if (id) {
|
|
2026
|
+
this.dismissedToasts.add(id);
|
|
2027
|
+
requestAnimationFrame(() => this.subscribers.forEach((subscriber) => subscriber({
|
|
2028
|
+
id,
|
|
2029
|
+
dismiss: true
|
|
2030
|
+
})));
|
|
2031
|
+
} else {
|
|
2032
|
+
this.toasts.forEach((toast2) => {
|
|
2033
|
+
this.subscribers.forEach((subscriber) => subscriber({
|
|
2034
|
+
id: toast2.id,
|
|
2035
|
+
dismiss: true
|
|
2036
|
+
}));
|
|
2037
|
+
});
|
|
2038
|
+
}
|
|
2039
|
+
return id;
|
|
2040
|
+
};
|
|
2041
|
+
this.message = (message, data) => {
|
|
2042
|
+
return this.create({
|
|
2043
|
+
...data,
|
|
2044
|
+
message
|
|
2045
|
+
});
|
|
2046
|
+
};
|
|
2047
|
+
this.error = (message, data) => {
|
|
2048
|
+
return this.create({
|
|
2049
|
+
...data,
|
|
2050
|
+
message,
|
|
2051
|
+
type: "error"
|
|
2052
|
+
});
|
|
2053
|
+
};
|
|
2054
|
+
this.success = (message, data) => {
|
|
2055
|
+
return this.create({
|
|
2056
|
+
...data,
|
|
2057
|
+
type: "success",
|
|
2058
|
+
message
|
|
2059
|
+
});
|
|
2060
|
+
};
|
|
2061
|
+
this.info = (message, data) => {
|
|
2062
|
+
return this.create({
|
|
2063
|
+
...data,
|
|
2064
|
+
type: "info",
|
|
2065
|
+
message
|
|
2066
|
+
});
|
|
2067
|
+
};
|
|
2068
|
+
this.warning = (message, data) => {
|
|
2069
|
+
return this.create({
|
|
2070
|
+
...data,
|
|
2071
|
+
type: "warning",
|
|
2072
|
+
message
|
|
2073
|
+
});
|
|
2074
|
+
};
|
|
2075
|
+
this.loading = (message, data) => {
|
|
2076
|
+
return this.create({
|
|
2077
|
+
...data,
|
|
2078
|
+
type: "loading",
|
|
2079
|
+
message
|
|
2080
|
+
});
|
|
2081
|
+
};
|
|
2082
|
+
this.promise = (promise, data) => {
|
|
2083
|
+
if (!data) {
|
|
2084
|
+
return;
|
|
2085
|
+
}
|
|
2086
|
+
let id = void 0;
|
|
2087
|
+
if (data.loading !== void 0) {
|
|
2088
|
+
id = this.create({
|
|
2089
|
+
...data,
|
|
2090
|
+
promise,
|
|
2091
|
+
type: "loading",
|
|
2092
|
+
message: data.loading,
|
|
2093
|
+
description: typeof data.description !== "function" ? data.description : void 0
|
|
2094
|
+
});
|
|
2095
|
+
}
|
|
2096
|
+
const p = Promise.resolve(promise instanceof Function ? promise() : promise);
|
|
2097
|
+
let shouldDismiss = id !== void 0;
|
|
2098
|
+
let result;
|
|
2099
|
+
const originalPromise = p.then(async (response) => {
|
|
2100
|
+
result = [
|
|
2101
|
+
"resolve",
|
|
2102
|
+
response
|
|
2103
|
+
];
|
|
2104
|
+
const isReactElementResponse = React__default.default.isValidElement(response);
|
|
2105
|
+
if (isReactElementResponse) {
|
|
2106
|
+
shouldDismiss = false;
|
|
2107
|
+
this.create({
|
|
2108
|
+
id,
|
|
2109
|
+
type: "default",
|
|
2110
|
+
message: response
|
|
2111
|
+
});
|
|
2112
|
+
} else if (isHttpResponse(response) && !response.ok) {
|
|
2113
|
+
shouldDismiss = false;
|
|
2114
|
+
const promiseData = typeof data.error === "function" ? await data.error(`HTTP error! status: ${response.status}`) : data.error;
|
|
2115
|
+
const description = typeof data.description === "function" ? await data.description(`HTTP error! status: ${response.status}`) : data.description;
|
|
2116
|
+
const isExtendedResult = typeof promiseData === "object" && !React__default.default.isValidElement(promiseData);
|
|
2117
|
+
const toastSettings = isExtendedResult ? promiseData : {
|
|
2118
|
+
message: promiseData
|
|
2119
|
+
};
|
|
2120
|
+
this.create({
|
|
2121
|
+
id,
|
|
2122
|
+
type: "error",
|
|
2123
|
+
description,
|
|
2124
|
+
...toastSettings
|
|
2125
|
+
});
|
|
2126
|
+
} else if (response instanceof Error) {
|
|
2127
|
+
shouldDismiss = false;
|
|
2128
|
+
const promiseData = typeof data.error === "function" ? await data.error(response) : data.error;
|
|
2129
|
+
const description = typeof data.description === "function" ? await data.description(response) : data.description;
|
|
2130
|
+
const isExtendedResult = typeof promiseData === "object" && !React__default.default.isValidElement(promiseData);
|
|
2131
|
+
const toastSettings = isExtendedResult ? promiseData : {
|
|
2132
|
+
message: promiseData
|
|
2133
|
+
};
|
|
2134
|
+
this.create({
|
|
2135
|
+
id,
|
|
2136
|
+
type: "error",
|
|
2137
|
+
description,
|
|
2138
|
+
...toastSettings
|
|
2139
|
+
});
|
|
2140
|
+
} else if (data.success !== void 0) {
|
|
2141
|
+
shouldDismiss = false;
|
|
2142
|
+
const promiseData = typeof data.success === "function" ? await data.success(response) : data.success;
|
|
2143
|
+
const description = typeof data.description === "function" ? await data.description(response) : data.description;
|
|
2144
|
+
const isExtendedResult = typeof promiseData === "object" && !React__default.default.isValidElement(promiseData);
|
|
2145
|
+
const toastSettings = isExtendedResult ? promiseData : {
|
|
2146
|
+
message: promiseData
|
|
2147
|
+
};
|
|
2148
|
+
this.create({
|
|
2149
|
+
id,
|
|
2150
|
+
type: "success",
|
|
2151
|
+
description,
|
|
2152
|
+
...toastSettings
|
|
2153
|
+
});
|
|
2154
|
+
}
|
|
2155
|
+
}).catch(async (error) => {
|
|
2156
|
+
result = [
|
|
2157
|
+
"reject",
|
|
2158
|
+
error
|
|
2159
|
+
];
|
|
2160
|
+
if (data.error !== void 0) {
|
|
2161
|
+
shouldDismiss = false;
|
|
2162
|
+
const promiseData = typeof data.error === "function" ? await data.error(error) : data.error;
|
|
2163
|
+
const description = typeof data.description === "function" ? await data.description(error) : data.description;
|
|
2164
|
+
const isExtendedResult = typeof promiseData === "object" && !React__default.default.isValidElement(promiseData);
|
|
2165
|
+
const toastSettings = isExtendedResult ? promiseData : {
|
|
2166
|
+
message: promiseData
|
|
2167
|
+
};
|
|
2168
|
+
this.create({
|
|
2169
|
+
id,
|
|
2170
|
+
type: "error",
|
|
2171
|
+
description,
|
|
2172
|
+
...toastSettings
|
|
2173
|
+
});
|
|
2174
|
+
}
|
|
2175
|
+
}).finally(() => {
|
|
2176
|
+
if (shouldDismiss) {
|
|
2177
|
+
this.dismiss(id);
|
|
2178
|
+
id = void 0;
|
|
2179
|
+
}
|
|
2180
|
+
data.finally == null ? void 0 : data.finally.call(data);
|
|
2181
|
+
});
|
|
2182
|
+
const unwrap = () => new Promise((resolve, reject) => originalPromise.then(() => result[0] === "reject" ? reject(result[1]) : resolve(result[1])).catch(reject));
|
|
2183
|
+
if (typeof id !== "string" && typeof id !== "number") {
|
|
2184
|
+
return {
|
|
2185
|
+
unwrap
|
|
2186
|
+
};
|
|
2187
|
+
} else {
|
|
2188
|
+
return Object.assign(id, {
|
|
2189
|
+
unwrap
|
|
2190
|
+
});
|
|
2191
|
+
}
|
|
2192
|
+
};
|
|
2193
|
+
this.custom = (jsx2, data) => {
|
|
2194
|
+
const id = (data == null ? void 0 : data.id) || toastsCounter++;
|
|
2195
|
+
this.create({
|
|
2196
|
+
jsx: jsx2(id),
|
|
2197
|
+
id,
|
|
2198
|
+
...data
|
|
2199
|
+
});
|
|
2200
|
+
return id;
|
|
2201
|
+
};
|
|
2202
|
+
this.getActiveToasts = () => {
|
|
2203
|
+
return this.toasts.filter((toast2) => !this.dismissedToasts.has(toast2.id));
|
|
2204
|
+
};
|
|
2205
|
+
this.subscribers = [];
|
|
2206
|
+
this.toasts = [];
|
|
2207
|
+
this.dismissedToasts = /* @__PURE__ */ new Set();
|
|
2208
|
+
}
|
|
2209
|
+
};
|
|
2210
|
+
var ToastState = new Observer();
|
|
2211
|
+
var toastFunction = (message, data) => {
|
|
2212
|
+
const id = (data == null ? void 0 : data.id) || toastsCounter++;
|
|
2213
|
+
ToastState.addToast({
|
|
2214
|
+
title: message,
|
|
2215
|
+
...data,
|
|
2216
|
+
id
|
|
2217
|
+
});
|
|
2218
|
+
return id;
|
|
2219
|
+
};
|
|
2220
|
+
var isHttpResponse = (data) => {
|
|
2221
|
+
return data && typeof data === "object" && "ok" in data && typeof data.ok === "boolean" && "status" in data && typeof data.status === "number";
|
|
2222
|
+
};
|
|
2223
|
+
var basicToast = toastFunction;
|
|
2224
|
+
var getHistory = () => ToastState.toasts;
|
|
2225
|
+
var getToasts = () => ToastState.getActiveToasts();
|
|
2226
|
+
var toast = Object.assign(basicToast, {
|
|
2227
|
+
success: ToastState.success,
|
|
2228
|
+
info: ToastState.info,
|
|
2229
|
+
warning: ToastState.warning,
|
|
2230
|
+
error: ToastState.error,
|
|
2231
|
+
custom: ToastState.custom,
|
|
2232
|
+
message: ToastState.message,
|
|
2233
|
+
promise: ToastState.promise,
|
|
2234
|
+
dismiss: ToastState.dismiss,
|
|
2235
|
+
loading: ToastState.loading
|
|
2236
|
+
}, {
|
|
2237
|
+
getHistory,
|
|
2238
|
+
getToasts
|
|
2239
|
+
});
|
|
2240
|
+
__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%)}");
|
|
2241
|
+
function isAction(action) {
|
|
2242
|
+
return action.label !== void 0;
|
|
2243
|
+
}
|
|
2244
|
+
var VISIBLE_TOASTS_AMOUNT = 3;
|
|
2245
|
+
var VIEWPORT_OFFSET = "24px";
|
|
2246
|
+
var MOBILE_VIEWPORT_OFFSET = "16px";
|
|
2247
|
+
var TOAST_LIFETIME = 4e3;
|
|
2248
|
+
var TOAST_WIDTH = 356;
|
|
2249
|
+
var GAP = 14;
|
|
2250
|
+
var SWIPE_THRESHOLD = 45;
|
|
2251
|
+
var TIME_BEFORE_UNMOUNT = 200;
|
|
2252
|
+
function cn(...classes) {
|
|
2253
|
+
return classes.filter(Boolean).join(" ");
|
|
2254
|
+
}
|
|
2255
|
+
function getDefaultSwipeDirections(position) {
|
|
2256
|
+
const [y, x] = position.split("-");
|
|
2257
|
+
const directions = [];
|
|
2258
|
+
if (y) {
|
|
2259
|
+
directions.push(y);
|
|
2260
|
+
}
|
|
2261
|
+
if (x) {
|
|
2262
|
+
directions.push(x);
|
|
2263
|
+
}
|
|
2264
|
+
return directions;
|
|
2265
|
+
}
|
|
2266
|
+
var Toast = (props) => {
|
|
2267
|
+
var _toast_classNames, _toast_classNames1, _toast_classNames2, _toast_classNames3, _toast_classNames4, _toast_classNames5, _toast_classNames6, _toast_classNames7, _toast_classNames8;
|
|
2268
|
+
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;
|
|
2269
|
+
const [swipeDirection, setSwipeDirection] = React__default.default.useState(null);
|
|
2270
|
+
const [swipeOutDirection, setSwipeOutDirection] = React__default.default.useState(null);
|
|
2271
|
+
const [mounted, setMounted] = React__default.default.useState(false);
|
|
2272
|
+
const [removed, setRemoved] = React__default.default.useState(false);
|
|
2273
|
+
const [swiping, setSwiping] = React__default.default.useState(false);
|
|
2274
|
+
const [swipeOut, setSwipeOut] = React__default.default.useState(false);
|
|
2275
|
+
const [isSwiped, setIsSwiped] = React__default.default.useState(false);
|
|
2276
|
+
const [offsetBeforeRemove, setOffsetBeforeRemove] = React__default.default.useState(0);
|
|
2277
|
+
const [initialHeight, setInitialHeight] = React__default.default.useState(0);
|
|
2278
|
+
const remainingTime = React__default.default.useRef(toast2.duration || durationFromToaster || TOAST_LIFETIME);
|
|
2279
|
+
const dragStartTime = React__default.default.useRef(null);
|
|
2280
|
+
const toastRef = React__default.default.useRef(null);
|
|
2281
|
+
const isFront = index === 0;
|
|
2282
|
+
const isVisible = index + 1 <= visibleToasts;
|
|
2283
|
+
const toastType = toast2.type;
|
|
2284
|
+
const dismissible = toast2.dismissible !== false;
|
|
2285
|
+
const toastClassname = toast2.className || "";
|
|
2286
|
+
const toastDescriptionClassname = toast2.descriptionClassName || "";
|
|
2287
|
+
const heightIndex = React__default.default.useMemo(() => heights.findIndex((height) => height.toastId === toast2.id) || 0, [
|
|
2288
|
+
heights,
|
|
2289
|
+
toast2.id
|
|
2290
|
+
]);
|
|
2291
|
+
const closeButton = React__default.default.useMemo(() => {
|
|
2292
|
+
var _toast_closeButton;
|
|
2293
|
+
return (_toast_closeButton = toast2.closeButton) != null ? _toast_closeButton : closeButtonFromToaster;
|
|
2294
|
+
}, [
|
|
2295
|
+
toast2.closeButton,
|
|
2296
|
+
closeButtonFromToaster
|
|
2297
|
+
]);
|
|
2298
|
+
const duration = React__default.default.useMemo(() => toast2.duration || durationFromToaster || TOAST_LIFETIME, [
|
|
2299
|
+
toast2.duration,
|
|
2300
|
+
durationFromToaster
|
|
2301
|
+
]);
|
|
2302
|
+
const closeTimerStartTimeRef = React__default.default.useRef(0);
|
|
2303
|
+
const offset = React__default.default.useRef(0);
|
|
2304
|
+
const lastCloseTimerStartTimeRef = React__default.default.useRef(0);
|
|
2305
|
+
const pointerStartRef = React__default.default.useRef(null);
|
|
2306
|
+
const [y, x] = position.split("-");
|
|
2307
|
+
const toastsHeightBefore = React__default.default.useMemo(() => {
|
|
2308
|
+
return heights.reduce((prev, curr, reducerIndex) => {
|
|
2309
|
+
if (reducerIndex >= heightIndex) {
|
|
2310
|
+
return prev;
|
|
2311
|
+
}
|
|
2312
|
+
return prev + curr.height;
|
|
2313
|
+
}, 0);
|
|
2314
|
+
}, [
|
|
2315
|
+
heights,
|
|
2316
|
+
heightIndex
|
|
2317
|
+
]);
|
|
2318
|
+
const isDocumentHidden = useIsDocumentHidden();
|
|
2319
|
+
const invert = toast2.invert || ToasterInvert;
|
|
2320
|
+
const disabled = toastType === "loading";
|
|
2321
|
+
offset.current = React__default.default.useMemo(() => heightIndex * gap + toastsHeightBefore, [
|
|
2322
|
+
heightIndex,
|
|
2323
|
+
toastsHeightBefore
|
|
2324
|
+
]);
|
|
2325
|
+
React__default.default.useEffect(() => {
|
|
2326
|
+
remainingTime.current = duration;
|
|
2327
|
+
}, [
|
|
2328
|
+
duration
|
|
2329
|
+
]);
|
|
2330
|
+
React__default.default.useEffect(() => {
|
|
2331
|
+
setMounted(true);
|
|
2332
|
+
}, []);
|
|
2333
|
+
React__default.default.useEffect(() => {
|
|
2334
|
+
const toastNode = toastRef.current;
|
|
2335
|
+
if (toastNode) {
|
|
2336
|
+
const height = toastNode.getBoundingClientRect().height;
|
|
2337
|
+
setInitialHeight(height);
|
|
2338
|
+
setHeights((h) => [
|
|
2339
|
+
{
|
|
2340
|
+
toastId: toast2.id,
|
|
2341
|
+
height,
|
|
2342
|
+
position: toast2.position
|
|
2343
|
+
},
|
|
2344
|
+
...h
|
|
2345
|
+
]);
|
|
2346
|
+
return () => setHeights((h) => h.filter((height2) => height2.toastId !== toast2.id));
|
|
2347
|
+
}
|
|
2348
|
+
}, [
|
|
2349
|
+
setHeights,
|
|
2350
|
+
toast2.id
|
|
2351
|
+
]);
|
|
2352
|
+
React__default.default.useLayoutEffect(() => {
|
|
2353
|
+
if (!mounted) return;
|
|
2354
|
+
const toastNode = toastRef.current;
|
|
2355
|
+
const originalHeight = toastNode.style.height;
|
|
2356
|
+
toastNode.style.height = "auto";
|
|
2357
|
+
const newHeight = toastNode.getBoundingClientRect().height;
|
|
2358
|
+
toastNode.style.height = originalHeight;
|
|
2359
|
+
setInitialHeight(newHeight);
|
|
2360
|
+
setHeights((heights2) => {
|
|
2361
|
+
const alreadyExists = heights2.find((height) => height.toastId === toast2.id);
|
|
2362
|
+
if (!alreadyExists) {
|
|
2363
|
+
return [
|
|
2364
|
+
{
|
|
2365
|
+
toastId: toast2.id,
|
|
2366
|
+
height: newHeight,
|
|
2367
|
+
position: toast2.position
|
|
2368
|
+
},
|
|
2369
|
+
...heights2
|
|
2370
|
+
];
|
|
2371
|
+
} else {
|
|
2372
|
+
return heights2.map((height) => height.toastId === toast2.id ? {
|
|
2373
|
+
...height,
|
|
2374
|
+
height: newHeight
|
|
2375
|
+
} : height);
|
|
2376
|
+
}
|
|
2377
|
+
});
|
|
2378
|
+
}, [
|
|
2379
|
+
mounted,
|
|
2380
|
+
toast2.title,
|
|
2381
|
+
toast2.description,
|
|
2382
|
+
setHeights,
|
|
2383
|
+
toast2.id,
|
|
2384
|
+
toast2.jsx,
|
|
2385
|
+
toast2.action,
|
|
2386
|
+
toast2.cancel
|
|
2387
|
+
]);
|
|
2388
|
+
const deleteToast = React__default.default.useCallback(() => {
|
|
2389
|
+
setRemoved(true);
|
|
2390
|
+
setOffsetBeforeRemove(offset.current);
|
|
2391
|
+
setHeights((h) => h.filter((height) => height.toastId !== toast2.id));
|
|
2392
|
+
setTimeout(() => {
|
|
2393
|
+
removeToast(toast2);
|
|
2394
|
+
}, TIME_BEFORE_UNMOUNT);
|
|
2395
|
+
}, [
|
|
2396
|
+
toast2,
|
|
2397
|
+
removeToast,
|
|
2398
|
+
setHeights,
|
|
2399
|
+
offset
|
|
2400
|
+
]);
|
|
2401
|
+
React__default.default.useEffect(() => {
|
|
2402
|
+
if (toast2.promise && toastType === "loading" || toast2.duration === Infinity || toast2.type === "loading") return;
|
|
2403
|
+
let timeoutId;
|
|
2404
|
+
const pauseTimer = () => {
|
|
2405
|
+
if (lastCloseTimerStartTimeRef.current < closeTimerStartTimeRef.current) {
|
|
2406
|
+
const elapsedTime = (/* @__PURE__ */ new Date()).getTime() - closeTimerStartTimeRef.current;
|
|
2407
|
+
remainingTime.current = remainingTime.current - elapsedTime;
|
|
2408
|
+
}
|
|
2409
|
+
lastCloseTimerStartTimeRef.current = (/* @__PURE__ */ new Date()).getTime();
|
|
2410
|
+
};
|
|
2411
|
+
const startTimer = () => {
|
|
2412
|
+
if (remainingTime.current === Infinity) return;
|
|
2413
|
+
closeTimerStartTimeRef.current = (/* @__PURE__ */ new Date()).getTime();
|
|
2414
|
+
timeoutId = setTimeout(() => {
|
|
2415
|
+
toast2.onAutoClose == null ? void 0 : toast2.onAutoClose.call(toast2, toast2);
|
|
2416
|
+
deleteToast();
|
|
2417
|
+
}, remainingTime.current);
|
|
2418
|
+
};
|
|
2419
|
+
if (expanded || interacting || isDocumentHidden) {
|
|
2420
|
+
pauseTimer();
|
|
1572
2421
|
} else {
|
|
1573
|
-
|
|
2422
|
+
startTimer();
|
|
2423
|
+
}
|
|
2424
|
+
return () => clearTimeout(timeoutId);
|
|
2425
|
+
}, [
|
|
2426
|
+
expanded,
|
|
2427
|
+
interacting,
|
|
2428
|
+
toast2,
|
|
2429
|
+
toastType,
|
|
2430
|
+
isDocumentHidden,
|
|
2431
|
+
deleteToast
|
|
2432
|
+
]);
|
|
2433
|
+
React__default.default.useEffect(() => {
|
|
2434
|
+
if (toast2.delete) {
|
|
2435
|
+
deleteToast();
|
|
2436
|
+
toast2.onDismiss == null ? void 0 : toast2.onDismiss.call(toast2, toast2);
|
|
2437
|
+
}
|
|
2438
|
+
}, [
|
|
2439
|
+
deleteToast,
|
|
2440
|
+
toast2.delete
|
|
2441
|
+
]);
|
|
2442
|
+
function getLoadingIcon() {
|
|
2443
|
+
var _toast_classNames9;
|
|
2444
|
+
if (icons == null ? void 0 : icons.loading) {
|
|
2445
|
+
var _toast_classNames12;
|
|
2446
|
+
return /* @__PURE__ */ React__default.default.createElement("div", {
|
|
2447
|
+
className: cn(classNames == null ? void 0 : classNames.loader, toast2 == null ? void 0 : (_toast_classNames12 = toast2.classNames) == null ? void 0 : _toast_classNames12.loader, "sonner-loader"),
|
|
2448
|
+
"data-visible": toastType === "loading"
|
|
2449
|
+
}, icons.loading);
|
|
2450
|
+
}
|
|
2451
|
+
return /* @__PURE__ */ React__default.default.createElement(Loader, {
|
|
2452
|
+
className: cn(classNames == null ? void 0 : classNames.loader, toast2 == null ? void 0 : (_toast_classNames9 = toast2.classNames) == null ? void 0 : _toast_classNames9.loader),
|
|
2453
|
+
visible: toastType === "loading"
|
|
2454
|
+
});
|
|
2455
|
+
}
|
|
2456
|
+
const icon = toast2.icon || (icons == null ? void 0 : icons[toastType]) || getAsset(toastType);
|
|
2457
|
+
var _toast_richColors, _icons_close;
|
|
2458
|
+
return /* @__PURE__ */ React__default.default.createElement("li", {
|
|
2459
|
+
tabIndex: 0,
|
|
2460
|
+
ref: toastRef,
|
|
2461
|
+
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]),
|
|
2462
|
+
"data-sonner-toast": "",
|
|
2463
|
+
"data-rich-colors": (_toast_richColors = toast2.richColors) != null ? _toast_richColors : defaultRichColors,
|
|
2464
|
+
"data-styled": !Boolean(toast2.jsx || toast2.unstyled || unstyled),
|
|
2465
|
+
"data-mounted": mounted,
|
|
2466
|
+
"data-promise": Boolean(toast2.promise),
|
|
2467
|
+
"data-swiped": isSwiped,
|
|
2468
|
+
"data-removed": removed,
|
|
2469
|
+
"data-visible": isVisible,
|
|
2470
|
+
"data-y-position": y,
|
|
2471
|
+
"data-x-position": x,
|
|
2472
|
+
"data-index": index,
|
|
2473
|
+
"data-front": isFront,
|
|
2474
|
+
"data-swiping": swiping,
|
|
2475
|
+
"data-dismissible": dismissible,
|
|
2476
|
+
"data-type": toastType,
|
|
2477
|
+
"data-invert": invert,
|
|
2478
|
+
"data-swipe-out": swipeOut,
|
|
2479
|
+
"data-swipe-direction": swipeOutDirection,
|
|
2480
|
+
"data-expanded": Boolean(expanded || expandByDefault && mounted),
|
|
2481
|
+
"data-testid": toast2.testId,
|
|
2482
|
+
style: {
|
|
2483
|
+
"--index": index,
|
|
2484
|
+
"--toasts-before": index,
|
|
2485
|
+
"--z-index": toasts.length - index,
|
|
2486
|
+
"--offset": `${removed ? offsetBeforeRemove : offset.current}px`,
|
|
2487
|
+
"--initial-height": expandByDefault ? "auto" : `${initialHeight}px`,
|
|
2488
|
+
...style,
|
|
2489
|
+
...toast2.style
|
|
2490
|
+
},
|
|
2491
|
+
onDragEnd: () => {
|
|
2492
|
+
setSwiping(false);
|
|
2493
|
+
setSwipeDirection(null);
|
|
2494
|
+
pointerStartRef.current = null;
|
|
2495
|
+
},
|
|
2496
|
+
onPointerDown: (event) => {
|
|
2497
|
+
if (event.button === 2) return;
|
|
2498
|
+
if (disabled || !dismissible) return;
|
|
2499
|
+
dragStartTime.current = /* @__PURE__ */ new Date();
|
|
2500
|
+
setOffsetBeforeRemove(offset.current);
|
|
2501
|
+
event.target.setPointerCapture(event.pointerId);
|
|
2502
|
+
if (event.target.tagName === "BUTTON") return;
|
|
2503
|
+
setSwiping(true);
|
|
2504
|
+
pointerStartRef.current = {
|
|
2505
|
+
x: event.clientX,
|
|
2506
|
+
y: event.clientY
|
|
2507
|
+
};
|
|
2508
|
+
},
|
|
2509
|
+
onPointerUp: () => {
|
|
2510
|
+
var _toastRef_current, _toastRef_current1, _dragStartTime_current;
|
|
2511
|
+
if (swipeOut || !dismissible) return;
|
|
2512
|
+
pointerStartRef.current = null;
|
|
2513
|
+
const swipeAmountX = Number(((_toastRef_current = toastRef.current) == null ? void 0 : _toastRef_current.style.getPropertyValue("--swipe-amount-x").replace("px", "")) || 0);
|
|
2514
|
+
const swipeAmountY = Number(((_toastRef_current1 = toastRef.current) == null ? void 0 : _toastRef_current1.style.getPropertyValue("--swipe-amount-y").replace("px", "")) || 0);
|
|
2515
|
+
const timeTaken = (/* @__PURE__ */ new Date()).getTime() - ((_dragStartTime_current = dragStartTime.current) == null ? void 0 : _dragStartTime_current.getTime());
|
|
2516
|
+
const swipeAmount = swipeDirection === "x" ? swipeAmountX : swipeAmountY;
|
|
2517
|
+
const velocity = Math.abs(swipeAmount) / timeTaken;
|
|
2518
|
+
if (Math.abs(swipeAmount) >= SWIPE_THRESHOLD || velocity > 0.11) {
|
|
2519
|
+
setOffsetBeforeRemove(offset.current);
|
|
2520
|
+
toast2.onDismiss == null ? void 0 : toast2.onDismiss.call(toast2, toast2);
|
|
2521
|
+
if (swipeDirection === "x") {
|
|
2522
|
+
setSwipeOutDirection(swipeAmountX > 0 ? "right" : "left");
|
|
2523
|
+
} else {
|
|
2524
|
+
setSwipeOutDirection(swipeAmountY > 0 ? "down" : "up");
|
|
2525
|
+
}
|
|
2526
|
+
deleteToast();
|
|
2527
|
+
setSwipeOut(true);
|
|
2528
|
+
return;
|
|
2529
|
+
} else {
|
|
2530
|
+
var _toastRef_current2, _toastRef_current3;
|
|
2531
|
+
(_toastRef_current2 = toastRef.current) == null ? void 0 : _toastRef_current2.style.setProperty("--swipe-amount-x", `0px`);
|
|
2532
|
+
(_toastRef_current3 = toastRef.current) == null ? void 0 : _toastRef_current3.style.setProperty("--swipe-amount-y", `0px`);
|
|
2533
|
+
}
|
|
2534
|
+
setIsSwiped(false);
|
|
2535
|
+
setSwiping(false);
|
|
2536
|
+
setSwipeDirection(null);
|
|
2537
|
+
},
|
|
2538
|
+
onPointerMove: (event) => {
|
|
2539
|
+
var _window_getSelection, _toastRef_current, _toastRef_current1;
|
|
2540
|
+
if (!pointerStartRef.current || !dismissible) return;
|
|
2541
|
+
const isHighlighted = ((_window_getSelection = window.getSelection()) == null ? void 0 : _window_getSelection.toString().length) > 0;
|
|
2542
|
+
if (isHighlighted) return;
|
|
2543
|
+
const yDelta = event.clientY - pointerStartRef.current.y;
|
|
2544
|
+
const xDelta = event.clientX - pointerStartRef.current.x;
|
|
2545
|
+
var _props_swipeDirections;
|
|
2546
|
+
const swipeDirections = (_props_swipeDirections = props.swipeDirections) != null ? _props_swipeDirections : getDefaultSwipeDirections(position);
|
|
2547
|
+
if (!swipeDirection && (Math.abs(xDelta) > 1 || Math.abs(yDelta) > 1)) {
|
|
2548
|
+
setSwipeDirection(Math.abs(xDelta) > Math.abs(yDelta) ? "x" : "y");
|
|
2549
|
+
}
|
|
2550
|
+
let swipeAmount = {
|
|
2551
|
+
x: 0,
|
|
2552
|
+
y: 0
|
|
2553
|
+
};
|
|
2554
|
+
const getDampening = (delta) => {
|
|
2555
|
+
const factor = Math.abs(delta) / 20;
|
|
2556
|
+
return 1 / (1.5 + factor);
|
|
2557
|
+
};
|
|
2558
|
+
if (swipeDirection === "y") {
|
|
2559
|
+
if (swipeDirections.includes("top") || swipeDirections.includes("bottom")) {
|
|
2560
|
+
if (swipeDirections.includes("top") && yDelta < 0 || swipeDirections.includes("bottom") && yDelta > 0) {
|
|
2561
|
+
swipeAmount.y = yDelta;
|
|
2562
|
+
} else {
|
|
2563
|
+
const dampenedDelta = yDelta * getDampening(yDelta);
|
|
2564
|
+
swipeAmount.y = Math.abs(dampenedDelta) < Math.abs(yDelta) ? dampenedDelta : yDelta;
|
|
2565
|
+
}
|
|
2566
|
+
}
|
|
2567
|
+
} else if (swipeDirection === "x") {
|
|
2568
|
+
if (swipeDirections.includes("left") || swipeDirections.includes("right")) {
|
|
2569
|
+
if (swipeDirections.includes("left") && xDelta < 0 || swipeDirections.includes("right") && xDelta > 0) {
|
|
2570
|
+
swipeAmount.x = xDelta;
|
|
2571
|
+
} else {
|
|
2572
|
+
const dampenedDelta = xDelta * getDampening(xDelta);
|
|
2573
|
+
swipeAmount.x = Math.abs(dampenedDelta) < Math.abs(xDelta) ? dampenedDelta : xDelta;
|
|
2574
|
+
}
|
|
2575
|
+
}
|
|
2576
|
+
}
|
|
2577
|
+
if (Math.abs(swipeAmount.x) > 0 || Math.abs(swipeAmount.y) > 0) {
|
|
2578
|
+
setIsSwiped(true);
|
|
2579
|
+
}
|
|
2580
|
+
(_toastRef_current = toastRef.current) == null ? void 0 : _toastRef_current.style.setProperty("--swipe-amount-x", `${swipeAmount.x}px`);
|
|
2581
|
+
(_toastRef_current1 = toastRef.current) == null ? void 0 : _toastRef_current1.style.setProperty("--swipe-amount-y", `${swipeAmount.y}px`);
|
|
2582
|
+
}
|
|
2583
|
+
}, closeButton && !toast2.jsx && toastType !== "loading" ? /* @__PURE__ */ React__default.default.createElement("button", {
|
|
2584
|
+
"aria-label": closeButtonAriaLabel,
|
|
2585
|
+
"data-disabled": disabled,
|
|
2586
|
+
"data-close-button": true,
|
|
2587
|
+
onClick: disabled || !dismissible ? () => {
|
|
2588
|
+
} : () => {
|
|
2589
|
+
deleteToast();
|
|
2590
|
+
toast2.onDismiss == null ? void 0 : toast2.onDismiss.call(toast2, toast2);
|
|
2591
|
+
},
|
|
2592
|
+
className: cn(classNames == null ? void 0 : classNames.closeButton, toast2 == null ? void 0 : (_toast_classNames2 = toast2.classNames) == null ? void 0 : _toast_classNames2.closeButton)
|
|
2593
|
+
}, (_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", {
|
|
2594
|
+
"data-icon": "",
|
|
2595
|
+
className: cn(classNames == null ? void 0 : classNames.icon, toast2 == null ? void 0 : (_toast_classNames3 = toast2.classNames) == null ? void 0 : _toast_classNames3.icon)
|
|
2596
|
+
}, toast2.promise || toast2.type === "loading" && !toast2.icon ? toast2.icon || getLoadingIcon() : null, toast2.type !== "loading" ? icon : null) : null, /* @__PURE__ */ React__default.default.createElement("div", {
|
|
2597
|
+
"data-content": "",
|
|
2598
|
+
className: cn(classNames == null ? void 0 : classNames.content, toast2 == null ? void 0 : (_toast_classNames4 = toast2.classNames) == null ? void 0 : _toast_classNames4.content)
|
|
2599
|
+
}, /* @__PURE__ */ React__default.default.createElement("div", {
|
|
2600
|
+
"data-title": "",
|
|
2601
|
+
className: cn(classNames == null ? void 0 : classNames.title, toast2 == null ? void 0 : (_toast_classNames5 = toast2.classNames) == null ? void 0 : _toast_classNames5.title)
|
|
2602
|
+
}, toast2.jsx ? toast2.jsx : typeof toast2.title === "function" ? toast2.title() : toast2.title), toast2.description ? /* @__PURE__ */ React__default.default.createElement("div", {
|
|
2603
|
+
"data-description": "",
|
|
2604
|
+
className: cn(descriptionClassName, toastDescriptionClassname, classNames == null ? void 0 : classNames.description, toast2 == null ? void 0 : (_toast_classNames6 = toast2.classNames) == null ? void 0 : _toast_classNames6.description)
|
|
2605
|
+
}, 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", {
|
|
2606
|
+
"data-button": true,
|
|
2607
|
+
"data-cancel": true,
|
|
2608
|
+
style: toast2.cancelButtonStyle || cancelButtonStyle,
|
|
2609
|
+
onClick: (event) => {
|
|
2610
|
+
if (!isAction(toast2.cancel)) return;
|
|
2611
|
+
if (!dismissible) return;
|
|
2612
|
+
toast2.cancel.onClick == null ? void 0 : toast2.cancel.onClick.call(toast2.cancel, event);
|
|
2613
|
+
deleteToast();
|
|
2614
|
+
},
|
|
2615
|
+
className: cn(classNames == null ? void 0 : classNames.cancelButton, toast2 == null ? void 0 : (_toast_classNames7 = toast2.classNames) == null ? void 0 : _toast_classNames7.cancelButton)
|
|
2616
|
+
}, toast2.cancel.label) : null, /* @__PURE__ */ React__default.default.isValidElement(toast2.action) ? toast2.action : toast2.action && isAction(toast2.action) ? /* @__PURE__ */ React__default.default.createElement("button", {
|
|
2617
|
+
"data-button": true,
|
|
2618
|
+
"data-action": true,
|
|
2619
|
+
style: toast2.actionButtonStyle || actionButtonStyle,
|
|
2620
|
+
onClick: (event) => {
|
|
2621
|
+
if (!isAction(toast2.action)) return;
|
|
2622
|
+
toast2.action.onClick == null ? void 0 : toast2.action.onClick.call(toast2.action, event);
|
|
2623
|
+
if (event.defaultPrevented) return;
|
|
2624
|
+
deleteToast();
|
|
2625
|
+
},
|
|
2626
|
+
className: cn(classNames == null ? void 0 : classNames.actionButton, toast2 == null ? void 0 : (_toast_classNames8 = toast2.classNames) == null ? void 0 : _toast_classNames8.actionButton)
|
|
2627
|
+
}, toast2.action.label) : null);
|
|
2628
|
+
};
|
|
2629
|
+
function getDocumentDirection() {
|
|
2630
|
+
if (typeof window === "undefined") return "ltr";
|
|
2631
|
+
if (typeof document === "undefined") return "ltr";
|
|
2632
|
+
const dirAttribute = document.documentElement.getAttribute("dir");
|
|
2633
|
+
if (dirAttribute === "auto" || !dirAttribute) {
|
|
2634
|
+
return window.getComputedStyle(document.documentElement).direction;
|
|
2635
|
+
}
|
|
2636
|
+
return dirAttribute;
|
|
2637
|
+
}
|
|
2638
|
+
function assignOffset(defaultOffset, mobileOffset) {
|
|
2639
|
+
const styles = {};
|
|
2640
|
+
[
|
|
2641
|
+
defaultOffset,
|
|
2642
|
+
mobileOffset
|
|
2643
|
+
].forEach((offset, index) => {
|
|
2644
|
+
const isMobile = index === 1;
|
|
2645
|
+
const prefix = isMobile ? "--mobile-offset" : "--offset";
|
|
2646
|
+
const defaultValue = isMobile ? MOBILE_VIEWPORT_OFFSET : VIEWPORT_OFFSET;
|
|
2647
|
+
function assignAll(offset2) {
|
|
2648
|
+
[
|
|
2649
|
+
"top",
|
|
2650
|
+
"right",
|
|
2651
|
+
"bottom",
|
|
2652
|
+
"left"
|
|
2653
|
+
].forEach((key) => {
|
|
2654
|
+
styles[`${prefix}-${key}`] = typeof offset2 === "number" ? `${offset2}px` : offset2;
|
|
2655
|
+
});
|
|
2656
|
+
}
|
|
2657
|
+
if (typeof offset === "number" || typeof offset === "string") {
|
|
2658
|
+
assignAll(offset);
|
|
2659
|
+
} else if (typeof offset === "object") {
|
|
2660
|
+
[
|
|
2661
|
+
"top",
|
|
2662
|
+
"right",
|
|
2663
|
+
"bottom",
|
|
2664
|
+
"left"
|
|
2665
|
+
].forEach((key) => {
|
|
2666
|
+
if (offset[key] === void 0) {
|
|
2667
|
+
styles[`${prefix}-${key}`] = defaultValue;
|
|
2668
|
+
} else {
|
|
2669
|
+
styles[`${prefix}-${key}`] = typeof offset[key] === "number" ? `${offset[key]}px` : offset[key];
|
|
2670
|
+
}
|
|
2671
|
+
});
|
|
2672
|
+
} else {
|
|
2673
|
+
assignAll(defaultValue);
|
|
1574
2674
|
}
|
|
1575
|
-
};
|
|
1576
|
-
return trigger(start, {
|
|
1577
|
-
asChild,
|
|
1578
|
-
children: children ?? "Add to my plan",
|
|
1579
|
-
className,
|
|
1580
|
-
disabled: checkout.isLoading
|
|
1581
2675
|
});
|
|
1582
|
-
|
|
2676
|
+
return styles;
|
|
2677
|
+
}
|
|
2678
|
+
var Toaster = /* @__PURE__ */ React__default.default.forwardRef(function Toaster2(props, ref) {
|
|
2679
|
+
const { id, invert, position = "bottom-right", hotkey = [
|
|
2680
|
+
"altKey",
|
|
2681
|
+
"KeyT"
|
|
2682
|
+
], expand, closeButton, className, offset, mobileOffset, theme = "light", richColors, duration, style, visibleToasts = VISIBLE_TOASTS_AMOUNT, toastOptions, dir = getDocumentDirection(), gap = GAP, icons, containerAriaLabel = "Notifications" } = props;
|
|
2683
|
+
const [toasts, setToasts] = React__default.default.useState([]);
|
|
2684
|
+
const filteredToasts = React__default.default.useMemo(() => {
|
|
2685
|
+
if (id) {
|
|
2686
|
+
return toasts.filter((toast2) => toast2.toasterId === id);
|
|
2687
|
+
}
|
|
2688
|
+
return toasts.filter((toast2) => !toast2.toasterId);
|
|
2689
|
+
}, [
|
|
2690
|
+
toasts,
|
|
2691
|
+
id
|
|
2692
|
+
]);
|
|
2693
|
+
const possiblePositions = React__default.default.useMemo(() => {
|
|
2694
|
+
return Array.from(new Set([
|
|
2695
|
+
position
|
|
2696
|
+
].concat(filteredToasts.filter((toast2) => toast2.position).map((toast2) => toast2.position))));
|
|
2697
|
+
}, [
|
|
2698
|
+
filteredToasts,
|
|
2699
|
+
position
|
|
2700
|
+
]);
|
|
2701
|
+
const [heights, setHeights] = React__default.default.useState([]);
|
|
2702
|
+
const [expanded, setExpanded] = React__default.default.useState(false);
|
|
2703
|
+
const [interacting, setInteracting] = React__default.default.useState(false);
|
|
2704
|
+
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");
|
|
2705
|
+
const listRef = React__default.default.useRef(null);
|
|
2706
|
+
const hotkeyLabel = hotkey.join("+").replace(/Key/g, "").replace(/Digit/g, "");
|
|
2707
|
+
const lastFocusedElementRef = React__default.default.useRef(null);
|
|
2708
|
+
const isFocusWithinRef = React__default.default.useRef(false);
|
|
2709
|
+
const removeToast = React__default.default.useCallback((toastToRemove) => {
|
|
2710
|
+
setToasts((toasts2) => {
|
|
2711
|
+
var _toasts_find;
|
|
2712
|
+
if (!((_toasts_find = toasts2.find((toast2) => toast2.id === toastToRemove.id)) == null ? void 0 : _toasts_find.delete)) {
|
|
2713
|
+
ToastState.dismiss(toastToRemove.id);
|
|
2714
|
+
}
|
|
2715
|
+
return toasts2.filter(({ id: id2 }) => id2 !== toastToRemove.id);
|
|
2716
|
+
});
|
|
2717
|
+
}, []);
|
|
2718
|
+
React__default.default.useEffect(() => {
|
|
2719
|
+
return ToastState.subscribe((toast2) => {
|
|
2720
|
+
if (toast2.dismiss) {
|
|
2721
|
+
requestAnimationFrame(() => {
|
|
2722
|
+
setToasts((toasts2) => toasts2.map((t) => t.id === toast2.id ? {
|
|
2723
|
+
...t,
|
|
2724
|
+
delete: true
|
|
2725
|
+
} : t));
|
|
2726
|
+
});
|
|
2727
|
+
return;
|
|
2728
|
+
}
|
|
2729
|
+
setTimeout(() => {
|
|
2730
|
+
ReactDOM__default.default.flushSync(() => {
|
|
2731
|
+
setToasts((toasts2) => {
|
|
2732
|
+
const indexOfExistingToast = toasts2.findIndex((t) => t.id === toast2.id);
|
|
2733
|
+
if (indexOfExistingToast !== -1) {
|
|
2734
|
+
return [
|
|
2735
|
+
...toasts2.slice(0, indexOfExistingToast),
|
|
2736
|
+
{
|
|
2737
|
+
...toasts2[indexOfExistingToast],
|
|
2738
|
+
...toast2
|
|
2739
|
+
},
|
|
2740
|
+
...toasts2.slice(indexOfExistingToast + 1)
|
|
2741
|
+
];
|
|
2742
|
+
}
|
|
2743
|
+
return [
|
|
2744
|
+
toast2,
|
|
2745
|
+
...toasts2
|
|
2746
|
+
];
|
|
2747
|
+
});
|
|
2748
|
+
});
|
|
2749
|
+
});
|
|
2750
|
+
});
|
|
2751
|
+
}, [
|
|
2752
|
+
toasts
|
|
2753
|
+
]);
|
|
2754
|
+
React__default.default.useEffect(() => {
|
|
2755
|
+
if (theme !== "system") {
|
|
2756
|
+
setActualTheme(theme);
|
|
2757
|
+
return;
|
|
2758
|
+
}
|
|
2759
|
+
if (theme === "system") {
|
|
2760
|
+
if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) {
|
|
2761
|
+
setActualTheme("dark");
|
|
2762
|
+
} else {
|
|
2763
|
+
setActualTheme("light");
|
|
2764
|
+
}
|
|
2765
|
+
}
|
|
2766
|
+
if (typeof window === "undefined") return;
|
|
2767
|
+
const darkMediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
2768
|
+
try {
|
|
2769
|
+
darkMediaQuery.addEventListener("change", ({ matches }) => {
|
|
2770
|
+
if (matches) {
|
|
2771
|
+
setActualTheme("dark");
|
|
2772
|
+
} else {
|
|
2773
|
+
setActualTheme("light");
|
|
2774
|
+
}
|
|
2775
|
+
});
|
|
2776
|
+
} catch (error) {
|
|
2777
|
+
darkMediaQuery.addListener(({ matches }) => {
|
|
2778
|
+
try {
|
|
2779
|
+
if (matches) {
|
|
2780
|
+
setActualTheme("dark");
|
|
2781
|
+
} else {
|
|
2782
|
+
setActualTheme("light");
|
|
2783
|
+
}
|
|
2784
|
+
} catch (e) {
|
|
2785
|
+
console.error(e);
|
|
2786
|
+
}
|
|
2787
|
+
});
|
|
2788
|
+
}
|
|
2789
|
+
}, [
|
|
2790
|
+
theme
|
|
2791
|
+
]);
|
|
2792
|
+
React__default.default.useEffect(() => {
|
|
2793
|
+
if (toasts.length <= 1) {
|
|
2794
|
+
setExpanded(false);
|
|
2795
|
+
}
|
|
2796
|
+
}, [
|
|
2797
|
+
toasts
|
|
2798
|
+
]);
|
|
2799
|
+
React__default.default.useEffect(() => {
|
|
2800
|
+
const handleKeyDown = (event) => {
|
|
2801
|
+
var _listRef_current;
|
|
2802
|
+
const isHotkeyPressed = hotkey.every((key) => event[key] || event.code === key);
|
|
2803
|
+
if (isHotkeyPressed) {
|
|
2804
|
+
var _listRef_current1;
|
|
2805
|
+
setExpanded(true);
|
|
2806
|
+
(_listRef_current1 = listRef.current) == null ? void 0 : _listRef_current1.focus();
|
|
2807
|
+
}
|
|
2808
|
+
if (event.code === "Escape" && (document.activeElement === listRef.current || ((_listRef_current = listRef.current) == null ? void 0 : _listRef_current.contains(document.activeElement)))) {
|
|
2809
|
+
setExpanded(false);
|
|
2810
|
+
}
|
|
2811
|
+
};
|
|
2812
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
2813
|
+
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
2814
|
+
}, [
|
|
2815
|
+
hotkey
|
|
2816
|
+
]);
|
|
2817
|
+
React__default.default.useEffect(() => {
|
|
2818
|
+
if (listRef.current) {
|
|
2819
|
+
return () => {
|
|
2820
|
+
if (lastFocusedElementRef.current) {
|
|
2821
|
+
lastFocusedElementRef.current.focus({
|
|
2822
|
+
preventScroll: true
|
|
2823
|
+
});
|
|
2824
|
+
lastFocusedElementRef.current = null;
|
|
2825
|
+
isFocusWithinRef.current = false;
|
|
2826
|
+
}
|
|
2827
|
+
};
|
|
2828
|
+
}
|
|
2829
|
+
}, [
|
|
2830
|
+
listRef.current
|
|
2831
|
+
]);
|
|
2832
|
+
return (
|
|
2833
|
+
// Remove item from normal navigation flow, only available via hotkey
|
|
2834
|
+
/* @__PURE__ */ React__default.default.createElement("section", {
|
|
2835
|
+
ref,
|
|
2836
|
+
"aria-label": `${containerAriaLabel} ${hotkeyLabel}`,
|
|
2837
|
+
tabIndex: -1,
|
|
2838
|
+
"aria-live": "polite",
|
|
2839
|
+
"aria-relevant": "additions text",
|
|
2840
|
+
"aria-atomic": "false",
|
|
2841
|
+
suppressHydrationWarning: true
|
|
2842
|
+
}, possiblePositions.map((position2, index) => {
|
|
2843
|
+
var _heights_;
|
|
2844
|
+
const [y, x] = position2.split("-");
|
|
2845
|
+
if (!filteredToasts.length) return null;
|
|
2846
|
+
return /* @__PURE__ */ React__default.default.createElement("ol", {
|
|
2847
|
+
key: position2,
|
|
2848
|
+
dir: dir === "auto" ? getDocumentDirection() : dir,
|
|
2849
|
+
tabIndex: -1,
|
|
2850
|
+
ref: listRef,
|
|
2851
|
+
className,
|
|
2852
|
+
"data-sonner-toaster": true,
|
|
2853
|
+
"data-sonner-theme": actualTheme,
|
|
2854
|
+
"data-y-position": y,
|
|
2855
|
+
"data-x-position": x,
|
|
2856
|
+
style: {
|
|
2857
|
+
"--front-toast-height": `${((_heights_ = heights[0]) == null ? void 0 : _heights_.height) || 0}px`,
|
|
2858
|
+
"--width": `${TOAST_WIDTH}px`,
|
|
2859
|
+
"--gap": `${gap}px`,
|
|
2860
|
+
...style,
|
|
2861
|
+
...assignOffset(offset, mobileOffset)
|
|
2862
|
+
},
|
|
2863
|
+
onBlur: (event) => {
|
|
2864
|
+
if (isFocusWithinRef.current && !event.currentTarget.contains(event.relatedTarget)) {
|
|
2865
|
+
isFocusWithinRef.current = false;
|
|
2866
|
+
if (lastFocusedElementRef.current) {
|
|
2867
|
+
lastFocusedElementRef.current.focus({
|
|
2868
|
+
preventScroll: true
|
|
2869
|
+
});
|
|
2870
|
+
lastFocusedElementRef.current = null;
|
|
2871
|
+
}
|
|
2872
|
+
}
|
|
2873
|
+
},
|
|
2874
|
+
onFocus: (event) => {
|
|
2875
|
+
const isNotDismissible = event.target instanceof HTMLElement && event.target.dataset.dismissible === "false";
|
|
2876
|
+
if (isNotDismissible) return;
|
|
2877
|
+
if (!isFocusWithinRef.current) {
|
|
2878
|
+
isFocusWithinRef.current = true;
|
|
2879
|
+
lastFocusedElementRef.current = event.relatedTarget;
|
|
2880
|
+
}
|
|
2881
|
+
},
|
|
2882
|
+
onMouseEnter: () => setExpanded(true),
|
|
2883
|
+
onMouseMove: () => setExpanded(true),
|
|
2884
|
+
onMouseLeave: () => {
|
|
2885
|
+
if (!interacting) {
|
|
2886
|
+
setExpanded(false);
|
|
2887
|
+
}
|
|
2888
|
+
},
|
|
2889
|
+
onDragEnd: () => setExpanded(false),
|
|
2890
|
+
onPointerDown: (event) => {
|
|
2891
|
+
const isNotDismissible = event.target instanceof HTMLElement && event.target.dataset.dismissible === "false";
|
|
2892
|
+
if (isNotDismissible) return;
|
|
2893
|
+
setInteracting(true);
|
|
2894
|
+
},
|
|
2895
|
+
onPointerUp: () => setInteracting(false)
|
|
2896
|
+
}, filteredToasts.filter((toast2) => !toast2.position && index === 0 || toast2.position === position2).map((toast2, index2) => {
|
|
2897
|
+
var _toastOptions_duration, _toastOptions_closeButton;
|
|
2898
|
+
return /* @__PURE__ */ React__default.default.createElement(Toast, {
|
|
2899
|
+
key: toast2.id,
|
|
2900
|
+
icons,
|
|
2901
|
+
index: index2,
|
|
2902
|
+
toast: toast2,
|
|
2903
|
+
defaultRichColors: richColors,
|
|
2904
|
+
duration: (_toastOptions_duration = toastOptions == null ? void 0 : toastOptions.duration) != null ? _toastOptions_duration : duration,
|
|
2905
|
+
className: toastOptions == null ? void 0 : toastOptions.className,
|
|
2906
|
+
descriptionClassName: toastOptions == null ? void 0 : toastOptions.descriptionClassName,
|
|
2907
|
+
invert,
|
|
2908
|
+
visibleToasts,
|
|
2909
|
+
closeButton: (_toastOptions_closeButton = toastOptions == null ? void 0 : toastOptions.closeButton) != null ? _toastOptions_closeButton : closeButton,
|
|
2910
|
+
interacting,
|
|
2911
|
+
position: position2,
|
|
2912
|
+
style: toastOptions == null ? void 0 : toastOptions.style,
|
|
2913
|
+
unstyled: toastOptions == null ? void 0 : toastOptions.unstyled,
|
|
2914
|
+
classNames: toastOptions == null ? void 0 : toastOptions.classNames,
|
|
2915
|
+
cancelButtonStyle: toastOptions == null ? void 0 : toastOptions.cancelButtonStyle,
|
|
2916
|
+
actionButtonStyle: toastOptions == null ? void 0 : toastOptions.actionButtonStyle,
|
|
2917
|
+
closeButtonAriaLabel: toastOptions == null ? void 0 : toastOptions.closeButtonAriaLabel,
|
|
2918
|
+
removeToast,
|
|
2919
|
+
toasts: filteredToasts.filter((t) => t.position == toast2.position),
|
|
2920
|
+
heights: heights.filter((h) => h.position == toast2.position),
|
|
2921
|
+
setHeights,
|
|
2922
|
+
expandByDefault: expand,
|
|
2923
|
+
gap,
|
|
2924
|
+
expanded,
|
|
2925
|
+
swipeDirections: props.swipeDirections
|
|
2926
|
+
});
|
|
2927
|
+
}));
|
|
2928
|
+
}))
|
|
2929
|
+
);
|
|
2930
|
+
});
|
|
1583
2931
|
function FunnelToaster(props = {}) {
|
|
1584
|
-
return
|
|
2932
|
+
return React.createElement(Toaster, { position: "top-center", ...props });
|
|
1585
2933
|
}
|
|
1586
2934
|
function renderToaster(toaster) {
|
|
1587
2935
|
if (toaster === false) return null;
|
|
1588
|
-
return
|
|
2936
|
+
return /* @__PURE__ */ jsxRuntime.jsx(FunnelToaster, { ...toaster === true ? {} : toaster });
|
|
1589
2937
|
}
|
|
1590
2938
|
|
|
1591
2939
|
// src/state/namespaces.ts
|
|
@@ -1609,7 +2957,8 @@ var Namespace = class {
|
|
|
1609
2957
|
const out = {};
|
|
1610
2958
|
const all = this.store.getState();
|
|
1611
2959
|
for (const key in all) {
|
|
1612
|
-
if (key.startsWith(this.dot))
|
|
2960
|
+
if (key.startsWith(this.dot))
|
|
2961
|
+
out[key.slice(this.dot.length)] = all[key];
|
|
1613
2962
|
}
|
|
1614
2963
|
return out;
|
|
1615
2964
|
}
|
|
@@ -1651,13 +3000,14 @@ function createFunnel(store, context, tracker, experiments = []) {
|
|
|
1651
3000
|
})
|
|
1652
3001
|
};
|
|
1653
3002
|
}
|
|
1654
|
-
var FunnelCtx =
|
|
3003
|
+
var FunnelCtx = React.createContext(null);
|
|
1655
3004
|
function FunnelProvider({
|
|
1656
3005
|
config,
|
|
1657
3006
|
sessionValues,
|
|
3007
|
+
deferSessionValues = false,
|
|
1658
3008
|
tracker,
|
|
1659
3009
|
context: contextOpts,
|
|
1660
|
-
|
|
3010
|
+
offerings,
|
|
1661
3011
|
experiments,
|
|
1662
3012
|
checkout,
|
|
1663
3013
|
messages,
|
|
@@ -1667,10 +3017,15 @@ function FunnelProvider({
|
|
|
1667
3017
|
children
|
|
1668
3018
|
}) {
|
|
1669
3019
|
const resolvedTracker = tracker ?? FALLBACK_TRACKER;
|
|
1670
|
-
const [value] =
|
|
3020
|
+
const [value] = React.useState(() => {
|
|
1671
3021
|
const s = store ?? createFunnelStore(
|
|
1672
3022
|
{ responses: config?.responses, data: config?.data },
|
|
1673
|
-
|
|
3023
|
+
// FLASH-FREE HYDRATION: with deferSessionValues the initial render is seeded with
|
|
3024
|
+
// DEFAULTS ONLY, so the client's first render is byte-identical to the build-time
|
|
3025
|
+
// DEFAULT-state prerender it hydrates against (clean match). The visitor's saved
|
|
3026
|
+
// answers arrive as a post-hydration setMany below. Without the flag, sessionValues
|
|
3027
|
+
// seed the store at construction (SSR + non-prerendered paths).
|
|
3028
|
+
deferSessionValues ? void 0 : sessionValues
|
|
1674
3029
|
);
|
|
1675
3030
|
const ctx = buildContext({
|
|
1676
3031
|
funnelId: config?.id,
|
|
@@ -1684,65 +3039,90 @@ function FunnelProvider({
|
|
|
1684
3039
|
getVisitorId: () => ctx.identity.visitorId
|
|
1685
3040
|
});
|
|
1686
3041
|
const busTracker = withBus(resolvedTracker, bus);
|
|
3042
|
+
const emailForCheckout = () => {
|
|
3043
|
+
const value2 = s.get("user.email");
|
|
3044
|
+
return typeof value2 === "string" && /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(value2) ? value2 : void 0;
|
|
3045
|
+
};
|
|
3046
|
+
const catalog = chunkY4YNJ2EX_cjs.buildCatalog(offerings ?? []);
|
|
1687
3047
|
return {
|
|
1688
3048
|
funnel: createFunnel(s, ctx, busTracker, experiments ?? []),
|
|
1689
3049
|
store: s,
|
|
1690
3050
|
context: ctx,
|
|
1691
|
-
catalog
|
|
1692
|
-
driver
|
|
3051
|
+
catalog,
|
|
3052
|
+
// Slot → catalog-key resolution wraps the email-enriching driver, so the wire charges the real
|
|
3053
|
+
// catalog key while pages + analytics keep the durable SLOT identity (req.product).
|
|
3054
|
+
driver: driverWithCatalog(
|
|
3055
|
+
driverWithEmail(
|
|
3056
|
+
checkout ?? createMockDriver(),
|
|
3057
|
+
emailForCheckout
|
|
3058
|
+
),
|
|
3059
|
+
catalog
|
|
3060
|
+
),
|
|
1693
3061
|
tracker: busTracker,
|
|
1694
3062
|
bus
|
|
1695
3063
|
};
|
|
1696
3064
|
});
|
|
1697
|
-
|
|
1698
|
-
|
|
3065
|
+
React.useEffect(() => attachBus(value.bus), [value.bus]);
|
|
3066
|
+
React.useEffect(() => {
|
|
3067
|
+
if (!deferSessionValues || !sessionValues) return;
|
|
3068
|
+
const updates = {};
|
|
3069
|
+
for (const [key, v] of Object.entries(sessionValues))
|
|
3070
|
+
if (v !== void 0) updates[key] = v;
|
|
3071
|
+
if (Object.keys(updates).length > 0) value.store.setMany(updates);
|
|
3072
|
+
}, []);
|
|
3073
|
+
React.useEffect(() => {
|
|
1699
3074
|
value.tracker.setAcquisition?.(buildAcquisition(value.context));
|
|
1700
3075
|
}, [value]);
|
|
1701
|
-
const tree =
|
|
3076
|
+
const tree = React.createElement(TrackerProvider, {
|
|
1702
3077
|
tracker: value.tracker,
|
|
1703
|
-
children:
|
|
3078
|
+
children: React.createElement(CheckoutDriverProvider, {
|
|
1704
3079
|
driver: value.driver,
|
|
1705
|
-
children:
|
|
3080
|
+
children: React.createElement(CatalogProvider, {
|
|
1706
3081
|
catalog: value.catalog,
|
|
1707
|
-
children:
|
|
3082
|
+
children: React.createElement(FunnelCtx.Provider, {
|
|
1708
3083
|
value,
|
|
1709
|
-
children:
|
|
3084
|
+
children: React.createElement(LocaleProvider, {
|
|
1710
3085
|
config: config?.locales,
|
|
1711
3086
|
catalog: messages,
|
|
1712
3087
|
detected: value.context.locale.locale,
|
|
1713
3088
|
override: localeOverride,
|
|
1714
|
-
children:
|
|
3089
|
+
children: React.createElement(ModalRuntime, { children })
|
|
1715
3090
|
})
|
|
1716
3091
|
})
|
|
1717
3092
|
})
|
|
1718
3093
|
})
|
|
1719
3094
|
});
|
|
1720
|
-
return
|
|
3095
|
+
return React.createElement(React.Fragment, null, tree, renderToaster(toaster));
|
|
1721
3096
|
}
|
|
1722
3097
|
var FALLBACK_TRACKER = createConsoleTracker({ silent: true });
|
|
1723
3098
|
function useFunnelCtx() {
|
|
1724
|
-
const ctx =
|
|
3099
|
+
const ctx = React.useContext(FunnelCtx);
|
|
1725
3100
|
if (!ctx) {
|
|
1726
|
-
throw new Error(
|
|
3101
|
+
throw new Error(
|
|
3102
|
+
"useFunnel/useResponse/\u2026 must be used inside <FunnelProvider>"
|
|
3103
|
+
);
|
|
1727
3104
|
}
|
|
1728
3105
|
return ctx;
|
|
1729
3106
|
}
|
|
1730
3107
|
function useFunnel() {
|
|
1731
3108
|
return useFunnelCtx().funnel;
|
|
1732
3109
|
}
|
|
3110
|
+
function useBusInternal() {
|
|
3111
|
+
return React.useContext(FunnelCtx)?.bus ?? null;
|
|
3112
|
+
}
|
|
1733
3113
|
function useStoreKey(store, key) {
|
|
1734
|
-
const subscribe =
|
|
3114
|
+
const subscribe = React.useCallback(
|
|
1735
3115
|
(cb) => store.subscribe(cb, { keys: [key] }),
|
|
1736
3116
|
[store, key]
|
|
1737
3117
|
);
|
|
1738
|
-
const getSnapshot =
|
|
1739
|
-
return
|
|
3118
|
+
const getSnapshot = React.useCallback(() => store.get(key), [store, key]);
|
|
3119
|
+
return React.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
1740
3120
|
}
|
|
1741
3121
|
function makeFieldHook(ns) {
|
|
1742
3122
|
return function useNamespaceField(key) {
|
|
1743
3123
|
const { funnel, store } = useFunnelCtx();
|
|
1744
3124
|
const value = useStoreKey(store, `${NAMESPACE_PREFIX[ns]}.${key}`);
|
|
1745
|
-
const set =
|
|
3125
|
+
const set = React.useCallback(
|
|
1746
3126
|
(v) => funnel[ns].set({ [key]: v }),
|
|
1747
3127
|
[funnel, key]
|
|
1748
3128
|
);
|
|
@@ -1758,10 +3138,12 @@ function useField(path) {
|
|
|
1758
3138
|
const key = path.slice(dot + 1);
|
|
1759
3139
|
const { funnel, store } = useFunnelCtx();
|
|
1760
3140
|
if (!(ns in NAMESPACE_PREFIX)) {
|
|
1761
|
-
throw new Error(
|
|
3141
|
+
throw new Error(
|
|
3142
|
+
`useField: "${path}" is not a writable namespace (user/responses/data)`
|
|
3143
|
+
);
|
|
1762
3144
|
}
|
|
1763
3145
|
const value = useStoreKey(store, `${NAMESPACE_PREFIX[ns]}.${key}`);
|
|
1764
|
-
const set =
|
|
3146
|
+
const set = React.useCallback(
|
|
1765
3147
|
(v) => funnel[ns].set({ [key]: v }),
|
|
1766
3148
|
[funnel, ns, key]
|
|
1767
3149
|
);
|
|
@@ -1787,11 +3169,404 @@ function useSystem() {
|
|
|
1787
3169
|
}
|
|
1788
3170
|
function useContextValue(path) {
|
|
1789
3171
|
const ctx = useContextObject();
|
|
1790
|
-
return
|
|
1791
|
-
() => path.split(".").reduce(
|
|
3172
|
+
return React.useMemo(
|
|
3173
|
+
() => path.split(".").reduce(
|
|
3174
|
+
(acc, k) => acc?.[k],
|
|
3175
|
+
ctx
|
|
3176
|
+
),
|
|
1792
3177
|
[ctx, path]
|
|
1793
3178
|
);
|
|
1794
3179
|
}
|
|
3180
|
+
|
|
3181
|
+
// src/tracking/snapshotPersistence.ts
|
|
3182
|
+
function createSnapshotPersistence(opts) {
|
|
3183
|
+
const baseDelay = opts.baseDelayMs ?? 1e3;
|
|
3184
|
+
const maxDelay = opts.maxDelayMs ?? 3e4;
|
|
3185
|
+
const setT = opts.timers?.setTimeout ?? ((fn, ms) => setTimeout(fn, ms));
|
|
3186
|
+
const clearT = opts.timers?.clearTimeout ?? ((h) => clearTimeout(h));
|
|
3187
|
+
const rand = opts.random ?? Math.random;
|
|
3188
|
+
let pending = null;
|
|
3189
|
+
let seq = 0;
|
|
3190
|
+
let pendingSeq = 0;
|
|
3191
|
+
let inFlight = false;
|
|
3192
|
+
let attempt = 0;
|
|
3193
|
+
let retryTimer = null;
|
|
3194
|
+
let disposed = false;
|
|
3195
|
+
let fatalSeq = -1;
|
|
3196
|
+
const clearRetry = () => {
|
|
3197
|
+
if (retryTimer !== null) {
|
|
3198
|
+
clearT(retryTimer);
|
|
3199
|
+
retryTimer = null;
|
|
3200
|
+
}
|
|
3201
|
+
};
|
|
3202
|
+
const buildBody = (snapshot, s) => JSON.stringify({ sessionId: opts.getSessionId(), userData: snapshot, seq: s });
|
|
3203
|
+
const trySend = () => {
|
|
3204
|
+
if (disposed || inFlight || pending === null) return;
|
|
3205
|
+
if (retryTimer !== null) return;
|
|
3206
|
+
const sessionId = opts.getSessionId();
|
|
3207
|
+
if (!sessionId) return;
|
|
3208
|
+
if (typeof navigator !== "undefined" && navigator.onLine === false) return;
|
|
3209
|
+
if (pendingSeq === fatalSeq) return;
|
|
3210
|
+
const snapshot = pending;
|
|
3211
|
+
const sentSeq = pendingSeq;
|
|
3212
|
+
const body = buildBody(snapshot, sentSeq);
|
|
3213
|
+
inFlight = true;
|
|
3214
|
+
void fetch(opts.url, {
|
|
3215
|
+
method: "POST",
|
|
3216
|
+
headers: { "content-type": "application/json" },
|
|
3217
|
+
body,
|
|
3218
|
+
keepalive: true
|
|
3219
|
+
}).then((res) => {
|
|
3220
|
+
inFlight = false;
|
|
3221
|
+
if (disposed) return;
|
|
3222
|
+
if (res.ok) {
|
|
3223
|
+
attempt = 0;
|
|
3224
|
+
if (pendingSeq === sentSeq) pending = null;
|
|
3225
|
+
else trySend();
|
|
3226
|
+
return;
|
|
3227
|
+
}
|
|
3228
|
+
if (res.status >= 400 && res.status < 500) {
|
|
3229
|
+
if (process.env.NODE_ENV !== "production") {
|
|
3230
|
+
console.warn(
|
|
3231
|
+
`[appfunnel] snapshot persist rejected ${res.status} (dropping snapshot; check session/payload)`
|
|
3232
|
+
);
|
|
3233
|
+
}
|
|
3234
|
+
fatalSeq = sentSeq;
|
|
3235
|
+
if (pendingSeq === sentSeq) pending = null;
|
|
3236
|
+
else trySend();
|
|
3237
|
+
return;
|
|
3238
|
+
}
|
|
3239
|
+
scheduleRetry();
|
|
3240
|
+
}).catch(() => {
|
|
3241
|
+
inFlight = false;
|
|
3242
|
+
if (disposed) return;
|
|
3243
|
+
scheduleRetry();
|
|
3244
|
+
});
|
|
3245
|
+
};
|
|
3246
|
+
const scheduleRetry = () => {
|
|
3247
|
+
if (disposed || pending === null) return;
|
|
3248
|
+
const exp = Math.min(maxDelay, baseDelay * 2 ** attempt);
|
|
3249
|
+
const delay2 = Math.round(exp * (0.5 + rand() * 0.5));
|
|
3250
|
+
attempt++;
|
|
3251
|
+
clearRetry();
|
|
3252
|
+
retryTimer = setT(() => {
|
|
3253
|
+
retryTimer = null;
|
|
3254
|
+
trySend();
|
|
3255
|
+
}, delay2);
|
|
3256
|
+
};
|
|
3257
|
+
const push = (snapshot) => {
|
|
3258
|
+
if (disposed) return;
|
|
3259
|
+
pending = snapshot;
|
|
3260
|
+
pendingSeq = ++seq;
|
|
3261
|
+
if (fatalSeq !== -1 && pendingSeq !== fatalSeq) fatalSeq = -1;
|
|
3262
|
+
if (retryTimer === null) trySend();
|
|
3263
|
+
};
|
|
3264
|
+
const flushBeacon = () => {
|
|
3265
|
+
if (disposed || pending === null) return;
|
|
3266
|
+
const sessionId = opts.getSessionId();
|
|
3267
|
+
if (!sessionId) return;
|
|
3268
|
+
const body = buildBody(pending, pendingSeq);
|
|
3269
|
+
let delivered = false;
|
|
3270
|
+
if (typeof navigator !== "undefined" && typeof navigator.sendBeacon === "function") {
|
|
3271
|
+
try {
|
|
3272
|
+
delivered = navigator.sendBeacon(
|
|
3273
|
+
opts.url,
|
|
3274
|
+
new Blob([body], { type: "application/json" })
|
|
3275
|
+
);
|
|
3276
|
+
} catch {
|
|
3277
|
+
delivered = false;
|
|
3278
|
+
}
|
|
3279
|
+
}
|
|
3280
|
+
if (!delivered) {
|
|
3281
|
+
try {
|
|
3282
|
+
void fetch(opts.url, {
|
|
3283
|
+
method: "POST",
|
|
3284
|
+
headers: { "content-type": "application/json" },
|
|
3285
|
+
body,
|
|
3286
|
+
keepalive: true
|
|
3287
|
+
}).catch(() => {
|
|
3288
|
+
});
|
|
3289
|
+
delivered = true;
|
|
3290
|
+
} catch {
|
|
3291
|
+
delivered = false;
|
|
3292
|
+
}
|
|
3293
|
+
}
|
|
3294
|
+
if (delivered) pending = null;
|
|
3295
|
+
};
|
|
3296
|
+
const onHidden = () => {
|
|
3297
|
+
if (typeof document !== "undefined" && document.visibilityState === "hidden") {
|
|
3298
|
+
flushBeacon();
|
|
3299
|
+
}
|
|
3300
|
+
};
|
|
3301
|
+
const onPageHide = () => flushBeacon();
|
|
3302
|
+
const onOnline = () => trySend();
|
|
3303
|
+
if (typeof window !== "undefined" && typeof window.addEventListener === "function") {
|
|
3304
|
+
try {
|
|
3305
|
+
document.addEventListener("visibilitychange", onHidden);
|
|
3306
|
+
window.addEventListener("pagehide", onPageHide);
|
|
3307
|
+
window.addEventListener("online", onOnline);
|
|
3308
|
+
} catch {
|
|
3309
|
+
}
|
|
3310
|
+
}
|
|
3311
|
+
const dispose = () => {
|
|
3312
|
+
disposed = true;
|
|
3313
|
+
clearRetry();
|
|
3314
|
+
pending = null;
|
|
3315
|
+
if (typeof window !== "undefined" && typeof window.removeEventListener === "function") {
|
|
3316
|
+
try {
|
|
3317
|
+
document.removeEventListener("visibilitychange", onHidden);
|
|
3318
|
+
window.removeEventListener("pagehide", onPageHide);
|
|
3319
|
+
window.removeEventListener("online", onOnline);
|
|
3320
|
+
} catch {
|
|
3321
|
+
}
|
|
3322
|
+
}
|
|
3323
|
+
};
|
|
3324
|
+
return { push, flushBeacon, dispose };
|
|
3325
|
+
}
|
|
3326
|
+
|
|
3327
|
+
// src/tracking/platformTracker.ts
|
|
3328
|
+
function validClickIds(acq) {
|
|
3329
|
+
return Object.entries(acq.clickIds ?? {}).filter(
|
|
3330
|
+
(e) => typeof e[1] === "string" && e[1] !== ""
|
|
3331
|
+
);
|
|
3332
|
+
}
|
|
3333
|
+
function acquisitionQueryBag(acq) {
|
|
3334
|
+
const q = {};
|
|
3335
|
+
const utm = acq.utm ?? {};
|
|
3336
|
+
for (const [k, v] of Object.entries({
|
|
3337
|
+
utm_source: utm.source,
|
|
3338
|
+
utm_medium: utm.medium,
|
|
3339
|
+
utm_campaign: utm.campaign,
|
|
3340
|
+
utm_term: utm.term,
|
|
3341
|
+
utm_content: utm.content
|
|
3342
|
+
})) if (v) q[k] = v;
|
|
3343
|
+
for (const [k, v] of validClickIds(acq)) q[k] = v;
|
|
3344
|
+
if (acq.cookies?.fbp) q._fbp = acq.cookies.fbp;
|
|
3345
|
+
if (acq.cookies?.fbc) q._fbc = acq.cookies.fbc;
|
|
3346
|
+
return q;
|
|
3347
|
+
}
|
|
3348
|
+
function acquisitionAdAttribution(acq) {
|
|
3349
|
+
const a = {};
|
|
3350
|
+
for (const [k, v] of validClickIds(acq)) a[k] = v;
|
|
3351
|
+
if (acq.cookies?.fbp) a.fbp = acq.cookies.fbp;
|
|
3352
|
+
if (acq.cookies?.fbc) a.fbc = acq.cookies.fbc;
|
|
3353
|
+
else if (acq.clickIds?.fbclid) a.fbc = `fb.1.${Date.now()}.${acq.clickIds.fbclid}`;
|
|
3354
|
+
return a;
|
|
3355
|
+
}
|
|
3356
|
+
function createPlatformTracker(cfg) {
|
|
3357
|
+
if (typeof window === "undefined" || cfg.mode === "test") return createConsoleTracker();
|
|
3358
|
+
const base = cfg.apiBase.replace(/\/+$/, "");
|
|
3359
|
+
const url = `${base}/campaign/${encodeURIComponent(cfg.campaignId)}/event`;
|
|
3360
|
+
const landingUrl = window.location.href;
|
|
3361
|
+
const sidKey = `af_sid:${cfg.campaignId}`;
|
|
3362
|
+
let sessionId = null;
|
|
3363
|
+
try {
|
|
3364
|
+
sessionId = window.sessionStorage.getItem(sidKey);
|
|
3365
|
+
} catch {
|
|
3366
|
+
}
|
|
3367
|
+
if (!sessionId && cfg.sessionId) sessionId = cfg.sessionId;
|
|
3368
|
+
let acquisition = null;
|
|
3369
|
+
let acquisitionSent = false;
|
|
3370
|
+
let chain = Promise.resolve();
|
|
3371
|
+
const send = async (event, data, userData) => {
|
|
3372
|
+
let mergedUserData = landingUrl ? { landingUrl, ...userData } : userData;
|
|
3373
|
+
let adAttribution;
|
|
3374
|
+
let attachingAcquisition = false;
|
|
3375
|
+
if (acquisition && !acquisitionSent) {
|
|
3376
|
+
attachingAcquisition = true;
|
|
3377
|
+
const query = acquisitionQueryBag(acquisition);
|
|
3378
|
+
if (Object.keys(query).length) mergedUserData = { ...mergedUserData, query };
|
|
3379
|
+
const ad = acquisitionAdAttribution(acquisition);
|
|
3380
|
+
if (Object.keys(ad).length) adAttribution = ad;
|
|
3381
|
+
}
|
|
3382
|
+
const body = {
|
|
3383
|
+
campaignId: cfg.campaignId,
|
|
3384
|
+
funnelId: cfg.funnelId,
|
|
3385
|
+
event,
|
|
3386
|
+
sessionId: sessionId ?? void 0,
|
|
3387
|
+
visitorId: cfg.visitorId ?? void 0,
|
|
3388
|
+
// Campaign route stamp (campaign-routing pivot): ingestion reads this ONLY at session create
|
|
3389
|
+
// (WebFunnelSession.campaignRouteId, snapshot semantics) — carried on every event but ignored
|
|
3390
|
+
// once the session exists. Undefined ⇒ default funnel / no route matched.
|
|
3391
|
+
campaignRouteId: cfg.route?.id ?? void 0,
|
|
3392
|
+
eventId: typeof data?.eventId === "string" ? data.eventId : void 0,
|
|
3393
|
+
data: data ?? {},
|
|
3394
|
+
metadata: {
|
|
3395
|
+
language: typeof navigator !== "undefined" ? navigator.language : void 0,
|
|
3396
|
+
screen: typeof window.screen !== "undefined" ? `${window.screen.width}x${window.screen.height}` : void 0,
|
|
3397
|
+
timezone: (() => {
|
|
3398
|
+
try {
|
|
3399
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
3400
|
+
} catch {
|
|
3401
|
+
return void 0;
|
|
3402
|
+
}
|
|
3403
|
+
})(),
|
|
3404
|
+
referrer: typeof document !== "undefined" && document.referrer ? document.referrer : void 0
|
|
3405
|
+
},
|
|
3406
|
+
...mergedUserData ? { userData: mergedUserData } : {},
|
|
3407
|
+
...adAttribution ? { adAttribution } : {}
|
|
3408
|
+
};
|
|
3409
|
+
const res = await fetch(url, {
|
|
3410
|
+
method: "POST",
|
|
3411
|
+
headers: { "content-type": "application/json" },
|
|
3412
|
+
body: JSON.stringify(body),
|
|
3413
|
+
keepalive: true
|
|
3414
|
+
// survives page.exit / unload
|
|
3415
|
+
});
|
|
3416
|
+
if (attachingAcquisition) acquisitionSent = true;
|
|
3417
|
+
const json = await res.json().catch(() => null);
|
|
3418
|
+
if (json?.sessionId && json.sessionId !== sessionId) {
|
|
3419
|
+
sessionId = json.sessionId;
|
|
3420
|
+
try {
|
|
3421
|
+
window.sessionStorage.setItem(sidKey, sessionId);
|
|
3422
|
+
} catch {
|
|
3423
|
+
}
|
|
3424
|
+
}
|
|
3425
|
+
};
|
|
3426
|
+
const enqueue = (event, data, userData) => {
|
|
3427
|
+
const task = () => send(event, data, userData).catch((e) => {
|
|
3428
|
+
if (process.env.NODE_ENV !== "production") console.warn(`[appfunnel] track failed (${event}):`, e);
|
|
3429
|
+
});
|
|
3430
|
+
if (!sessionId) {
|
|
3431
|
+
chain = chain.then(task);
|
|
3432
|
+
} else {
|
|
3433
|
+
void task();
|
|
3434
|
+
}
|
|
3435
|
+
};
|
|
3436
|
+
const VARS_DEBOUNCE_MS = 800;
|
|
3437
|
+
const dataUrl = `${base}/campaign/${encodeURIComponent(cfg.campaignId)}/event/session/data`;
|
|
3438
|
+
let pendingVars = null;
|
|
3439
|
+
let varsTimer = null;
|
|
3440
|
+
const persistence = createSnapshotPersistence({
|
|
3441
|
+
url: dataUrl,
|
|
3442
|
+
getSessionId: () => sessionId
|
|
3443
|
+
});
|
|
3444
|
+
const flushVars = () => {
|
|
3445
|
+
if (varsTimer) {
|
|
3446
|
+
clearTimeout(varsTimer);
|
|
3447
|
+
varsTimer = null;
|
|
3448
|
+
}
|
|
3449
|
+
if (!pendingVars) return;
|
|
3450
|
+
const snapshot = pendingVars;
|
|
3451
|
+
pendingVars = null;
|
|
3452
|
+
persistence.push(snapshot);
|
|
3453
|
+
if (!sessionId) chain = chain.then(() => {
|
|
3454
|
+
persistence.push(snapshot);
|
|
3455
|
+
});
|
|
3456
|
+
};
|
|
3457
|
+
const drainOnExit = () => {
|
|
3458
|
+
if (pendingVars) {
|
|
3459
|
+
flushVars();
|
|
3460
|
+
persistence.flushBeacon();
|
|
3461
|
+
}
|
|
3462
|
+
};
|
|
3463
|
+
try {
|
|
3464
|
+
window.addEventListener("visibilitychange", drainOnExit);
|
|
3465
|
+
window.addEventListener("pagehide", drainOnExit);
|
|
3466
|
+
} catch {
|
|
3467
|
+
}
|
|
3468
|
+
if (cfg.experiment?.id && cfg.experiment.variant) {
|
|
3469
|
+
enqueue("experiment.exposure", exposurePayload(cfg.experiment.id, cfg.experiment.variant, cfg.experiment.version));
|
|
3470
|
+
}
|
|
3471
|
+
return {
|
|
3472
|
+
track: (event, data, userData) => enqueue(event, data, userData),
|
|
3473
|
+
identify: (email, eventId) => enqueue("user.registered", { email, ...eventId ? { eventId } : {} }),
|
|
3474
|
+
setVariables: (variables) => {
|
|
3475
|
+
pendingVars = variables;
|
|
3476
|
+
if (varsTimer) clearTimeout(varsTimer);
|
|
3477
|
+
varsTimer = setTimeout(flushVars, VARS_DEBOUNCE_MS);
|
|
3478
|
+
},
|
|
3479
|
+
setAcquisition: (acq) => {
|
|
3480
|
+
acquisition = acq;
|
|
3481
|
+
}
|
|
3482
|
+
};
|
|
3483
|
+
}
|
|
3484
|
+
|
|
3485
|
+
// src/flow/mount.tsx
|
|
3486
|
+
var AssetsContext = React.createContext({});
|
|
3487
|
+
function AssetsProvider({
|
|
3488
|
+
assets,
|
|
3489
|
+
children
|
|
3490
|
+
}) {
|
|
3491
|
+
return React.createElement(AssetsContext.Provider, { value: assets ?? {} }, children);
|
|
3492
|
+
}
|
|
3493
|
+
function useAssets() {
|
|
3494
|
+
return React.useContext(AssetsContext);
|
|
3495
|
+
}
|
|
3496
|
+
function useAsset(name) {
|
|
3497
|
+
return React.useContext(AssetsContext)[name];
|
|
3498
|
+
}
|
|
3499
|
+
function createFunnelTree({
|
|
3500
|
+
config,
|
|
3501
|
+
pages,
|
|
3502
|
+
layout,
|
|
3503
|
+
checkoutDriver,
|
|
3504
|
+
messages
|
|
3505
|
+
}) {
|
|
3506
|
+
return function tree(opts) {
|
|
3507
|
+
const mode = opts.mode ?? "live";
|
|
3508
|
+
const embedEnabled = opts.embed ?? false;
|
|
3509
|
+
const tracker = opts.tracking ? createPlatformTracker({
|
|
3510
|
+
...opts.tracking,
|
|
3511
|
+
visitorId: opts.visitorId,
|
|
3512
|
+
sessionId: opts.sessionId,
|
|
3513
|
+
mode
|
|
3514
|
+
}) : void 0;
|
|
3515
|
+
const checkout = checkoutDriver && opts.tracking ? checkoutDriver({
|
|
3516
|
+
apiBase: opts.tracking.apiBase,
|
|
3517
|
+
campaignId: opts.tracking.campaignId,
|
|
3518
|
+
funnelId: opts.tracking.funnelId,
|
|
3519
|
+
mode,
|
|
3520
|
+
sessionId: opts.sessionId,
|
|
3521
|
+
visitorId: opts.visitorId
|
|
3522
|
+
}) : void 0;
|
|
3523
|
+
const layoutWithResume = function LayoutWithCheckoutResume({
|
|
3524
|
+
children
|
|
3525
|
+
}) {
|
|
3526
|
+
return React.createElement(
|
|
3527
|
+
React.Fragment,
|
|
3528
|
+
null,
|
|
3529
|
+
React.createElement(CheckoutResume, null),
|
|
3530
|
+
embedEnabled ? React.createElement(EmbedBridge, null) : null,
|
|
3531
|
+
layout ? React.createElement(layout, null, children) : children
|
|
3532
|
+
);
|
|
3533
|
+
};
|
|
3534
|
+
return React.createElement(AssetsProvider, {
|
|
3535
|
+
assets: opts.assets,
|
|
3536
|
+
children: React.createElement(FunnelProvider, {
|
|
3537
|
+
config,
|
|
3538
|
+
offerings: opts.offerings ?? [],
|
|
3539
|
+
messages: opts.messages ?? messages,
|
|
3540
|
+
locale: opts.locale,
|
|
3541
|
+
tracker,
|
|
3542
|
+
checkout,
|
|
3543
|
+
sessionValues: opts.sessionValues,
|
|
3544
|
+
deferSessionValues: opts.deferSessionValues,
|
|
3545
|
+
experiments: opts.experiments,
|
|
3546
|
+
context: {
|
|
3547
|
+
mode,
|
|
3548
|
+
visitorId: opts.visitorId ?? null,
|
|
3549
|
+
customerId: opts.customerId ?? null,
|
|
3550
|
+
sessionId: opts.sessionId ?? null
|
|
3551
|
+
},
|
|
3552
|
+
children: React.createElement(FunnelView, {
|
|
3553
|
+
pages,
|
|
3554
|
+
layout: layoutWithResume,
|
|
3555
|
+
initialKey: opts.initialKey,
|
|
3556
|
+
// FLASH-FREE HYDRATION (Option B, FIX 1): trust the SERVER-resolved initialKey instead of
|
|
3557
|
+
// re-running the entry guard. The CLIENT sets this via deferSessionValues (its deferred
|
|
3558
|
+
// snapshot is empty, so re-running the guard would bounce a qualified deep-link and
|
|
3559
|
+
// mismatch the markup). The build-time PRERENDER sets `trustInitialKey` explicitly so a
|
|
3560
|
+
// guarded page prerenders its OWN default-state markup (the renderer picks target-vs-bounce
|
|
3561
|
+
// at serve time). Live SSR fallback leaves both off → the guard runs against real values.
|
|
3562
|
+
trustInitialKey: opts.trustInitialKey ?? opts.deferSessionValues,
|
|
3563
|
+
prefetch: "off"
|
|
3564
|
+
// renderer background-modulepreloads all chunks
|
|
3565
|
+
})
|
|
3566
|
+
})
|
|
3567
|
+
});
|
|
3568
|
+
};
|
|
3569
|
+
}
|
|
1795
3570
|
function Choice({
|
|
1796
3571
|
bind,
|
|
1797
3572
|
options,
|
|
@@ -1804,7 +3579,7 @@ function Choice({
|
|
|
1804
3579
|
return options.map((opt) => {
|
|
1805
3580
|
const o = typeof opt === "string" ? { value: opt } : opt;
|
|
1806
3581
|
const selected = value === o.value;
|
|
1807
|
-
return
|
|
3582
|
+
return React.createElement(
|
|
1808
3583
|
"button",
|
|
1809
3584
|
{
|
|
1810
3585
|
key: o.value,
|
|
@@ -1823,7 +3598,7 @@ function Choice({
|
|
|
1823
3598
|
}
|
|
1824
3599
|
function Next({ children, onClick, ...rest }) {
|
|
1825
3600
|
const { next } = useNavigation();
|
|
1826
|
-
return
|
|
3601
|
+
return React.createElement(
|
|
1827
3602
|
"button",
|
|
1828
3603
|
{
|
|
1829
3604
|
type: "button",
|
|
@@ -1839,7 +3614,7 @@ function Next({ children, onClick, ...rest }) {
|
|
|
1839
3614
|
function Back({ children, onClick, ...rest }) {
|
|
1840
3615
|
const { back, canGoBack } = useNavigation();
|
|
1841
3616
|
if (!canGoBack) return null;
|
|
1842
|
-
return
|
|
3617
|
+
return React.createElement(
|
|
1843
3618
|
"button",
|
|
1844
3619
|
{
|
|
1845
3620
|
type: "button",
|
|
@@ -1892,7 +3667,7 @@ function Script({
|
|
|
1892
3667
|
onLoad,
|
|
1893
3668
|
onError
|
|
1894
3669
|
}) {
|
|
1895
|
-
|
|
3670
|
+
React.useEffect(() => {
|
|
1896
3671
|
if (typeof document === "undefined") return;
|
|
1897
3672
|
const key = id ?? src;
|
|
1898
3673
|
if (injected.has(key)) {
|
|
@@ -1931,159 +3706,198 @@ function Script({
|
|
|
1931
3706
|
return null;
|
|
1932
3707
|
}
|
|
1933
3708
|
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
return
|
|
1937
|
-
}
|
|
1938
|
-
|
|
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
|
-
function
|
|
1993
|
-
|
|
1994
|
-
|
|
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
|
-
|
|
3709
|
+
Object.defineProperty(exports, "assignVariant", {
|
|
3710
|
+
enumerable: true,
|
|
3711
|
+
get: function () { return chunkY4YNJ2EX_cjs.assignVariant; }
|
|
3712
|
+
});
|
|
3713
|
+
Object.defineProperty(exports, "bucketingSeed", {
|
|
3714
|
+
enumerable: true,
|
|
3715
|
+
get: function () { return chunkY4YNJ2EX_cjs.bucketingSeed; }
|
|
3716
|
+
});
|
|
3717
|
+
Object.defineProperty(exports, "buildCatalog", {
|
|
3718
|
+
enumerable: true,
|
|
3719
|
+
get: function () { return chunkY4YNJ2EX_cjs.buildCatalog; }
|
|
3720
|
+
});
|
|
3721
|
+
Object.defineProperty(exports, "compileManifest", {
|
|
3722
|
+
enumerable: true,
|
|
3723
|
+
get: function () { return chunkY4YNJ2EX_cjs.compileManifest; }
|
|
3724
|
+
});
|
|
3725
|
+
Object.defineProperty(exports, "currencyExponent", {
|
|
3726
|
+
enumerable: true,
|
|
3727
|
+
get: function () { return chunkY4YNJ2EX_cjs.currencyExponent; }
|
|
3728
|
+
});
|
|
3729
|
+
Object.defineProperty(exports, "defineFunnel", {
|
|
3730
|
+
enumerable: true,
|
|
3731
|
+
get: function () { return chunkY4YNJ2EX_cjs.defineFunnel; }
|
|
3732
|
+
});
|
|
3733
|
+
Object.defineProperty(exports, "definePage", {
|
|
3734
|
+
enumerable: true,
|
|
3735
|
+
get: function () { return chunkY4YNJ2EX_cjs.definePage; }
|
|
3736
|
+
});
|
|
3737
|
+
Object.defineProperty(exports, "entryGuard", {
|
|
3738
|
+
enumerable: true,
|
|
3739
|
+
get: function () { return chunkY4YNJ2EX_cjs.entryGuard; }
|
|
3740
|
+
});
|
|
3741
|
+
Object.defineProperty(exports, "evaluateCondition", {
|
|
3742
|
+
enumerable: true,
|
|
3743
|
+
get: function () { return chunkY4YNJ2EX_cjs.evaluateCondition; }
|
|
3744
|
+
});
|
|
3745
|
+
Object.defineProperty(exports, "evaluateGate", {
|
|
3746
|
+
enumerable: true,
|
|
3747
|
+
get: function () { return chunkY4YNJ2EX_cjs.evaluateGate; }
|
|
3748
|
+
});
|
|
3749
|
+
Object.defineProperty(exports, "expectedPathLength", {
|
|
3750
|
+
enumerable: true,
|
|
3751
|
+
get: function () { return chunkY4YNJ2EX_cjs.expectedPathLength; }
|
|
3752
|
+
});
|
|
3753
|
+
Object.defineProperty(exports, "fnv1a", {
|
|
3754
|
+
enumerable: true,
|
|
3755
|
+
get: function () { return chunkY4YNJ2EX_cjs.fnv1a; }
|
|
3756
|
+
});
|
|
3757
|
+
Object.defineProperty(exports, "formatMoney", {
|
|
3758
|
+
enumerable: true,
|
|
3759
|
+
get: function () { return chunkY4YNJ2EX_cjs.formatMoney; }
|
|
3760
|
+
});
|
|
3761
|
+
Object.defineProperty(exports, "formatOffering", {
|
|
3762
|
+
enumerable: true,
|
|
3763
|
+
get: function () { return chunkY4YNJ2EX_cjs.formatOffering; }
|
|
3764
|
+
});
|
|
3765
|
+
Object.defineProperty(exports, "hashToUnit", {
|
|
3766
|
+
enumerable: true,
|
|
3767
|
+
get: function () { return chunkY4YNJ2EX_cjs.hashToUnit; }
|
|
3768
|
+
});
|
|
3769
|
+
Object.defineProperty(exports, "isRtl", {
|
|
3770
|
+
enumerable: true,
|
|
3771
|
+
get: function () { return chunkY4YNJ2EX_cjs.isRtl; }
|
|
3772
|
+
});
|
|
3773
|
+
Object.defineProperty(exports, "isVariantKey", {
|
|
3774
|
+
enumerable: true,
|
|
3775
|
+
get: function () { return chunkY4YNJ2EX_cjs.isVariantKey; }
|
|
3776
|
+
});
|
|
3777
|
+
Object.defineProperty(exports, "nextPage", {
|
|
3778
|
+
enumerable: true,
|
|
3779
|
+
get: function () { return chunkY4YNJ2EX_cjs.nextPage; }
|
|
3780
|
+
});
|
|
3781
|
+
Object.defineProperty(exports, "outgoingKeys", {
|
|
3782
|
+
enumerable: true,
|
|
3783
|
+
get: function () { return chunkY4YNJ2EX_cjs.outgoingKeys; }
|
|
3784
|
+
});
|
|
3785
|
+
Object.defineProperty(exports, "pageMeta", {
|
|
3786
|
+
enumerable: true,
|
|
3787
|
+
get: function () { return chunkY4YNJ2EX_cjs.pageMeta; }
|
|
3788
|
+
});
|
|
3789
|
+
Object.defineProperty(exports, "parseSlotKey", {
|
|
3790
|
+
enumerable: true,
|
|
3791
|
+
get: function () { return chunkY4YNJ2EX_cjs.parseSlotKey; }
|
|
3792
|
+
});
|
|
3793
|
+
Object.defineProperty(exports, "pickByWeight", {
|
|
3794
|
+
enumerable: true,
|
|
3795
|
+
get: function () { return chunkY4YNJ2EX_cjs.pickByWeight; }
|
|
3796
|
+
});
|
|
3797
|
+
Object.defineProperty(exports, "resolveExperiments", {
|
|
3798
|
+
enumerable: true,
|
|
3799
|
+
get: function () { return chunkY4YNJ2EX_cjs.resolveExperiments; }
|
|
3800
|
+
});
|
|
3801
|
+
Object.defineProperty(exports, "resolveLocale", {
|
|
3802
|
+
enumerable: true,
|
|
3803
|
+
get: function () { return chunkY4YNJ2EX_cjs.resolveLocale; }
|
|
3804
|
+
});
|
|
3805
|
+
Object.defineProperty(exports, "resolveOffering", {
|
|
3806
|
+
enumerable: true,
|
|
3807
|
+
get: function () { return chunkY4YNJ2EX_cjs.resolveOffering; }
|
|
3808
|
+
});
|
|
3809
|
+
Object.defineProperty(exports, "resolveRoute", {
|
|
3810
|
+
enumerable: true,
|
|
3811
|
+
get: function () { return chunkY4YNJ2EX_cjs.resolveRoute; }
|
|
3812
|
+
});
|
|
3813
|
+
Object.defineProperty(exports, "INLINE_SURFACES", {
|
|
3814
|
+
enumerable: true,
|
|
3815
|
+
get: function () { return chunk7JLOF6CJ_cjs.INLINE_SURFACES; }
|
|
3816
|
+
});
|
|
3817
|
+
Object.defineProperty(exports, "PROVIDER_PROFILES", {
|
|
3818
|
+
enumerable: true,
|
|
3819
|
+
get: function () { return chunk7JLOF6CJ_cjs.PROVIDER_PROFILES; }
|
|
3820
|
+
});
|
|
3821
|
+
Object.defineProperty(exports, "checkoutError", {
|
|
3822
|
+
enumerable: true,
|
|
3823
|
+
get: function () { return chunk7JLOF6CJ_cjs.checkoutError; }
|
|
3824
|
+
});
|
|
3825
|
+
Object.defineProperty(exports, "isInlineSurface", {
|
|
3826
|
+
enumerable: true,
|
|
3827
|
+
get: function () { return chunk7JLOF6CJ_cjs.isInlineSurface; }
|
|
3828
|
+
});
|
|
3829
|
+
Object.defineProperty(exports, "isMerchantOfRecord", {
|
|
3830
|
+
enumerable: true,
|
|
3831
|
+
get: function () { return chunk7JLOF6CJ_cjs.isMerchantOfRecord; }
|
|
3832
|
+
});
|
|
3833
|
+
Object.defineProperty(exports, "isOrchestrator", {
|
|
3834
|
+
enumerable: true,
|
|
3835
|
+
get: function () { return chunk7JLOF6CJ_cjs.isOrchestrator; }
|
|
3836
|
+
});
|
|
3837
|
+
Object.defineProperty(exports, "isRenderableSurface", {
|
|
3838
|
+
enumerable: true,
|
|
3839
|
+
get: function () { return chunk7JLOF6CJ_cjs.isRenderableSurface; }
|
|
3840
|
+
});
|
|
3841
|
+
Object.defineProperty(exports, "renderableSurfaces", {
|
|
3842
|
+
enumerable: true,
|
|
3843
|
+
get: function () { return chunk7JLOF6CJ_cjs.renderableSurfaces; }
|
|
3844
|
+
});
|
|
3845
|
+
Object.defineProperty(exports, "resolveSurface", {
|
|
3846
|
+
enumerable: true,
|
|
3847
|
+
get: function () { return chunk7JLOF6CJ_cjs.resolveSurface; }
|
|
3848
|
+
});
|
|
3849
|
+
Object.defineProperty(exports, "surfacesFor", {
|
|
3850
|
+
enumerable: true,
|
|
3851
|
+
get: function () { return chunk7JLOF6CJ_cjs.surfacesFor; }
|
|
3852
|
+
});
|
|
3853
|
+
Object.defineProperty(exports, "validateCheckout", {
|
|
3854
|
+
enumerable: true,
|
|
3855
|
+
get: function () { return chunk7JLOF6CJ_cjs.validateCheckout; }
|
|
3856
|
+
});
|
|
3857
|
+
Object.defineProperty(exports, "validateUpsell", {
|
|
2024
3858
|
enumerable: true,
|
|
2025
|
-
get: function () { return
|
|
3859
|
+
get: function () { return chunk7JLOF6CJ_cjs.validateUpsell; }
|
|
2026
3860
|
});
|
|
3861
|
+
exports.AssetsContext = AssetsContext;
|
|
3862
|
+
exports.AssetsProvider = AssetsProvider;
|
|
2027
3863
|
exports.Back = Back;
|
|
2028
3864
|
exports.Checkout = Checkout;
|
|
3865
|
+
exports.CheckoutResume = CheckoutResume;
|
|
2029
3866
|
exports.Choice = Choice;
|
|
2030
3867
|
exports.EVENT_CATALOG = EVENT_CATALOG;
|
|
2031
3868
|
exports.FunnelProvider = FunnelProvider;
|
|
2032
3869
|
exports.FunnelToaster = FunnelToaster;
|
|
2033
3870
|
exports.FunnelView = FunnelView;
|
|
2034
|
-
exports.INLINE_SURFACES = INLINE_SURFACES;
|
|
2035
3871
|
exports.Modal = Modal;
|
|
2036
3872
|
exports.Next = Next;
|
|
2037
|
-
exports.PROVIDER_PROFILES = PROVIDER_PROFILES;
|
|
2038
3873
|
exports.Script = Script;
|
|
2039
3874
|
exports.Sheet = Sheet;
|
|
2040
3875
|
exports.Upsell = Upsell;
|
|
2041
3876
|
exports.VariableStore = VariableStore;
|
|
2042
|
-
exports.assignVariant = assignVariant;
|
|
2043
3877
|
exports.attachBus = attachBus;
|
|
2044
|
-
exports.bucketingSeed = bucketingSeed;
|
|
2045
3878
|
exports.buildAcquisition = buildAcquisition;
|
|
2046
|
-
exports.
|
|
2047
|
-
exports.checkoutError = checkoutError;
|
|
2048
|
-
exports.compileManifest = compileManifest;
|
|
3879
|
+
exports.buildContext = buildContext;
|
|
2049
3880
|
exports.createBus = createBus;
|
|
2050
3881
|
exports.createConsoleTracker = createConsoleTracker;
|
|
2051
3882
|
exports.createFunnelStore = createFunnelStore;
|
|
3883
|
+
exports.createFunnelTree = createFunnelTree;
|
|
2052
3884
|
exports.createMockDriver = createMockDriver;
|
|
2053
|
-
exports.
|
|
3885
|
+
exports.createPlatformTracker = createPlatformTracker;
|
|
2054
3886
|
exports.defineModal = defineModal;
|
|
2055
|
-
exports.
|
|
2056
|
-
exports.entryGuard = entryGuard;
|
|
2057
|
-
exports.evaluateCondition = evaluateCondition;
|
|
2058
|
-
exports.evaluateGate = evaluateGate;
|
|
2059
|
-
exports.expectedPathLength = expectedPathLength;
|
|
2060
|
-
exports.fnv1a = fnv1a;
|
|
2061
|
-
exports.formatProduct = formatProduct;
|
|
2062
|
-
exports.hashToUnit = hashToUnit;
|
|
3887
|
+
exports.driverWithEmail = driverWithEmail;
|
|
2063
3888
|
exports.hideModal = hideModal;
|
|
2064
3889
|
exports.integrationEvents = integrationEvents;
|
|
2065
|
-
exports.isInlineSurface = isInlineSurface;
|
|
2066
3890
|
exports.isIntegrationEvent = isIntegrationEvent;
|
|
2067
|
-
exports.isMerchantOfRecord = isMerchantOfRecord;
|
|
2068
|
-
exports.isOrchestrator = isOrchestrator;
|
|
2069
|
-
exports.isRtl = isRtl;
|
|
2070
3891
|
exports.isTrackableEvent = isTrackableEvent;
|
|
2071
|
-
exports.isVariantKey = isVariantKey;
|
|
2072
3892
|
exports.newEventId = newEventId;
|
|
2073
|
-
exports.nextPage = nextPage;
|
|
2074
|
-
exports.outgoingKeys = outgoingKeys;
|
|
2075
|
-
exports.pageMeta = pageMeta;
|
|
2076
|
-
exports.parseSlotKey = parseSlotKey;
|
|
2077
|
-
exports.pickByWeight = pickByWeight;
|
|
2078
3893
|
exports.registerModal = registerModal;
|
|
2079
3894
|
exports.removeModal = removeModal;
|
|
2080
|
-
exports.resolveExperiments = resolveExperiments;
|
|
2081
|
-
exports.resolveLocale = resolveLocale;
|
|
2082
|
-
exports.resolveProduct = resolveProduct;
|
|
2083
|
-
exports.resolveRoute = resolveRoute;
|
|
2084
3895
|
exports.showModal = showModal;
|
|
2085
|
-
exports.
|
|
3896
|
+
exports.toast = toast;
|
|
2086
3897
|
exports.unregisterModal = unregisterModal;
|
|
3898
|
+
exports.useActiveLocale = useActiveLocale;
|
|
3899
|
+
exports.useAsset = useAsset;
|
|
3900
|
+
exports.useAssets = useAssets;
|
|
2087
3901
|
exports.useCheckout = useCheckout;
|
|
2088
3902
|
exports.useClickIds = useClickIds;
|
|
2089
3903
|
exports.useContextValue = useContextValue;
|
|
@@ -2095,18 +3909,15 @@ exports.useFunnel = useFunnel;
|
|
|
2095
3909
|
exports.useLocale = useLocale;
|
|
2096
3910
|
exports.useModal = useModal;
|
|
2097
3911
|
exports.useNavigation = useNavigation;
|
|
3912
|
+
exports.useOffering = useOffering;
|
|
3913
|
+
exports.useOfferings = useOfferings;
|
|
2098
3914
|
exports.usePage = usePage;
|
|
2099
|
-
exports.useProduct = useProduct;
|
|
2100
|
-
exports.useProducts = useProducts;
|
|
2101
3915
|
exports.useResponse = useResponse;
|
|
2102
3916
|
exports.useSystem = useSystem;
|
|
2103
3917
|
exports.useTracker = useTracker;
|
|
2104
3918
|
exports.useTranslation = useTranslation;
|
|
2105
3919
|
exports.useUserAttribute = useUserAttribute;
|
|
2106
3920
|
exports.useUtm = useUtm;
|
|
2107
|
-
exports.validateCheckout = validateCheckout;
|
|
2108
|
-
exports.validateExperiments = validateExperiments;
|
|
2109
|
-
exports.validateUpsell = validateUpsell;
|
|
2110
3921
|
exports.withBus = withBus;
|
|
2111
3922
|
//# sourceMappingURL=index.cjs.map
|
|
2112
3923
|
//# sourceMappingURL=index.cjs.map
|