@appfunnel-dev/sdk 2.0.0-canary.1 → 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 +2650 -841
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +343 -1005
- package/dist/index.d.ts +343 -1005
- package/dist/index.js +2381 -701
- 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,141 +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
|
-
const at = now();
|
|
938
|
-
const hidden = t.hidden + (t.hiddenSince != null ? at - t.hiddenSince : 0);
|
|
939
|
-
const durationMs = Math.round(at - t.enteredAt);
|
|
940
|
-
tracker.track("page.exit", { pageId: t.key, durationMs, activeMs: Math.max(0, Math.round(durationMs - hidden)) });
|
|
941
|
-
}, [tracker]);
|
|
942
|
-
react.useEffect(() => {
|
|
943
|
-
if (typeof document === "undefined") return;
|
|
944
|
-
const onVis = () => {
|
|
945
|
-
const t = timer.current;
|
|
946
|
-
if (!t) return;
|
|
947
|
-
if (document.visibilityState === "hidden") t.hiddenSince = now();
|
|
948
|
-
else if (t.hiddenSince != null) {
|
|
949
|
-
t.hidden += now() - t.hiddenSince;
|
|
950
|
-
t.hiddenSince = null;
|
|
951
|
-
}
|
|
952
|
-
};
|
|
953
|
-
document.addEventListener("visibilitychange", onVis);
|
|
954
|
-
return () => document.removeEventListener("visibilitychange", onVis);
|
|
955
|
-
}, []);
|
|
956
|
-
react.useEffect(() => {
|
|
957
|
-
if (!currentKey) return;
|
|
958
|
-
if (timer.current && timer.current.key !== currentKey) emitExit();
|
|
959
|
-
const hidden = typeof document !== "undefined" && document.visibilityState === "hidden";
|
|
960
|
-
timer.current = { key: currentKey, enteredAt: now(), hidden: 0, hiddenSince: hidden ? now() : null };
|
|
961
|
-
}, [currentKey, emitExit]);
|
|
962
|
-
react.useEffect(() => () => emitExit(), [emitExit]);
|
|
963
|
-
const [isNavigating, startNav] = react.useTransition();
|
|
964
|
-
const next = react.useCallback(() => {
|
|
965
|
-
const target = nextPage(flow, currentKey, funnel.snapshot());
|
|
966
|
-
const resolved = target ? experiments.slotOf[target] ?? target : target;
|
|
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;
|
|
967
862
|
if (resolved) startNav(() => setHistory((h) => [...h, resolved]));
|
|
968
|
-
}, [flow, currentKey, funnel,
|
|
969
|
-
const back =
|
|
863
|
+
}, [flow, currentKey, funnel, toSlot]);
|
|
864
|
+
const back = React.useCallback(
|
|
970
865
|
() => startNav(() => setHistory((h) => h.length > 1 ? h.slice(0, -1) : h)),
|
|
971
866
|
[]
|
|
972
867
|
);
|
|
973
|
-
const go =
|
|
974
|
-
(key) => startNav(() => setHistory((h) => [...h,
|
|
975
|
-
[
|
|
868
|
+
const go = React.useCallback(
|
|
869
|
+
(key) => startNav(() => setHistory((h) => [...h, toSlot(key)])),
|
|
870
|
+
[toSlot]
|
|
976
871
|
);
|
|
977
|
-
const prefetched =
|
|
978
|
-
const prefetch =
|
|
872
|
+
const prefetched = React.useRef(/* @__PURE__ */ new Set());
|
|
873
|
+
const prefetch = React.useCallback(
|
|
979
874
|
(key) => {
|
|
980
|
-
const target =
|
|
875
|
+
const target = toRenderKey(key);
|
|
981
876
|
const load = loaders.get(target);
|
|
982
877
|
if (!load || prefetched.current.has(target)) return;
|
|
983
878
|
prefetched.current.add(target);
|
|
984
879
|
load().catch(() => prefetched.current.delete(target));
|
|
985
880
|
},
|
|
986
|
-
[loaders,
|
|
881
|
+
[loaders, toRenderKey]
|
|
987
882
|
);
|
|
988
|
-
const nextCandidates =
|
|
989
|
-
() => [...new Set(outgoingKeys(flow, currentKey).map(
|
|
990
|
-
[flow, currentKey,
|
|
883
|
+
const nextCandidates = React.useMemo(
|
|
884
|
+
() => [...new Set(chunkY4YNJ2EX_cjs.outgoingKeys(flow, currentKey).map(toRenderKey))],
|
|
885
|
+
[flow, currentKey, toRenderKey]
|
|
991
886
|
);
|
|
992
|
-
|
|
887
|
+
React.useEffect(() => {
|
|
993
888
|
if (prefetchMode !== "auto") return;
|
|
994
889
|
for (const key of nextCandidates) prefetch(key);
|
|
995
890
|
}, [prefetchMode, nextCandidates, prefetch]);
|
|
@@ -1003,36 +898,157 @@ function FunnelView({
|
|
|
1003
898
|
canGoBack: history.length > 1,
|
|
1004
899
|
isNavigating
|
|
1005
900
|
};
|
|
1006
|
-
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);
|
|
1007
940
|
const Current = components.get(renderKey);
|
|
1008
|
-
const pageEl =
|
|
1009
|
-
const content = layout ?
|
|
1010
|
-
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(
|
|
1011
944
|
ExperimentContext.Provider,
|
|
1012
945
|
{ value: experiments.assignments },
|
|
1013
|
-
|
|
946
|
+
React.createElement(NavContext.Provider, { value }, content)
|
|
1014
947
|
);
|
|
1015
948
|
}
|
|
1016
949
|
function useNavigation() {
|
|
1017
|
-
const ctx =
|
|
950
|
+
const ctx = React.useContext(NavContext);
|
|
1018
951
|
if (!ctx) throw new Error("useNavigation must be used inside <FunnelView>");
|
|
1019
952
|
return ctx;
|
|
1020
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
|
+
}
|
|
1021
1029
|
function usePage() {
|
|
1022
1030
|
return useNavigation().page;
|
|
1023
1031
|
}
|
|
1024
|
-
var symModalId = /* @__PURE__ */ Symbol("
|
|
1032
|
+
var symModalId = /* @__PURE__ */ Symbol("AppfunnelModalId");
|
|
1025
1033
|
var initialState = {};
|
|
1026
|
-
var ModalStateContext =
|
|
1027
|
-
var ModalIdContext =
|
|
1034
|
+
var ModalStateContext = React.createContext(initialState);
|
|
1035
|
+
var ModalIdContext = React.createContext(null);
|
|
1036
|
+
var ModalDispatchContext = React.createContext(null);
|
|
1028
1037
|
var REGISTRY = {};
|
|
1029
1038
|
var ALREADY_MOUNTED = {};
|
|
1030
1039
|
var modalCallbacks = {};
|
|
1031
1040
|
var hideCallbacks = {};
|
|
1032
1041
|
var uid = 0;
|
|
1033
1042
|
var getUid = () => `_af_modal_${uid++}`;
|
|
1034
|
-
var
|
|
1035
|
-
|
|
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);
|
|
1036
1052
|
};
|
|
1037
1053
|
var modalReducer = (state = initialState, action) => {
|
|
1038
1054
|
const { modalId, args, flags } = action.payload;
|
|
@@ -1075,10 +1091,10 @@ function registerModal(id, comp, props) {
|
|
|
1075
1091
|
function unregisterModal(id) {
|
|
1076
1092
|
delete REGISTRY[id];
|
|
1077
1093
|
}
|
|
1078
|
-
function
|
|
1094
|
+
function showModalVia(d, modal, args) {
|
|
1079
1095
|
const id = idOf(modal);
|
|
1080
1096
|
if (typeof modal !== "string" && !REGISTRY[id]) registerModal(id, modal);
|
|
1081
|
-
|
|
1097
|
+
d({ type: "show", payload: { modalId: id, args } });
|
|
1082
1098
|
if (!modalCallbacks[id]) {
|
|
1083
1099
|
let res;
|
|
1084
1100
|
let rej;
|
|
@@ -1090,9 +1106,12 @@ function showModal(modal, args) {
|
|
|
1090
1106
|
}
|
|
1091
1107
|
return modalCallbacks[id].promise;
|
|
1092
1108
|
}
|
|
1093
|
-
function
|
|
1109
|
+
function showModal(modal, args) {
|
|
1110
|
+
return showModalVia(dispatch, modal, args);
|
|
1111
|
+
}
|
|
1112
|
+
function hideModalVia(d, modal) {
|
|
1094
1113
|
const id = idOf(modal);
|
|
1095
|
-
|
|
1114
|
+
d({ type: "hide", payload: { modalId: id } });
|
|
1096
1115
|
delete modalCallbacks[id];
|
|
1097
1116
|
if (!hideCallbacks[id]) {
|
|
1098
1117
|
let res;
|
|
@@ -1105,41 +1124,64 @@ function hideModal(modal) {
|
|
|
1105
1124
|
}
|
|
1106
1125
|
return hideCallbacks[id].promise;
|
|
1107
1126
|
}
|
|
1108
|
-
function
|
|
1127
|
+
function hideModal(modal) {
|
|
1128
|
+
return hideModalVia(dispatch, modal);
|
|
1129
|
+
}
|
|
1130
|
+
function removeModalVia(d, modal) {
|
|
1109
1131
|
const id = idOf(modal);
|
|
1110
|
-
|
|
1132
|
+
d({ type: "remove", payload: { modalId: id } });
|
|
1111
1133
|
delete modalCallbacks[id];
|
|
1112
1134
|
delete hideCallbacks[id];
|
|
1113
1135
|
}
|
|
1114
|
-
function
|
|
1115
|
-
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 } });
|
|
1116
1141
|
}
|
|
1117
1142
|
function useModal(modal, args) {
|
|
1118
|
-
const modals =
|
|
1119
|
-
const contextId =
|
|
1143
|
+
const modals = React.useContext(ModalStateContext);
|
|
1144
|
+
const contextId = React.useContext(ModalIdContext);
|
|
1145
|
+
const d = React.useContext(ModalDispatchContext) ?? dispatch;
|
|
1120
1146
|
const isComponent = !!modal && typeof modal !== "string";
|
|
1121
1147
|
const id = modal ? idOf(modal) : contextId;
|
|
1122
|
-
if (!id)
|
|
1123
|
-
|
|
1124
|
-
|
|
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);
|
|
1125
1155
|
}, [isComponent, id, modal, args]);
|
|
1126
1156
|
const info = modals[id];
|
|
1127
|
-
const show =
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
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(
|
|
1143
1185
|
() => ({
|
|
1144
1186
|
id,
|
|
1145
1187
|
args: info?.args,
|
|
@@ -1152,53 +1194,90 @@ function useModal(modal, args) {
|
|
|
1152
1194
|
reject,
|
|
1153
1195
|
resolveHide
|
|
1154
1196
|
}),
|
|
1155
|
-
[
|
|
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
|
+
]
|
|
1156
1209
|
);
|
|
1157
1210
|
}
|
|
1158
1211
|
function defineModal(Comp) {
|
|
1159
|
-
return function Wrapped({
|
|
1212
|
+
return function Wrapped({
|
|
1213
|
+
id,
|
|
1214
|
+
defaultVisible,
|
|
1215
|
+
keepMounted,
|
|
1216
|
+
...props
|
|
1217
|
+
}) {
|
|
1160
1218
|
const { args, show } = useModal(id);
|
|
1161
|
-
const modals =
|
|
1219
|
+
const modals = React.useContext(ModalStateContext);
|
|
1220
|
+
const d = React.useContext(ModalDispatchContext) ?? dispatch;
|
|
1162
1221
|
const shouldMount = !!modals[id];
|
|
1163
|
-
|
|
1222
|
+
React.useEffect(() => {
|
|
1164
1223
|
if (defaultVisible) show();
|
|
1165
1224
|
ALREADY_MOUNTED[id] = true;
|
|
1166
1225
|
return () => {
|
|
1167
1226
|
delete ALREADY_MOUNTED[id];
|
|
1168
1227
|
};
|
|
1169
1228
|
}, [id, show, defaultVisible]);
|
|
1170
|
-
|
|
1171
|
-
if (keepMounted) setFlags(id, { keepMounted: true });
|
|
1172
|
-
}, [id, keepMounted]);
|
|
1229
|
+
React.useEffect(() => {
|
|
1230
|
+
if (keepMounted) setFlags(d, id, { keepMounted: true });
|
|
1231
|
+
}, [d, id, keepMounted]);
|
|
1173
1232
|
const delayVisible = modals[id]?.delayVisible;
|
|
1174
|
-
|
|
1233
|
+
React.useEffect(() => {
|
|
1175
1234
|
if (delayVisible) show(args);
|
|
1176
1235
|
}, [delayVisible, args, show]);
|
|
1177
1236
|
if (!shouldMount) return null;
|
|
1178
|
-
return
|
|
1237
|
+
return React.createElement(
|
|
1179
1238
|
ModalIdContext.Provider,
|
|
1180
1239
|
{ value: id },
|
|
1181
|
-
|
|
1240
|
+
React.createElement(Comp, { ...props, ...args })
|
|
1182
1241
|
);
|
|
1183
1242
|
};
|
|
1184
1243
|
}
|
|
1185
1244
|
function ModalRoot() {
|
|
1186
|
-
const modals =
|
|
1245
|
+
const modals = React.useContext(ModalStateContext);
|
|
1187
1246
|
const toRender = Object.keys(modals).filter((id) => REGISTRY[id]).map((id) => ({ id, ...REGISTRY[id] }));
|
|
1188
|
-
return
|
|
1247
|
+
return React.createElement(
|
|
1189
1248
|
"div",
|
|
1190
1249
|
{ "data-appfunnel-modal-root": "" },
|
|
1191
|
-
...toRender.map(
|
|
1250
|
+
...toRender.map(
|
|
1251
|
+
(t) => React.createElement(t.comp, { key: t.id, id: t.id, ...t.props })
|
|
1252
|
+
)
|
|
1192
1253
|
);
|
|
1193
1254
|
}
|
|
1194
1255
|
function ModalRuntime({ children }) {
|
|
1195
|
-
const [state, localDispatch] =
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
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
|
+
)
|
|
1202
1281
|
);
|
|
1203
1282
|
}
|
|
1204
1283
|
|
|
@@ -1225,7 +1304,7 @@ function OverlayShell({
|
|
|
1225
1304
|
panel,
|
|
1226
1305
|
children
|
|
1227
1306
|
}) {
|
|
1228
|
-
|
|
1307
|
+
React.useEffect(() => {
|
|
1229
1308
|
if (!open || typeof document === "undefined") return;
|
|
1230
1309
|
const onKey = (e) => {
|
|
1231
1310
|
if (e.key === "Escape") onClose();
|
|
@@ -1239,7 +1318,7 @@ function OverlayShell({
|
|
|
1239
1318
|
};
|
|
1240
1319
|
}, [open, onClose]);
|
|
1241
1320
|
if (!open || typeof document === "undefined") return null;
|
|
1242
|
-
const overlay =
|
|
1321
|
+
const overlay = React.createElement(
|
|
1243
1322
|
"div",
|
|
1244
1323
|
{
|
|
1245
1324
|
role: "dialog",
|
|
@@ -1248,19 +1327,19 @@ function OverlayShell({
|
|
|
1248
1327
|
"data-appfunnel-overlay": "",
|
|
1249
1328
|
style: { position: "fixed", inset: 0, zIndex: 1e3, display: "flex", ...container }
|
|
1250
1329
|
},
|
|
1251
|
-
|
|
1330
|
+
React.createElement("div", {
|
|
1252
1331
|
"data-appfunnel-overlay-backdrop": "",
|
|
1253
1332
|
className: backdropClassName,
|
|
1254
1333
|
onClick: dismissOnBackdrop ? onClose : void 0,
|
|
1255
1334
|
style: { position: "absolute", inset: 0, background: "rgba(0,0,0,0.5)" }
|
|
1256
1335
|
}),
|
|
1257
|
-
|
|
1336
|
+
React.createElement(
|
|
1258
1337
|
"div",
|
|
1259
1338
|
{ "data-appfunnel-overlay-panel": "", className, style: { position: "relative", background: "#fff", ...panel } },
|
|
1260
1339
|
children
|
|
1261
1340
|
)
|
|
1262
1341
|
);
|
|
1263
|
-
return
|
|
1342
|
+
return ReactDOM.createPortal(overlay, document.body);
|
|
1264
1343
|
}
|
|
1265
1344
|
var SHEET_LAYOUT = {
|
|
1266
1345
|
bottom: { container: { justifyContent: "flex-end", alignItems: "stretch" }, panel: { borderTopLeftRadius: 16, borderTopRightRadius: 16, padding: 16, maxHeight: "90vh", overflow: "auto" } },
|
|
@@ -1271,7 +1350,7 @@ var SHEET_LAYOUT = {
|
|
|
1271
1350
|
function Sheet(props) {
|
|
1272
1351
|
const control = useModalControl();
|
|
1273
1352
|
const layout = SHEET_LAYOUT[props.side ?? "bottom"];
|
|
1274
|
-
return
|
|
1353
|
+
return React.createElement(OverlayShell, {
|
|
1275
1354
|
...control,
|
|
1276
1355
|
className: props.className,
|
|
1277
1356
|
backdropClassName: props.backdropClassName,
|
|
@@ -1284,7 +1363,7 @@ function Sheet(props) {
|
|
|
1284
1363
|
}
|
|
1285
1364
|
function Modal(props) {
|
|
1286
1365
|
const control = useModalControl();
|
|
1287
|
-
return
|
|
1366
|
+
return React.createElement(OverlayShell, {
|
|
1288
1367
|
...control,
|
|
1289
1368
|
className: props.className,
|
|
1290
1369
|
backdropClassName: props.backdropClassName,
|
|
@@ -1297,263 +1376,412 @@ function Modal(props) {
|
|
|
1297
1376
|
}
|
|
1298
1377
|
|
|
1299
1378
|
// src/commerce/checkout.tsx
|
|
1300
|
-
var INLINE_SURFACES = /* @__PURE__ */ new Set([
|
|
1301
|
-
"express",
|
|
1302
|
-
"element",
|
|
1303
|
-
"embedded"
|
|
1304
|
-
]);
|
|
1305
|
-
function isInlineSurface(surface) {
|
|
1306
|
-
return INLINE_SURFACES.has(surface);
|
|
1307
|
-
}
|
|
1308
|
-
var ON = { purchase: true, wallets: true, offSession: "reliable" };
|
|
1309
|
-
var MANAGED = { purchase: true, wallets: true, offSession: "conditional" };
|
|
1310
|
-
var ALL_UPSELLS = ["subscription", "one-time", "upgrade"];
|
|
1311
|
-
var PROVIDER_PROFILES = {
|
|
1312
|
-
// PSP; you hold the PaymentMethod and fire your own off-session PaymentIntent on
|
|
1313
|
-
// the Elements surfaces (reliable). **Managed** Checkout (`embedded`/`redirect`)
|
|
1314
|
-
// upsells too, but as a direct charge against Stripe's vaulted card → conditional.
|
|
1315
|
-
// No provider "popup" surface.
|
|
1316
|
-
stripe: {
|
|
1317
|
-
isMoR: false,
|
|
1318
|
-
offSessionUpsells: ALL_UPSELLS,
|
|
1319
|
-
tokenModel: "merchant",
|
|
1320
|
-
orchestrator: false,
|
|
1321
|
-
surfaces: { express: ON, element: ON, sheet: ON, embedded: MANAGED, redirect: MANAGED }
|
|
1322
|
-
},
|
|
1323
|
-
// Merchant-of-Record; card entry is always Paddle's frame → no own element/sheet/
|
|
1324
|
-
// express. Off-session via subscription one-time charge / collection_mode, but
|
|
1325
|
-
// **no 2nd concurrent subscription** (upgrade + one-time only), provider-initiated.
|
|
1326
|
-
paddle: {
|
|
1327
|
-
isMoR: true,
|
|
1328
|
-
offSessionUpsells: ["one-time", "upgrade"],
|
|
1329
|
-
tokenModel: "provider",
|
|
1330
|
-
orchestrator: false,
|
|
1331
|
-
surfaces: { embedded: ON, popup: ON, redirect: ON }
|
|
1332
|
-
},
|
|
1333
|
-
// Merchant-of-Record; iframe-only (no raw card field). Off-session works
|
|
1334
|
-
// (provider-initiated, async via webhooks). `sheet` = our chrome hosting their embed.
|
|
1335
|
-
whop: {
|
|
1336
|
-
isMoR: true,
|
|
1337
|
-
offSessionUpsells: ALL_UPSELLS,
|
|
1338
|
-
tokenModel: "provider",
|
|
1339
|
-
orchestrator: false,
|
|
1340
|
-
surfaces: { embedded: ON, redirect: ON, sheet: ON }
|
|
1341
|
-
},
|
|
1342
|
-
// Pure orchestrator (PSP-side); Headless renders express/element/sheet/embedded.
|
|
1343
|
-
// No hosted redirect page, no popup checkout. Off-session is PSP-conditional.
|
|
1344
|
-
primer: {
|
|
1345
|
-
isMoR: false,
|
|
1346
|
-
offSessionUpsells: ALL_UPSELLS,
|
|
1347
|
-
tokenModel: "merchant",
|
|
1348
|
-
orchestrator: true,
|
|
1349
|
-
surfaces: { express: ON, element: ON, sheet: ON, embedded: ON }
|
|
1350
|
-
},
|
|
1351
|
-
// PSP **and** orchestrator (own acquiring + connectors routing across Stripe/Adyen/
|
|
1352
|
-
// PayPal). One Payment Form (element/sheet/embedded) + hosted page + wallet button.
|
|
1353
|
-
// Strong native off-session (Recurring API). No popup.
|
|
1354
|
-
solidgate: {
|
|
1355
|
-
isMoR: false,
|
|
1356
|
-
offSessionUpsells: ALL_UPSELLS,
|
|
1357
|
-
tokenModel: "merchant",
|
|
1358
|
-
orchestrator: true,
|
|
1359
|
-
surfaces: { express: ON, element: ON, sheet: ON, embedded: ON, redirect: ON }
|
|
1360
|
-
}
|
|
1361
|
-
};
|
|
1362
|
-
function surfacesFor(provider) {
|
|
1363
|
-
return Object.keys(PROVIDER_PROFILES[provider]?.surfaces ?? {});
|
|
1364
|
-
}
|
|
1365
|
-
function isMerchantOfRecord(provider) {
|
|
1366
|
-
return PROVIDER_PROFILES[provider]?.isMoR ?? false;
|
|
1367
|
-
}
|
|
1368
|
-
function isOrchestrator(provider) {
|
|
1369
|
-
return PROVIDER_PROFILES[provider]?.orchestrator ?? false;
|
|
1370
|
-
}
|
|
1371
|
-
function validateCheckout(provider, surface) {
|
|
1372
|
-
const cap = PROVIDER_PROFILES[provider]?.surfaces[surface];
|
|
1373
|
-
if (!cap) return { ok: false, reason: `${provider} has no '${surface}' surface` };
|
|
1374
|
-
return cap.purchase ? { ok: true } : { ok: false, reason: `'${surface}' can't take a purchase on ${provider}` };
|
|
1375
|
-
}
|
|
1376
|
-
function validateUpsell(provider, paywallSurface, kind = "subscription") {
|
|
1377
|
-
const profile = PROVIDER_PROFILES[provider];
|
|
1378
|
-
if (!profile) return { ok: false, reason: `unknown provider '${provider}'` };
|
|
1379
|
-
if (!profile.offSessionUpsells.includes(kind)) {
|
|
1380
|
-
const can = profile.offSessionUpsells.length ? ` (it supports: ${profile.offSessionUpsells.join(", ")})` : "";
|
|
1381
|
-
return { ok: false, reason: `${provider} can't charge a '${kind}' upsell off-session${can}` };
|
|
1382
|
-
}
|
|
1383
|
-
const cap = profile.surfaces[paywallSurface];
|
|
1384
|
-
if (!cap) return { ok: false, reason: `${provider} has no '${paywallSurface}' surface to upsell after` };
|
|
1385
|
-
if (cap.offSession === "none") {
|
|
1386
|
-
return { ok: false, reason: `a purchase on '${paywallSurface}' retains no chargeable method for an upsell` };
|
|
1387
|
-
}
|
|
1388
|
-
if (cap.offSession === "conditional") {
|
|
1389
|
-
return { ok: true, note: `off-session upsell after ${provider} '${paywallSurface}' (managed checkout) may decline \u2014 prefer element/sheet/express` };
|
|
1390
|
-
}
|
|
1391
|
-
return { ok: true };
|
|
1392
|
-
}
|
|
1393
|
-
function checkoutError(category, message, extra = {}) {
|
|
1394
|
-
const retryable = extra.retryable ?? (category === "authentication_required" || category === "requires_payment_method" || category === "processing_error");
|
|
1395
|
-
return { category, message, declineCode: extra.declineCode, retryable };
|
|
1396
|
-
}
|
|
1397
1379
|
function createMockDriver(provider = "stripe", options = {}) {
|
|
1398
|
-
const success = options.result ?? {
|
|
1399
|
-
|
|
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
|
+
);
|
|
1400
1389
|
return {
|
|
1401
1390
|
provider,
|
|
1402
1391
|
start: (req) => {
|
|
1403
|
-
if (options.failOffSession && !req.surface)
|
|
1392
|
+
if (options.failOffSession && !req.surface)
|
|
1393
|
+
return Promise.resolve({ ok: false, error: offError });
|
|
1404
1394
|
return Promise.resolve(success);
|
|
1405
1395
|
},
|
|
1406
|
-
renderInline: (req, cb) =>
|
|
1396
|
+
renderInline: (req, cb) => React.createElement(
|
|
1407
1397
|
"button",
|
|
1408
1398
|
{
|
|
1409
1399
|
type: "button",
|
|
1410
1400
|
"data-mock-surface": req.surface,
|
|
1411
|
-
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
|
+
}
|
|
1412
1412
|
},
|
|
1413
1413
|
`Pay (mock ${req.surface})`
|
|
1414
|
-
)
|
|
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
|
|
1415
1432
|
};
|
|
1416
1433
|
}
|
|
1417
|
-
var DriverContext =
|
|
1434
|
+
var DriverContext = React.createContext(null);
|
|
1418
1435
|
function CheckoutDriverProvider({
|
|
1419
1436
|
driver,
|
|
1420
1437
|
children
|
|
1421
1438
|
}) {
|
|
1422
|
-
return
|
|
1439
|
+
return React.createElement(DriverContext.Provider, { value: driver }, children);
|
|
1423
1440
|
}
|
|
1424
1441
|
function useDriver() {
|
|
1425
|
-
return
|
|
1442
|
+
return React.useContext(DriverContext) ?? FALLBACK_DRIVER;
|
|
1426
1443
|
}
|
|
1427
1444
|
var FALLBACK_DRIVER = createMockDriver();
|
|
1428
1445
|
function useCheckout(opts = {}) {
|
|
1429
1446
|
const driver = useDriver();
|
|
1430
1447
|
const tracker = useTrackerRef();
|
|
1448
|
+
const catalog = useCatalog();
|
|
1431
1449
|
const nav = useNavigation();
|
|
1432
|
-
const [status, setStatus] =
|
|
1433
|
-
const [error, setError] =
|
|
1450
|
+
const [status, setStatus] = React.useState("idle");
|
|
1451
|
+
const [error, setError] = React.useState(null);
|
|
1434
1452
|
const intent = opts.intent ?? "purchase";
|
|
1435
1453
|
const kind = opts.kind;
|
|
1436
1454
|
const advance = opts.advanceOnSuccess ?? true;
|
|
1437
|
-
const
|
|
1438
|
-
(
|
|
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) => {
|
|
1439
1475
|
setStatus("success");
|
|
1476
|
+
const { productId, isSubscription } = productFacts(product);
|
|
1477
|
+
const amount = result.amountMinor != null ? result.amountMinor / 100 : void 0;
|
|
1440
1478
|
tracker.track("purchase.complete", {
|
|
1441
|
-
amount
|
|
1479
|
+
amount,
|
|
1442
1480
|
currency: result.currency,
|
|
1481
|
+
productId,
|
|
1443
1482
|
eventId: result.eventId
|
|
1444
1483
|
// server-minted (browser↔CAPI dedup); undefined if none
|
|
1445
1484
|
});
|
|
1485
|
+
if (isSubscription) {
|
|
1486
|
+
tracker.track("subscription.created", {
|
|
1487
|
+
amount,
|
|
1488
|
+
currency: result.currency,
|
|
1489
|
+
productId,
|
|
1490
|
+
eventId: result.eventId
|
|
1491
|
+
});
|
|
1492
|
+
}
|
|
1446
1493
|
opts.onSuccess?.(result);
|
|
1447
1494
|
if (advance) nav.next();
|
|
1448
1495
|
},
|
|
1449
|
-
[tracker, nav, advance, opts]
|
|
1450
|
-
);
|
|
1451
|
-
const fail = react.useCallback(
|
|
1452
|
-
(err, product) => {
|
|
1453
|
-
setStatus(err.category === "authentication_required" ? "requires_action" : "error");
|
|
1454
|
-
setError(err);
|
|
1455
|
-
tracker.track("checkout.failed", {
|
|
1456
|
-
category: err.category,
|
|
1457
|
-
declineCode: err.declineCode,
|
|
1458
|
-
productId: product,
|
|
1459
|
-
intent
|
|
1460
|
-
});
|
|
1461
|
-
opts.onError?.(err);
|
|
1462
|
-
},
|
|
1463
|
-
[tracker, intent, opts]
|
|
1496
|
+
[tracker, nav, advance, opts, productFacts]
|
|
1464
1497
|
);
|
|
1465
|
-
const
|
|
1498
|
+
const succeedRef = React.useRef(succeed);
|
|
1499
|
+
succeedRef.current = succeed;
|
|
1500
|
+
const begin = React.useCallback(
|
|
1466
1501
|
(product, surface) => {
|
|
1467
1502
|
setStatus("loading");
|
|
1468
1503
|
setError(null);
|
|
1469
|
-
|
|
1504
|
+
const { productId, amount, currency } = productFacts(product);
|
|
1505
|
+
tracker.track("checkout.start", {
|
|
1506
|
+
productId,
|
|
1507
|
+
amount,
|
|
1508
|
+
currency,
|
|
1509
|
+
surface,
|
|
1510
|
+
eventId: newEventId()
|
|
1511
|
+
});
|
|
1470
1512
|
},
|
|
1471
|
-
[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]
|
|
1472
1526
|
);
|
|
1473
|
-
const
|
|
1474
|
-
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(
|
|
1475
1626
|
async (product, surface) => {
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
const result = await driver.start({ product, intent, surface, kind });
|
|
1479
|
-
if (result.ok) succeed(result);
|
|
1480
|
-
else fail(result.error ?? checkoutError("unknown", "Checkout failed"), product);
|
|
1481
|
-
return result;
|
|
1482
|
-
} catch (e) {
|
|
1483
|
-
const err = checkoutError("processing_error", e instanceof Error ? e.message : "Checkout failed");
|
|
1484
|
-
fail(err, product);
|
|
1485
|
-
return { ok: false, error: err };
|
|
1486
|
-
}
|
|
1627
|
+
recovering.current = false;
|
|
1628
|
+
return attemptRef.current(product, surface);
|
|
1487
1629
|
},
|
|
1488
|
-
[
|
|
1630
|
+
[]
|
|
1489
1631
|
);
|
|
1490
|
-
const callbacksFor =
|
|
1632
|
+
const callbacksFor = React.useCallback(
|
|
1491
1633
|
(product, surface) => ({
|
|
1492
|
-
onStart: () =>
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
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)
|
|
1496
1641
|
}),
|
|
1497
|
-
[begin,
|
|
1642
|
+
[begin, paymentAdded]
|
|
1498
1643
|
);
|
|
1499
|
-
const reset =
|
|
1644
|
+
const reset = React.useCallback(() => {
|
|
1645
|
+
recovering.current = false;
|
|
1500
1646
|
setStatus("idle");
|
|
1501
1647
|
setError(null);
|
|
1502
1648
|
}, []);
|
|
1503
|
-
return {
|
|
1649
|
+
return {
|
|
1650
|
+
open,
|
|
1651
|
+
status,
|
|
1652
|
+
error,
|
|
1653
|
+
isLoading: status === "loading",
|
|
1654
|
+
reset,
|
|
1655
|
+
callbacksFor
|
|
1656
|
+
};
|
|
1504
1657
|
}
|
|
1505
|
-
var CheckoutSheet = defineModal(({ product, surface, intent, onSuccess, onError }) => {
|
|
1658
|
+
var CheckoutSheet = defineModal(({ product, surface, intent, kind, onSuccess, onError, onPaymentAdded }) => {
|
|
1506
1659
|
const driver = useDriver();
|
|
1507
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
|
+
}, []);
|
|
1508
1689
|
if (!driver.renderInline) return null;
|
|
1509
|
-
|
|
1690
|
+
const close = () => {
|
|
1691
|
+
resolved.current = true;
|
|
1692
|
+
void modal.hide();
|
|
1693
|
+
modal.remove();
|
|
1694
|
+
};
|
|
1695
|
+
return React.createElement(Sheet, {
|
|
1510
1696
|
children: driver.renderInline(
|
|
1511
|
-
{ product, intent, surface },
|
|
1697
|
+
{ product, intent, surface, kind },
|
|
1512
1698
|
{
|
|
1513
1699
|
onSuccess: (r) => {
|
|
1514
|
-
|
|
1700
|
+
close();
|
|
1515
1701
|
onSuccess(r);
|
|
1516
1702
|
},
|
|
1517
1703
|
onError: (e) => {
|
|
1518
|
-
|
|
1704
|
+
close();
|
|
1519
1705
|
onError(e);
|
|
1520
|
-
}
|
|
1706
|
+
},
|
|
1707
|
+
onPaymentAdded
|
|
1521
1708
|
}
|
|
1522
1709
|
)
|
|
1523
1710
|
});
|
|
1524
1711
|
});
|
|
1525
|
-
function trigger(onClick, {
|
|
1526
|
-
|
|
1712
|
+
function trigger(onClick, {
|
|
1713
|
+
asChild,
|
|
1714
|
+
children,
|
|
1715
|
+
className,
|
|
1716
|
+
disabled
|
|
1717
|
+
}) {
|
|
1718
|
+
if (asChild && React.isValidElement(children)) {
|
|
1527
1719
|
const child = children;
|
|
1528
|
-
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 {
|
|
1529
1737
|
}
|
|
1530
|
-
return
|
|
1738
|
+
return "desktop";
|
|
1531
1739
|
}
|
|
1532
1740
|
function Checkout({
|
|
1533
|
-
|
|
1534
|
-
surface,
|
|
1741
|
+
offering,
|
|
1742
|
+
surface: surfacePreference,
|
|
1535
1743
|
asChild,
|
|
1536
1744
|
children,
|
|
1537
1745
|
className,
|
|
1538
1746
|
...options
|
|
1539
1747
|
}) {
|
|
1748
|
+
const product = offering;
|
|
1540
1749
|
const driver = useDriver();
|
|
1750
|
+
const [surface] = React.useState(
|
|
1751
|
+
() => chunk7JLOF6CJ_cjs.resolveSurface(driver.provider, surfacePreference, detectDeviceClass())
|
|
1752
|
+
);
|
|
1541
1753
|
const checkout = useCheckout(options);
|
|
1542
1754
|
const intent = options.intent ?? "purchase";
|
|
1543
|
-
if (isInlineSurface(surface)) {
|
|
1544
|
-
return driver.renderInline ?
|
|
1755
|
+
if (chunk7JLOF6CJ_cjs.isInlineSurface(surface)) {
|
|
1756
|
+
return driver.renderInline ? React.createElement(
|
|
1545
1757
|
"div",
|
|
1546
1758
|
{ key: product, className },
|
|
1547
|
-
driver.renderInline(
|
|
1759
|
+
driver.renderInline(
|
|
1760
|
+
{ product, intent, surface },
|
|
1761
|
+
checkout.callbacksFor(product, surface)
|
|
1762
|
+
)
|
|
1548
1763
|
) : null;
|
|
1549
1764
|
}
|
|
1550
1765
|
if (surface === "sheet") {
|
|
1551
1766
|
const cb = checkout.callbacksFor(product, surface);
|
|
1552
1767
|
const open = () => {
|
|
1553
1768
|
cb.onStart();
|
|
1554
|
-
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
|
+
});
|
|
1555
1778
|
};
|
|
1556
|
-
return trigger(open, {
|
|
1779
|
+
return trigger(open, {
|
|
1780
|
+
asChild,
|
|
1781
|
+
children,
|
|
1782
|
+
className,
|
|
1783
|
+
disabled: checkout.isLoading
|
|
1784
|
+
});
|
|
1557
1785
|
}
|
|
1558
1786
|
return trigger(() => checkout.open(product, surface), {
|
|
1559
1787
|
asChild,
|
|
@@ -1562,32 +1790,1150 @@ function Checkout({
|
|
|
1562
1790
|
disabled: checkout.isLoading
|
|
1563
1791
|
});
|
|
1564
1792
|
}
|
|
1565
|
-
function Upsell({
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
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();
|
|
1574
2421
|
} else {
|
|
1575
|
-
|
|
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);
|
|
1576
2674
|
}
|
|
1577
|
-
};
|
|
1578
|
-
return trigger(start, {
|
|
1579
|
-
asChild,
|
|
1580
|
-
children: children ?? "Add to my plan",
|
|
1581
|
-
className,
|
|
1582
|
-
disabled: checkout.isLoading
|
|
1583
2675
|
});
|
|
1584
|
-
|
|
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
|
+
});
|
|
1585
2931
|
function FunnelToaster(props = {}) {
|
|
1586
|
-
return
|
|
2932
|
+
return React.createElement(Toaster, { position: "top-center", ...props });
|
|
1587
2933
|
}
|
|
1588
2934
|
function renderToaster(toaster) {
|
|
1589
2935
|
if (toaster === false) return null;
|
|
1590
|
-
return
|
|
2936
|
+
return /* @__PURE__ */ jsxRuntime.jsx(FunnelToaster, { ...toaster === true ? {} : toaster });
|
|
1591
2937
|
}
|
|
1592
2938
|
|
|
1593
2939
|
// src/state/namespaces.ts
|
|
@@ -1611,7 +2957,8 @@ var Namespace = class {
|
|
|
1611
2957
|
const out = {};
|
|
1612
2958
|
const all = this.store.getState();
|
|
1613
2959
|
for (const key in all) {
|
|
1614
|
-
if (key.startsWith(this.dot))
|
|
2960
|
+
if (key.startsWith(this.dot))
|
|
2961
|
+
out[key.slice(this.dot.length)] = all[key];
|
|
1615
2962
|
}
|
|
1616
2963
|
return out;
|
|
1617
2964
|
}
|
|
@@ -1653,13 +3000,14 @@ function createFunnel(store, context, tracker, experiments = []) {
|
|
|
1653
3000
|
})
|
|
1654
3001
|
};
|
|
1655
3002
|
}
|
|
1656
|
-
var FunnelCtx =
|
|
3003
|
+
var FunnelCtx = React.createContext(null);
|
|
1657
3004
|
function FunnelProvider({
|
|
1658
3005
|
config,
|
|
1659
3006
|
sessionValues,
|
|
3007
|
+
deferSessionValues = false,
|
|
1660
3008
|
tracker,
|
|
1661
3009
|
context: contextOpts,
|
|
1662
|
-
|
|
3010
|
+
offerings,
|
|
1663
3011
|
experiments,
|
|
1664
3012
|
checkout,
|
|
1665
3013
|
messages,
|
|
@@ -1669,10 +3017,15 @@ function FunnelProvider({
|
|
|
1669
3017
|
children
|
|
1670
3018
|
}) {
|
|
1671
3019
|
const resolvedTracker = tracker ?? FALLBACK_TRACKER;
|
|
1672
|
-
const [value] =
|
|
3020
|
+
const [value] = React.useState(() => {
|
|
1673
3021
|
const s = store ?? createFunnelStore(
|
|
1674
3022
|
{ responses: config?.responses, data: config?.data },
|
|
1675
|
-
|
|
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
|
|
1676
3029
|
);
|
|
1677
3030
|
const ctx = buildContext({
|
|
1678
3031
|
funnelId: config?.id,
|
|
@@ -1686,65 +3039,90 @@ function FunnelProvider({
|
|
|
1686
3039
|
getVisitorId: () => ctx.identity.visitorId
|
|
1687
3040
|
});
|
|
1688
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 ?? []);
|
|
1689
3047
|
return {
|
|
1690
3048
|
funnel: createFunnel(s, ctx, busTracker, experiments ?? []),
|
|
1691
3049
|
store: s,
|
|
1692
3050
|
context: ctx,
|
|
1693
|
-
catalog
|
|
1694
|
-
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
|
+
),
|
|
1695
3061
|
tracker: busTracker,
|
|
1696
3062
|
bus
|
|
1697
3063
|
};
|
|
1698
3064
|
});
|
|
1699
|
-
|
|
1700
|
-
|
|
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(() => {
|
|
1701
3074
|
value.tracker.setAcquisition?.(buildAcquisition(value.context));
|
|
1702
3075
|
}, [value]);
|
|
1703
|
-
const tree =
|
|
3076
|
+
const tree = React.createElement(TrackerProvider, {
|
|
1704
3077
|
tracker: value.tracker,
|
|
1705
|
-
children:
|
|
3078
|
+
children: React.createElement(CheckoutDriverProvider, {
|
|
1706
3079
|
driver: value.driver,
|
|
1707
|
-
children:
|
|
3080
|
+
children: React.createElement(CatalogProvider, {
|
|
1708
3081
|
catalog: value.catalog,
|
|
1709
|
-
children:
|
|
3082
|
+
children: React.createElement(FunnelCtx.Provider, {
|
|
1710
3083
|
value,
|
|
1711
|
-
children:
|
|
3084
|
+
children: React.createElement(LocaleProvider, {
|
|
1712
3085
|
config: config?.locales,
|
|
1713
3086
|
catalog: messages,
|
|
1714
3087
|
detected: value.context.locale.locale,
|
|
1715
3088
|
override: localeOverride,
|
|
1716
|
-
children:
|
|
3089
|
+
children: React.createElement(ModalRuntime, { children })
|
|
1717
3090
|
})
|
|
1718
3091
|
})
|
|
1719
3092
|
})
|
|
1720
3093
|
})
|
|
1721
3094
|
});
|
|
1722
|
-
return
|
|
3095
|
+
return React.createElement(React.Fragment, null, tree, renderToaster(toaster));
|
|
1723
3096
|
}
|
|
1724
3097
|
var FALLBACK_TRACKER = createConsoleTracker({ silent: true });
|
|
1725
3098
|
function useFunnelCtx() {
|
|
1726
|
-
const ctx =
|
|
3099
|
+
const ctx = React.useContext(FunnelCtx);
|
|
1727
3100
|
if (!ctx) {
|
|
1728
|
-
throw new Error(
|
|
3101
|
+
throw new Error(
|
|
3102
|
+
"useFunnel/useResponse/\u2026 must be used inside <FunnelProvider>"
|
|
3103
|
+
);
|
|
1729
3104
|
}
|
|
1730
3105
|
return ctx;
|
|
1731
3106
|
}
|
|
1732
3107
|
function useFunnel() {
|
|
1733
3108
|
return useFunnelCtx().funnel;
|
|
1734
3109
|
}
|
|
3110
|
+
function useBusInternal() {
|
|
3111
|
+
return React.useContext(FunnelCtx)?.bus ?? null;
|
|
3112
|
+
}
|
|
1735
3113
|
function useStoreKey(store, key) {
|
|
1736
|
-
const subscribe =
|
|
3114
|
+
const subscribe = React.useCallback(
|
|
1737
3115
|
(cb) => store.subscribe(cb, { keys: [key] }),
|
|
1738
3116
|
[store, key]
|
|
1739
3117
|
);
|
|
1740
|
-
const getSnapshot =
|
|
1741
|
-
return
|
|
3118
|
+
const getSnapshot = React.useCallback(() => store.get(key), [store, key]);
|
|
3119
|
+
return React.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
1742
3120
|
}
|
|
1743
3121
|
function makeFieldHook(ns) {
|
|
1744
3122
|
return function useNamespaceField(key) {
|
|
1745
3123
|
const { funnel, store } = useFunnelCtx();
|
|
1746
3124
|
const value = useStoreKey(store, `${NAMESPACE_PREFIX[ns]}.${key}`);
|
|
1747
|
-
const set =
|
|
3125
|
+
const set = React.useCallback(
|
|
1748
3126
|
(v) => funnel[ns].set({ [key]: v }),
|
|
1749
3127
|
[funnel, key]
|
|
1750
3128
|
);
|
|
@@ -1760,10 +3138,12 @@ function useField(path) {
|
|
|
1760
3138
|
const key = path.slice(dot + 1);
|
|
1761
3139
|
const { funnel, store } = useFunnelCtx();
|
|
1762
3140
|
if (!(ns in NAMESPACE_PREFIX)) {
|
|
1763
|
-
throw new Error(
|
|
3141
|
+
throw new Error(
|
|
3142
|
+
`useField: "${path}" is not a writable namespace (user/responses/data)`
|
|
3143
|
+
);
|
|
1764
3144
|
}
|
|
1765
3145
|
const value = useStoreKey(store, `${NAMESPACE_PREFIX[ns]}.${key}`);
|
|
1766
|
-
const set =
|
|
3146
|
+
const set = React.useCallback(
|
|
1767
3147
|
(v) => funnel[ns].set({ [key]: v }),
|
|
1768
3148
|
[funnel, ns, key]
|
|
1769
3149
|
);
|
|
@@ -1789,11 +3169,404 @@ function useSystem() {
|
|
|
1789
3169
|
}
|
|
1790
3170
|
function useContextValue(path) {
|
|
1791
3171
|
const ctx = useContextObject();
|
|
1792
|
-
return
|
|
1793
|
-
() => path.split(".").reduce(
|
|
3172
|
+
return React.useMemo(
|
|
3173
|
+
() => path.split(".").reduce(
|
|
3174
|
+
(acc, k) => acc?.[k],
|
|
3175
|
+
ctx
|
|
3176
|
+
),
|
|
1794
3177
|
[ctx, path]
|
|
1795
3178
|
);
|
|
1796
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
|
+
}
|
|
1797
3570
|
function Choice({
|
|
1798
3571
|
bind,
|
|
1799
3572
|
options,
|
|
@@ -1806,7 +3579,7 @@ function Choice({
|
|
|
1806
3579
|
return options.map((opt) => {
|
|
1807
3580
|
const o = typeof opt === "string" ? { value: opt } : opt;
|
|
1808
3581
|
const selected = value === o.value;
|
|
1809
|
-
return
|
|
3582
|
+
return React.createElement(
|
|
1810
3583
|
"button",
|
|
1811
3584
|
{
|
|
1812
3585
|
key: o.value,
|
|
@@ -1825,7 +3598,7 @@ function Choice({
|
|
|
1825
3598
|
}
|
|
1826
3599
|
function Next({ children, onClick, ...rest }) {
|
|
1827
3600
|
const { next } = useNavigation();
|
|
1828
|
-
return
|
|
3601
|
+
return React.createElement(
|
|
1829
3602
|
"button",
|
|
1830
3603
|
{
|
|
1831
3604
|
type: "button",
|
|
@@ -1841,7 +3614,7 @@ function Next({ children, onClick, ...rest }) {
|
|
|
1841
3614
|
function Back({ children, onClick, ...rest }) {
|
|
1842
3615
|
const { back, canGoBack } = useNavigation();
|
|
1843
3616
|
if (!canGoBack) return null;
|
|
1844
|
-
return
|
|
3617
|
+
return React.createElement(
|
|
1845
3618
|
"button",
|
|
1846
3619
|
{
|
|
1847
3620
|
type: "button",
|
|
@@ -1894,7 +3667,7 @@ function Script({
|
|
|
1894
3667
|
onLoad,
|
|
1895
3668
|
onError
|
|
1896
3669
|
}) {
|
|
1897
|
-
|
|
3670
|
+
React.useEffect(() => {
|
|
1898
3671
|
if (typeof document === "undefined") return;
|
|
1899
3672
|
const key = id ?? src;
|
|
1900
3673
|
if (injected.has(key)) {
|
|
@@ -1933,159 +3706,198 @@ function Script({
|
|
|
1933
3706
|
return null;
|
|
1934
3707
|
}
|
|
1935
3708
|
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
return
|
|
1939
|
-
}
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
function
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
}
|
|
2023
|
-
}
|
|
2024
|
-
|
|
2025
|
-
|
|
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", {
|
|
2026
3858
|
enumerable: true,
|
|
2027
|
-
get: function () { return
|
|
3859
|
+
get: function () { return chunk7JLOF6CJ_cjs.validateUpsell; }
|
|
2028
3860
|
});
|
|
3861
|
+
exports.AssetsContext = AssetsContext;
|
|
3862
|
+
exports.AssetsProvider = AssetsProvider;
|
|
2029
3863
|
exports.Back = Back;
|
|
2030
3864
|
exports.Checkout = Checkout;
|
|
3865
|
+
exports.CheckoutResume = CheckoutResume;
|
|
2031
3866
|
exports.Choice = Choice;
|
|
2032
3867
|
exports.EVENT_CATALOG = EVENT_CATALOG;
|
|
2033
3868
|
exports.FunnelProvider = FunnelProvider;
|
|
2034
3869
|
exports.FunnelToaster = FunnelToaster;
|
|
2035
3870
|
exports.FunnelView = FunnelView;
|
|
2036
|
-
exports.INLINE_SURFACES = INLINE_SURFACES;
|
|
2037
3871
|
exports.Modal = Modal;
|
|
2038
3872
|
exports.Next = Next;
|
|
2039
|
-
exports.PROVIDER_PROFILES = PROVIDER_PROFILES;
|
|
2040
3873
|
exports.Script = Script;
|
|
2041
3874
|
exports.Sheet = Sheet;
|
|
2042
3875
|
exports.Upsell = Upsell;
|
|
2043
3876
|
exports.VariableStore = VariableStore;
|
|
2044
|
-
exports.assignVariant = assignVariant;
|
|
2045
3877
|
exports.attachBus = attachBus;
|
|
2046
|
-
exports.bucketingSeed = bucketingSeed;
|
|
2047
3878
|
exports.buildAcquisition = buildAcquisition;
|
|
2048
|
-
exports.
|
|
2049
|
-
exports.checkoutError = checkoutError;
|
|
2050
|
-
exports.compileManifest = compileManifest;
|
|
3879
|
+
exports.buildContext = buildContext;
|
|
2051
3880
|
exports.createBus = createBus;
|
|
2052
3881
|
exports.createConsoleTracker = createConsoleTracker;
|
|
2053
3882
|
exports.createFunnelStore = createFunnelStore;
|
|
3883
|
+
exports.createFunnelTree = createFunnelTree;
|
|
2054
3884
|
exports.createMockDriver = createMockDriver;
|
|
2055
|
-
exports.
|
|
3885
|
+
exports.createPlatformTracker = createPlatformTracker;
|
|
2056
3886
|
exports.defineModal = defineModal;
|
|
2057
|
-
exports.
|
|
2058
|
-
exports.entryGuard = entryGuard;
|
|
2059
|
-
exports.evaluateCondition = evaluateCondition;
|
|
2060
|
-
exports.evaluateGate = evaluateGate;
|
|
2061
|
-
exports.expectedPathLength = expectedPathLength;
|
|
2062
|
-
exports.fnv1a = fnv1a;
|
|
2063
|
-
exports.formatProduct = formatProduct;
|
|
2064
|
-
exports.hashToUnit = hashToUnit;
|
|
3887
|
+
exports.driverWithEmail = driverWithEmail;
|
|
2065
3888
|
exports.hideModal = hideModal;
|
|
2066
3889
|
exports.integrationEvents = integrationEvents;
|
|
2067
|
-
exports.isInlineSurface = isInlineSurface;
|
|
2068
3890
|
exports.isIntegrationEvent = isIntegrationEvent;
|
|
2069
|
-
exports.isMerchantOfRecord = isMerchantOfRecord;
|
|
2070
|
-
exports.isOrchestrator = isOrchestrator;
|
|
2071
|
-
exports.isRtl = isRtl;
|
|
2072
3891
|
exports.isTrackableEvent = isTrackableEvent;
|
|
2073
|
-
exports.isVariantKey = isVariantKey;
|
|
2074
3892
|
exports.newEventId = newEventId;
|
|
2075
|
-
exports.nextPage = nextPage;
|
|
2076
|
-
exports.outgoingKeys = outgoingKeys;
|
|
2077
|
-
exports.pageMeta = pageMeta;
|
|
2078
|
-
exports.parseSlotKey = parseSlotKey;
|
|
2079
|
-
exports.pickByWeight = pickByWeight;
|
|
2080
3893
|
exports.registerModal = registerModal;
|
|
2081
3894
|
exports.removeModal = removeModal;
|
|
2082
|
-
exports.resolveExperiments = resolveExperiments;
|
|
2083
|
-
exports.resolveLocale = resolveLocale;
|
|
2084
|
-
exports.resolveProduct = resolveProduct;
|
|
2085
|
-
exports.resolveRoute = resolveRoute;
|
|
2086
3895
|
exports.showModal = showModal;
|
|
2087
|
-
exports.
|
|
3896
|
+
exports.toast = toast;
|
|
2088
3897
|
exports.unregisterModal = unregisterModal;
|
|
3898
|
+
exports.useActiveLocale = useActiveLocale;
|
|
3899
|
+
exports.useAsset = useAsset;
|
|
3900
|
+
exports.useAssets = useAssets;
|
|
2089
3901
|
exports.useCheckout = useCheckout;
|
|
2090
3902
|
exports.useClickIds = useClickIds;
|
|
2091
3903
|
exports.useContextValue = useContextValue;
|
|
@@ -2097,18 +3909,15 @@ exports.useFunnel = useFunnel;
|
|
|
2097
3909
|
exports.useLocale = useLocale;
|
|
2098
3910
|
exports.useModal = useModal;
|
|
2099
3911
|
exports.useNavigation = useNavigation;
|
|
3912
|
+
exports.useOffering = useOffering;
|
|
3913
|
+
exports.useOfferings = useOfferings;
|
|
2100
3914
|
exports.usePage = usePage;
|
|
2101
|
-
exports.useProduct = useProduct;
|
|
2102
|
-
exports.useProducts = useProducts;
|
|
2103
3915
|
exports.useResponse = useResponse;
|
|
2104
3916
|
exports.useSystem = useSystem;
|
|
2105
3917
|
exports.useTracker = useTracker;
|
|
2106
3918
|
exports.useTranslation = useTranslation;
|
|
2107
3919
|
exports.useUserAttribute = useUserAttribute;
|
|
2108
3920
|
exports.useUtm = useUtm;
|
|
2109
|
-
exports.validateCheckout = validateCheckout;
|
|
2110
|
-
exports.validateExperiments = validateExperiments;
|
|
2111
|
-
exports.validateUpsell = validateUpsell;
|
|
2112
3921
|
exports.withBus = withBus;
|
|
2113
3922
|
//# sourceMappingURL=index.cjs.map
|
|
2114
3923
|
//# sourceMappingURL=index.cjs.map
|