@appfunnel-dev/sdk 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-BUF5FDKC.cjs → chunk-EVUYCLVY.cjs} +42 -19
- package/dist/chunk-EVUYCLVY.cjs.map +1 -0
- package/dist/{chunk-E6KSJ5UI.js → chunk-H3KHXZSI.js} +42 -20
- package/dist/chunk-H3KHXZSI.js.map +1 -0
- package/dist/chunk-P4SLDMWY.js +104 -0
- package/dist/chunk-P4SLDMWY.js.map +1 -0
- package/dist/chunk-XP44I2MU.cjs +108 -0
- package/dist/chunk-XP44I2MU.cjs.map +1 -0
- package/dist/elements/index.cjs +12172 -0
- package/dist/elements/index.cjs.map +1 -0
- package/dist/elements/index.d.cts +602 -0
- package/dist/elements/index.d.ts +602 -0
- package/dist/elements/index.js +12137 -0
- package/dist/elements/index.js.map +1 -0
- package/dist/index.cjs +233 -114
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +70 -3
- package/dist/index.d.ts +70 -3
- package/dist/index.js +199 -91
- package/dist/index.js.map +1 -1
- package/dist/{internal-BlgQ9C2d.d.cts → internal-C7seLJBr.d.cts} +1 -0
- package/dist/{internal-BlgQ9C2d.d.ts → internal-C7seLJBr.d.ts} +1 -0
- package/dist/internal.cjs +2 -2
- package/dist/internal.d.cts +1 -1
- package/dist/internal.d.ts +1 -1
- package/dist/internal.js +1 -1
- package/package.json +17 -2
- package/dist/chunk-BUF5FDKC.cjs.map +0 -1
- package/dist/chunk-E6KSJ5UI.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export {
|
|
1
|
+
import { useNavigation, useResponses } from './chunk-P4SLDMWY.js';
|
|
2
|
+
export { useNavigation, useResponse, useResponses } from './chunk-P4SLDMWY.js';
|
|
3
|
+
import { useFunnelContext } from './chunk-H3KHXZSI.js';
|
|
4
|
+
export { FunnelProvider, registerIntegration } from './chunk-H3KHXZSI.js';
|
|
3
5
|
import { forwardRef, useState, useRef, useEffect, useCallback, useImperativeHandle, useMemo, useSyncExternalStore } from 'react';
|
|
4
6
|
import { loadStripe } from '@stripe/stripe-js';
|
|
5
7
|
import { useStripe, useElements, PaymentElement, EmbeddedCheckoutProvider, EmbeddedCheckout, Elements } from '@stripe/react-stripe-js';
|
|
@@ -41,6 +43,102 @@ function useVariables() {
|
|
|
41
43
|
);
|
|
42
44
|
return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
43
45
|
}
|
|
46
|
+
|
|
47
|
+
// src/utils/date.ts
|
|
48
|
+
function toISODate(input) {
|
|
49
|
+
if (!input || !input.trim()) return "";
|
|
50
|
+
const s = input.trim();
|
|
51
|
+
if (/^\d{4}-\d{2}-\d{2}$/.test(s)) return s;
|
|
52
|
+
if (/^\d{4}-\d{2}-\d{2}T/.test(s)) return s.slice(0, 10);
|
|
53
|
+
const sepMatch = s.match(/^(\d{1,2})[/\-.](\d{1,2})[/\-.](\d{4})$/);
|
|
54
|
+
if (sepMatch) {
|
|
55
|
+
const a = parseInt(sepMatch[1], 10);
|
|
56
|
+
const b = parseInt(sepMatch[2], 10);
|
|
57
|
+
const year = sepMatch[3];
|
|
58
|
+
let month;
|
|
59
|
+
let day;
|
|
60
|
+
if (a > 12 && b <= 12) {
|
|
61
|
+
day = a;
|
|
62
|
+
month = b;
|
|
63
|
+
} else if (b > 12 && a <= 12) {
|
|
64
|
+
month = a;
|
|
65
|
+
day = b;
|
|
66
|
+
} else {
|
|
67
|
+
month = a;
|
|
68
|
+
day = b;
|
|
69
|
+
}
|
|
70
|
+
if (month >= 1 && month <= 12 && day >= 1 && day <= 31) {
|
|
71
|
+
return `${year}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const ymdSlash = s.match(/^(\d{4})[/\-.](\d{1,2})[/\-.](\d{1,2})$/);
|
|
75
|
+
if (ymdSlash) {
|
|
76
|
+
const year = ymdSlash[1];
|
|
77
|
+
const month = parseInt(ymdSlash[2], 10);
|
|
78
|
+
const day = parseInt(ymdSlash[3], 10);
|
|
79
|
+
if (month >= 1 && month <= 12 && day >= 1 && day <= 31) {
|
|
80
|
+
return `${year}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (/^\d{8}$/.test(s)) {
|
|
84
|
+
const a = parseInt(s.slice(0, 2), 10);
|
|
85
|
+
const b = parseInt(s.slice(2, 4), 10);
|
|
86
|
+
const year = s.slice(4, 8);
|
|
87
|
+
let month;
|
|
88
|
+
let day;
|
|
89
|
+
if (a > 12 && b <= 12) {
|
|
90
|
+
day = a;
|
|
91
|
+
month = b;
|
|
92
|
+
} else {
|
|
93
|
+
month = a;
|
|
94
|
+
day = b;
|
|
95
|
+
}
|
|
96
|
+
if (month >= 1 && month <= 12 && day >= 1 && day <= 31) {
|
|
97
|
+
return `${year}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
throw new Error(
|
|
101
|
+
`[AppFunnel] Invalid date format: "${input}". Expected a date string like MM/DD/YYYY, DD/MM/YYYY, YYYY-MM-DD, or MMDDYYYY.`
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
function toISODateWithFormat(input, format) {
|
|
105
|
+
if (!input || !input.trim()) return "";
|
|
106
|
+
const s = input.trim();
|
|
107
|
+
if (/^\d{4}-\d{2}-\d{2}$/.test(s) || /^\d{4}-\d{2}-\d{2}T/.test(s)) {
|
|
108
|
+
return s.slice(0, 10);
|
|
109
|
+
}
|
|
110
|
+
const digits = s.replace(/[^\d]/g, "");
|
|
111
|
+
if (format === "YYYY-MM-DD") {
|
|
112
|
+
const m = s.match(/^(\d{4})[/\-.](\d{1,2})[/\-.](\d{1,2})$/);
|
|
113
|
+
if (m) return `${m[1]}-${m[2].padStart(2, "0")}-${m[3].padStart(2, "0")}`;
|
|
114
|
+
if (digits.length === 8) {
|
|
115
|
+
return `${digits.slice(0, 4)}-${digits.slice(4, 6)}-${digits.slice(6, 8)}`;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (format === "MM/DD/YYYY") {
|
|
119
|
+
const m = s.match(/^(\d{1,2})[/\-.](\d{1,2})[/\-.](\d{4})$/);
|
|
120
|
+
if (m) return `${m[3]}-${m[1].padStart(2, "0")}-${m[2].padStart(2, "0")}`;
|
|
121
|
+
if (digits.length === 8) {
|
|
122
|
+
return `${digits.slice(4, 8)}-${digits.slice(0, 2)}-${digits.slice(2, 4)}`;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (format === "DD/MM/YYYY") {
|
|
126
|
+
const m = s.match(/^(\d{1,2})[/\-.](\d{1,2})[/\-.](\d{4})$/);
|
|
127
|
+
if (m) return `${m[3]}-${m[2].padStart(2, "0")}-${m[1].padStart(2, "0")}`;
|
|
128
|
+
if (digits.length === 8) {
|
|
129
|
+
return `${digits.slice(4, 8)}-${digits.slice(2, 4)}-${digits.slice(0, 2)}`;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
throw new Error(
|
|
133
|
+
`[AppFunnel] Invalid date format: "${input}". Expected format ${format} (e.g. ${{
|
|
134
|
+
"MM/DD/YYYY": "03/15/1990 or 03151990",
|
|
135
|
+
"DD/MM/YYYY": "15/03/1990 or 15031990",
|
|
136
|
+
"YYYY-MM-DD": "1990-03-15 or 19900315"
|
|
137
|
+
}[format]}).`
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// src/hooks/useUser.ts
|
|
44
142
|
function useUser() {
|
|
45
143
|
const { variableStore } = useFunnelContext();
|
|
46
144
|
const subscribe = useCallback(
|
|
@@ -66,7 +164,7 @@ function useUser() {
|
|
|
66
164
|
variableStore.set("user.name", name);
|
|
67
165
|
},
|
|
68
166
|
setDateOfBirth(dateOfBirth) {
|
|
69
|
-
variableStore.set("user.dateOfBirth", dateOfBirth);
|
|
167
|
+
variableStore.set("user.dateOfBirth", toISODate(dateOfBirth));
|
|
70
168
|
}
|
|
71
169
|
}),
|
|
72
170
|
[variables, variableStore]
|
|
@@ -90,45 +188,24 @@ function useUserProperty(field) {
|
|
|
90
188
|
);
|
|
91
189
|
return [value, setValue];
|
|
92
190
|
}
|
|
93
|
-
function
|
|
191
|
+
function useDateOfBirth(format = "MM/DD/YYYY") {
|
|
94
192
|
const { variableStore } = useFunnelContext();
|
|
95
|
-
const
|
|
193
|
+
const key = "user.dateOfBirth";
|
|
96
194
|
const subscribe = useCallback(
|
|
97
|
-
(cb) => variableStore.subscribe(cb, { keys: [
|
|
98
|
-
[variableStore
|
|
195
|
+
(cb) => variableStore.subscribe(cb, { keys: [key] }),
|
|
196
|
+
[variableStore]
|
|
99
197
|
);
|
|
100
198
|
const getSnapshot = useCallback(
|
|
101
|
-
() => variableStore.get(
|
|
102
|
-
[variableStore
|
|
199
|
+
() => variableStore.get(key) || "",
|
|
200
|
+
[variableStore]
|
|
103
201
|
);
|
|
104
202
|
const value = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
105
203
|
const setValue = useCallback(
|
|
106
|
-
(v) => variableStore.set(
|
|
107
|
-
[variableStore,
|
|
204
|
+
(v) => variableStore.set(key, toISODateWithFormat(v, format)),
|
|
205
|
+
[variableStore, format]
|
|
108
206
|
);
|
|
109
207
|
return [value, setValue];
|
|
110
208
|
}
|
|
111
|
-
function useResponses() {
|
|
112
|
-
const { variableStore } = useFunnelContext();
|
|
113
|
-
const subscribe = useCallback(
|
|
114
|
-
(cb) => variableStore.subscribe(cb, { prefix: "answers." }),
|
|
115
|
-
[variableStore]
|
|
116
|
-
);
|
|
117
|
-
const getSnapshot = useCallback(
|
|
118
|
-
() => variableStore.getState(),
|
|
119
|
-
[variableStore]
|
|
120
|
-
);
|
|
121
|
-
const variables = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
122
|
-
return useMemo(() => {
|
|
123
|
-
const result = {};
|
|
124
|
-
for (const [key, value] of Object.entries(variables)) {
|
|
125
|
-
if (key.startsWith("answers.")) {
|
|
126
|
-
result[key.slice(8)] = value;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
return result;
|
|
130
|
-
}, [variables]);
|
|
131
|
-
}
|
|
132
209
|
function useQueryParams() {
|
|
133
210
|
const { variableStore } = useFunnelContext();
|
|
134
211
|
const subscribe = useCallback(
|
|
@@ -239,64 +316,6 @@ function useTranslation() {
|
|
|
239
316
|
const availableLocales = i18n.getAvailableLocales();
|
|
240
317
|
return { t, locale, setLocale, availableLocales };
|
|
241
318
|
}
|
|
242
|
-
function useNavigation() {
|
|
243
|
-
const { router, variableStore, tracker } = useFunnelContext();
|
|
244
|
-
useSyncExternalStore(
|
|
245
|
-
useCallback((cb) => router.subscribe(cb), [router]),
|
|
246
|
-
useCallback(() => router.getSnapshot(), [router]),
|
|
247
|
-
useCallback(() => router.getSnapshot(), [router])
|
|
248
|
-
);
|
|
249
|
-
const afterNavigate = useCallback((key) => {
|
|
250
|
-
const page = router.getCurrentPage();
|
|
251
|
-
if (page) {
|
|
252
|
-
tracker.track("page.view", {
|
|
253
|
-
pageId: page.key,
|
|
254
|
-
pageKey: page.key,
|
|
255
|
-
pageName: page.name
|
|
256
|
-
});
|
|
257
|
-
tracker.startPageTracking(page.key);
|
|
258
|
-
}
|
|
259
|
-
variableStore.setMany({
|
|
260
|
-
"page.currentId": key,
|
|
261
|
-
"page.currentIndex": router.getPageHistory().length,
|
|
262
|
-
"page.current": router.getPageHistory().length + 1,
|
|
263
|
-
"page.startedAt": Date.now()
|
|
264
|
-
});
|
|
265
|
-
}, [router, tracker, variableStore]);
|
|
266
|
-
const goToNextPage = useCallback(() => {
|
|
267
|
-
const previousPage = router.getCurrentPage();
|
|
268
|
-
if (previousPage) {
|
|
269
|
-
tracker.stopPageTracking();
|
|
270
|
-
}
|
|
271
|
-
const variables = variableStore.getState();
|
|
272
|
-
const nextKey = router.goToNextPage(variables);
|
|
273
|
-
if (nextKey) {
|
|
274
|
-
afterNavigate(nextKey);
|
|
275
|
-
}
|
|
276
|
-
}, [router, tracker, variableStore, afterNavigate]);
|
|
277
|
-
const goBack = useCallback(() => {
|
|
278
|
-
tracker.stopPageTracking();
|
|
279
|
-
const prevKey = router.goBack();
|
|
280
|
-
if (prevKey) {
|
|
281
|
-
afterNavigate(prevKey);
|
|
282
|
-
}
|
|
283
|
-
}, [router, tracker, afterNavigate]);
|
|
284
|
-
const goToPage = useCallback((pageKey) => {
|
|
285
|
-
tracker.stopPageTracking();
|
|
286
|
-
const key = router.goToPage(pageKey);
|
|
287
|
-
if (key) {
|
|
288
|
-
afterNavigate(key);
|
|
289
|
-
}
|
|
290
|
-
}, [router, tracker, afterNavigate]);
|
|
291
|
-
return {
|
|
292
|
-
goToNextPage,
|
|
293
|
-
goBack,
|
|
294
|
-
goToPage,
|
|
295
|
-
currentPage: router.getCurrentPage(),
|
|
296
|
-
pageHistory: router.getPageHistory(),
|
|
297
|
-
progress: router.getProgress()
|
|
298
|
-
};
|
|
299
|
-
}
|
|
300
319
|
function useProducts() {
|
|
301
320
|
const { products, variableStore, selectProduct: ctxSelect } = useFunnelContext();
|
|
302
321
|
const subscribe = useCallback(
|
|
@@ -421,6 +440,95 @@ function useDeviceInfo() {
|
|
|
421
440
|
}
|
|
422
441
|
}), [variables]);
|
|
423
442
|
}
|
|
443
|
+
function useSafeArea() {
|
|
444
|
+
const [insets, setInsets] = useState({ top: 0, right: 0, bottom: 0, left: 0 });
|
|
445
|
+
useEffect(() => {
|
|
446
|
+
if (typeof window === "undefined") return;
|
|
447
|
+
const el = document.createElement("div");
|
|
448
|
+
el.style.cssText = [
|
|
449
|
+
"position:fixed",
|
|
450
|
+
"top:env(safe-area-inset-top,0px)",
|
|
451
|
+
"right:env(safe-area-inset-right,0px)",
|
|
452
|
+
"bottom:env(safe-area-inset-bottom,0px)",
|
|
453
|
+
"left:env(safe-area-inset-left,0px)",
|
|
454
|
+
"pointer-events:none",
|
|
455
|
+
"visibility:hidden",
|
|
456
|
+
"z-index:-1"
|
|
457
|
+
].join(";");
|
|
458
|
+
document.body.appendChild(el);
|
|
459
|
+
function read() {
|
|
460
|
+
const style = getComputedStyle(el);
|
|
461
|
+
setInsets({
|
|
462
|
+
top: parseFloat(style.top) || 0,
|
|
463
|
+
right: parseFloat(style.right) || 0,
|
|
464
|
+
bottom: parseFloat(style.bottom) || 0,
|
|
465
|
+
left: parseFloat(style.left) || 0
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
read();
|
|
469
|
+
const observer = new ResizeObserver(read);
|
|
470
|
+
observer.observe(el);
|
|
471
|
+
window.addEventListener("resize", read);
|
|
472
|
+
return () => {
|
|
473
|
+
observer.disconnect();
|
|
474
|
+
window.removeEventListener("resize", read);
|
|
475
|
+
el.remove();
|
|
476
|
+
};
|
|
477
|
+
}, []);
|
|
478
|
+
return insets;
|
|
479
|
+
}
|
|
480
|
+
function useKeyboard() {
|
|
481
|
+
const [state, setState] = useState({ isOpen: false, height: 0 });
|
|
482
|
+
const timeoutRef = useRef();
|
|
483
|
+
useEffect(() => {
|
|
484
|
+
if (typeof window === "undefined") return;
|
|
485
|
+
if ("virtualKeyboard" in navigator) {
|
|
486
|
+
const vk = navigator.virtualKeyboard;
|
|
487
|
+
vk.overlaysContent = true;
|
|
488
|
+
const handler = () => {
|
|
489
|
+
const h = vk.boundingRect.height;
|
|
490
|
+
setState((prev) => {
|
|
491
|
+
if (prev.height === h && prev.isOpen === h > 0) return prev;
|
|
492
|
+
return { isOpen: h > 0, height: h };
|
|
493
|
+
});
|
|
494
|
+
};
|
|
495
|
+
vk.addEventListener("geometrychange", handler);
|
|
496
|
+
handler();
|
|
497
|
+
return () => vk.removeEventListener("geometrychange", handler);
|
|
498
|
+
}
|
|
499
|
+
const vv = window.visualViewport;
|
|
500
|
+
if (!vv) return;
|
|
501
|
+
let layoutHeight = window.innerHeight;
|
|
502
|
+
function compute() {
|
|
503
|
+
const kbHeight = Math.max(0, layoutHeight - vv.height);
|
|
504
|
+
const h = kbHeight > 40 ? Math.round(kbHeight) : 0;
|
|
505
|
+
setState((prev) => {
|
|
506
|
+
if (prev.height === h && prev.isOpen === h > 0) return prev;
|
|
507
|
+
return { isOpen: h > 0, height: h };
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
function onViewportResize() {
|
|
511
|
+
compute();
|
|
512
|
+
clearTimeout(timeoutRef.current);
|
|
513
|
+
timeoutRef.current = setTimeout(compute, 1e3);
|
|
514
|
+
}
|
|
515
|
+
function onWindowResize() {
|
|
516
|
+
layoutHeight = window.innerHeight;
|
|
517
|
+
compute();
|
|
518
|
+
}
|
|
519
|
+
vv.addEventListener("resize", onViewportResize);
|
|
520
|
+
window.addEventListener("resize", onWindowResize);
|
|
521
|
+
window.addEventListener("orientationchange", onWindowResize);
|
|
522
|
+
compute();
|
|
523
|
+
return () => {
|
|
524
|
+
clearTimeout(timeoutRef.current);
|
|
525
|
+
vv.removeEventListener("resize", onViewportResize);
|
|
526
|
+
window.removeEventListener("resize", onWindowResize);
|
|
527
|
+
window.removeEventListener("orientationchange", onWindowResize);
|
|
528
|
+
};
|
|
529
|
+
}, []);
|
|
530
|
+
return state;
|
|
531
|
+
}
|
|
424
532
|
var PAGE_KEYS = [
|
|
425
533
|
"page.currentId",
|
|
426
534
|
"page.currentIndex",
|
|
@@ -807,6 +915,6 @@ function PaddleCheckout({
|
|
|
807
915
|
return null;
|
|
808
916
|
}
|
|
809
917
|
|
|
810
|
-
export { PaddleCheckout, StripePaymentForm, defineConfig, definePage, useData, useDeviceInfo, useFunnel,
|
|
918
|
+
export { PaddleCheckout, StripePaymentForm, defineConfig, definePage, useData, useDateOfBirth, useDeviceInfo, useFunnel, useKeyboard, useLocale, usePageData, usePayment, useProducts, useQueryParam, useQueryParams, useSafeArea, useTracking, useTranslation, useUser, useUserProperty, useVariable, useVariables };
|
|
811
919
|
//# sourceMappingURL=index.js.map
|
|
812
920
|
//# sourceMappingURL=index.js.map
|