@usefillo/react 0.16.1 → 0.17.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/index.d.ts +13 -1
- package/dist/index.js +145 -21
- package/dist/styles.css +9 -0
- package/dist/styles.unlayered.css +9 -0
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ComponentType, ReactNode, ReactElement } from 'react';
|
|
3
3
|
import * as _usefillo_core from '@usefillo/core';
|
|
4
|
-
import { FormSchema, FilloClient, ResponseData, FieldValue, FormPage, Block, FormStatus, FieldKind, Field, CustomField, FormTheme, FilloAppearance, FilloRendererStrings, FilloRespondent, ChallengeConfig, FilloError, CodeForm, FormSettings, Condition, TextField, PhoneField, NumberField, ChoiceField, SelectOption, CheckboxField, RatingField, LinearScaleField, RankingField, SignatureField, DateField, FileUploadField as FileUploadField$1, HiddenField, CalculatedField } from '@usefillo/core';
|
|
4
|
+
import { FormSchema, FilloClient, ResponseData, FieldValue, FormPage, Block, FormStatus, FieldKind, Field, CustomField, FormTheme, FilloAppearance, FilloRendererStrings, FilloRespondent, ChallengeConfig, ChallengeTheme, FilloError, CodeForm, FormSettings, Condition, TextField, PhoneField, NumberField, ChoiceField, SelectOption, CheckboxField, RatingField, LinearScaleField, RankingField, SignatureField, DateField, FileUploadField as FileUploadField$1, HiddenField, CalculatedField } from '@usefillo/core';
|
|
5
5
|
export { CodeForm, Field, FieldValue, FileValue, FilloAppearance, FilloClient, FilloError, FilloJsxError, FilloRendererStrings, FilloSlot, FilloStrings, FormSchema, FormStatus, FormTheme, ProvisionWorkspaceResult, PublishedForm, ResponseData, SlotState, createClient, defineForm, provisionWorkspace, when } from '@usefillo/core';
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -143,6 +143,18 @@ interface FilloFormBaseProps {
|
|
|
143
143
|
* hosted page does this). The SECRET key stays server-side — never passed here.
|
|
144
144
|
*/
|
|
145
145
|
challenge?: ChallengeConfig;
|
|
146
|
+
/**
|
|
147
|
+
* Theme for the human-verification widget. Defaults to "auto" (the
|
|
148
|
+
* visitor's OS preference); apps with their own theme switch should pass
|
|
149
|
+
* "light"/"dark" so the widget matches the surrounding form.
|
|
150
|
+
*/
|
|
151
|
+
challengeTheme?: ChallengeTheme;
|
|
152
|
+
/**
|
|
153
|
+
* Bridge-mode visibility for the human check. "interaction-only" (default)
|
|
154
|
+
* keeps it invisible unless Cloudflare needs the visitor to act; "always"
|
|
155
|
+
* shows the classic widget box the whole time.
|
|
156
|
+
*/
|
|
157
|
+
challengeAppearance?: "always" | "interaction-only";
|
|
146
158
|
/**
|
|
147
159
|
* Server-owned per-file ceiling when an inline schema skips the normal form
|
|
148
160
|
* fetch. Fillo's hosted page uses this to preserve its server-rendered fast
|
package/dist/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
useId as useId2,
|
|
8
8
|
useMemo as useMemo6,
|
|
9
9
|
useRef as useRef9,
|
|
10
|
-
useState as
|
|
10
|
+
useState as useState7,
|
|
11
11
|
useSyncExternalStore as useSyncExternalStore2
|
|
12
12
|
} from "react";
|
|
13
13
|
import {
|
|
@@ -2500,10 +2500,12 @@ function FormField({
|
|
|
2500
2500
|
}
|
|
2501
2501
|
|
|
2502
2502
|
// src/turnstile.tsx
|
|
2503
|
-
import { useEffect as useEffect8, useRef as useRef8 } from "react";
|
|
2503
|
+
import { useEffect as useEffect8, useRef as useRef8, useState as useState6 } from "react";
|
|
2504
2504
|
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
2505
2505
|
var SCRIPT_SRC = "https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit";
|
|
2506
2506
|
var SCRIPT_ID = "fillo-turnstile-script";
|
|
2507
|
+
var BRIDGE_READY_TIMEOUT_MS = 2e4;
|
|
2508
|
+
var CDATA_RE = /^[A-Za-z0-9_-]{1,255}$/;
|
|
2507
2509
|
var scriptPromise = null;
|
|
2508
2510
|
function turnstileGlobal() {
|
|
2509
2511
|
return globalThis.turnstile;
|
|
@@ -2552,8 +2554,133 @@ function loadTurnstileScript() {
|
|
|
2552
2554
|
});
|
|
2553
2555
|
return scriptPromise;
|
|
2554
2556
|
}
|
|
2555
|
-
function TurnstileWidget({
|
|
2557
|
+
function TurnstileWidget(props) {
|
|
2558
|
+
return props.bridgeUrl ? /* @__PURE__ */ jsx7(BridgeChallenge, { ...props, bridgeUrl: props.bridgeUrl }) : /* @__PURE__ */ jsx7(DirectChallenge, { ...props });
|
|
2559
|
+
}
|
|
2560
|
+
function BridgeChallenge({
|
|
2561
|
+
bridgeUrl,
|
|
2562
|
+
formId,
|
|
2563
|
+
theme,
|
|
2564
|
+
appearance,
|
|
2565
|
+
onToken,
|
|
2566
|
+
onError,
|
|
2567
|
+
registerReset,
|
|
2568
|
+
className
|
|
2569
|
+
}) {
|
|
2570
|
+
const iframeRef = useRef8(null);
|
|
2571
|
+
const interactionOnly = (appearance ?? "interaction-only") === "interaction-only";
|
|
2572
|
+
const [frame, setFrame] = useState6(null);
|
|
2573
|
+
const [expanded, setExpanded] = useState6(!interactionOnly);
|
|
2574
|
+
const onTokenRef = useRef8(onToken);
|
|
2575
|
+
onTokenRef.current = onToken;
|
|
2576
|
+
const onErrorRef = useRef8(onError);
|
|
2577
|
+
onErrorRef.current = onError;
|
|
2578
|
+
const registerResetRef = useRef8(registerReset);
|
|
2579
|
+
registerResetRef.current = registerReset;
|
|
2580
|
+
useEffect8(() => {
|
|
2581
|
+
if (typeof window === "undefined") return;
|
|
2582
|
+
let url;
|
|
2583
|
+
try {
|
|
2584
|
+
url = new URL(bridgeUrl);
|
|
2585
|
+
} catch {
|
|
2586
|
+
onErrorRef.current?.();
|
|
2587
|
+
return;
|
|
2588
|
+
}
|
|
2589
|
+
if (url.protocol !== "https:" && url.protocol !== "http:") {
|
|
2590
|
+
onErrorRef.current?.();
|
|
2591
|
+
return;
|
|
2592
|
+
}
|
|
2593
|
+
url.searchParams.set("origin", window.location.origin);
|
|
2594
|
+
url.searchParams.set("theme", theme ?? "auto");
|
|
2595
|
+
url.searchParams.set("appearance", interactionOnly ? "interaction-only" : "always");
|
|
2596
|
+
if (formId && CDATA_RE.test(formId)) url.searchParams.set("cdata", formId);
|
|
2597
|
+
setFrame({ src: url.toString(), origin: url.origin });
|
|
2598
|
+
}, [bridgeUrl, formId, theme, interactionOnly]);
|
|
2599
|
+
useEffect8(() => {
|
|
2600
|
+
if (!frame || typeof window === "undefined") return;
|
|
2601
|
+
let settled = false;
|
|
2602
|
+
const watchdog = window.setTimeout(() => {
|
|
2603
|
+
if (!settled) {
|
|
2604
|
+
onTokenRef.current(void 0);
|
|
2605
|
+
onErrorRef.current?.();
|
|
2606
|
+
}
|
|
2607
|
+
}, BRIDGE_READY_TIMEOUT_MS);
|
|
2608
|
+
const settle = () => {
|
|
2609
|
+
settled = true;
|
|
2610
|
+
window.clearTimeout(watchdog);
|
|
2611
|
+
};
|
|
2612
|
+
const onMessage = (event) => {
|
|
2613
|
+
if (event.origin !== frame.origin) return;
|
|
2614
|
+
const target = iframeRef.current;
|
|
2615
|
+
if (!target || event.source !== target.contentWindow) return;
|
|
2616
|
+
const data = event.data;
|
|
2617
|
+
switch (data?.type) {
|
|
2618
|
+
case "fillo:challenge:ready":
|
|
2619
|
+
settle();
|
|
2620
|
+
break;
|
|
2621
|
+
case "fillo:challenge:token":
|
|
2622
|
+
settle();
|
|
2623
|
+
if (typeof data.token === "string" && data.token.length > 0) {
|
|
2624
|
+
onTokenRef.current(data.token);
|
|
2625
|
+
}
|
|
2626
|
+
if (interactionOnly) setExpanded(false);
|
|
2627
|
+
break;
|
|
2628
|
+
case "fillo:challenge:interactive":
|
|
2629
|
+
if (interactionOnly) setExpanded(true);
|
|
2630
|
+
break;
|
|
2631
|
+
case "fillo:challenge:interactive-done":
|
|
2632
|
+
if (interactionOnly) setExpanded(false);
|
|
2633
|
+
break;
|
|
2634
|
+
case "fillo:challenge:expired":
|
|
2635
|
+
onTokenRef.current(void 0);
|
|
2636
|
+
break;
|
|
2637
|
+
case "fillo:challenge:error":
|
|
2638
|
+
settle();
|
|
2639
|
+
onTokenRef.current(void 0);
|
|
2640
|
+
onErrorRef.current?.();
|
|
2641
|
+
break;
|
|
2642
|
+
}
|
|
2643
|
+
};
|
|
2644
|
+
window.addEventListener("message", onMessage);
|
|
2645
|
+
registerResetRef.current?.(() => {
|
|
2646
|
+
onTokenRef.current(void 0);
|
|
2647
|
+
iframeRef.current?.contentWindow?.postMessage(
|
|
2648
|
+
{ type: "fillo:challenge:reset" },
|
|
2649
|
+
frame.origin
|
|
2650
|
+
);
|
|
2651
|
+
});
|
|
2652
|
+
return () => {
|
|
2653
|
+
window.clearTimeout(watchdog);
|
|
2654
|
+
window.removeEventListener("message", onMessage);
|
|
2655
|
+
onTokenRef.current(void 0);
|
|
2656
|
+
};
|
|
2657
|
+
}, [frame, interactionOnly]);
|
|
2658
|
+
return /* @__PURE__ */ jsx7(
|
|
2659
|
+
"div",
|
|
2660
|
+
{
|
|
2661
|
+
className,
|
|
2662
|
+
"data-fillo": "turnstile",
|
|
2663
|
+
"data-fillo-challenge-visible": expanded ? "true" : "false",
|
|
2664
|
+
children: frame && /* @__PURE__ */ jsx7(
|
|
2665
|
+
"iframe",
|
|
2666
|
+
{
|
|
2667
|
+
ref: iframeRef,
|
|
2668
|
+
src: frame.src,
|
|
2669
|
+
title: "Human verification",
|
|
2670
|
+
width: 300,
|
|
2671
|
+
height: expanded ? 65 : 0,
|
|
2672
|
+
className: "fillo-turnstile-frame",
|
|
2673
|
+
referrerPolicy: "no-referrer",
|
|
2674
|
+
"aria-hidden": expanded ? void 0 : true,
|
|
2675
|
+
tabIndex: expanded ? void 0 : -1
|
|
2676
|
+
}
|
|
2677
|
+
)
|
|
2678
|
+
}
|
|
2679
|
+
);
|
|
2680
|
+
}
|
|
2681
|
+
function DirectChallenge({
|
|
2556
2682
|
siteKey,
|
|
2683
|
+
theme,
|
|
2557
2684
|
onToken,
|
|
2558
2685
|
onError,
|
|
2559
2686
|
registerReset,
|
|
@@ -2582,7 +2709,7 @@ function TurnstileWidget({
|
|
|
2582
2709
|
};
|
|
2583
2710
|
widgetIdRef.current = api.render(containerRef.current, {
|
|
2584
2711
|
sitekey: siteKey,
|
|
2585
|
-
theme: "auto",
|
|
2712
|
+
theme: theme ?? "auto",
|
|
2586
2713
|
callback: (token) => onTokenRef.current(token),
|
|
2587
2714
|
"error-callback": () => {
|
|
2588
2715
|
onTokenRef.current(void 0);
|
|
@@ -2607,15 +2734,8 @@ function TurnstileWidget({
|
|
|
2607
2734
|
widgetIdRef.current = null;
|
|
2608
2735
|
onTokenRef.current(void 0);
|
|
2609
2736
|
};
|
|
2610
|
-
}, [siteKey]);
|
|
2611
|
-
return /* @__PURE__ */ jsx7(
|
|
2612
|
-
"div",
|
|
2613
|
-
{
|
|
2614
|
-
ref: containerRef,
|
|
2615
|
-
className,
|
|
2616
|
-
"data-fillo": "turnstile"
|
|
2617
|
-
}
|
|
2618
|
-
);
|
|
2737
|
+
}, [siteKey, theme]);
|
|
2738
|
+
return /* @__PURE__ */ jsx7("div", { ref: containerRef, className, "data-fillo": "turnstile" });
|
|
2619
2739
|
}
|
|
2620
2740
|
|
|
2621
2741
|
// src/FilloForm.tsx
|
|
@@ -2680,8 +2800,8 @@ function FilloForm(props) {
|
|
|
2680
2800
|
() => renderOnly ? void 0 : props.client ?? (formId ? createClient() : void 0),
|
|
2681
2801
|
[renderOnly, props.client, formId]
|
|
2682
2802
|
);
|
|
2683
|
-
const [loaded, setLoaded] =
|
|
2684
|
-
const [loadFailure, setLoadFailure] =
|
|
2803
|
+
const [loaded, setLoaded] = useState7(null);
|
|
2804
|
+
const [loadFailure, setLoadFailure] = useState7(
|
|
2685
2805
|
null
|
|
2686
2806
|
);
|
|
2687
2807
|
const devEnv = useIsDevEnv();
|
|
@@ -2996,10 +3116,10 @@ function ResolvedForm(props) {
|
|
|
2996
3116
|
const challenge = renderOnly ? void 0 : props.challenge;
|
|
2997
3117
|
const challengeRequired = Boolean(challenge);
|
|
2998
3118
|
const challengeTokenRef = useRef9(void 0);
|
|
2999
|
-
const [resyncWarning, setResyncWarning] =
|
|
3000
|
-
const [resolutionFailureCode, setResolutionFailureCode] =
|
|
3001
|
-
const [challengeToken, setChallengeToken] =
|
|
3002
|
-
const [challengeError, setChallengeError] =
|
|
3119
|
+
const [resyncWarning, setResyncWarning] = useState7(null);
|
|
3120
|
+
const [resolutionFailureCode, setResolutionFailureCode] = useState7(null);
|
|
3121
|
+
const [challengeToken, setChallengeToken] = useState7(void 0);
|
|
3122
|
+
const [challengeError, setChallengeError] = useState7(false);
|
|
3003
3123
|
const resetChallengeRef = useRef9(void 0);
|
|
3004
3124
|
const handleChallengeToken = useCallback2((token) => {
|
|
3005
3125
|
challengeTokenRef.current = token;
|
|
@@ -3364,6 +3484,10 @@ function ResolvedForm(props) {
|
|
|
3364
3484
|
TurnstileWidget,
|
|
3365
3485
|
{
|
|
3366
3486
|
siteKey: challenge.siteKey,
|
|
3487
|
+
bridgeUrl: challenge.bridgeUrl,
|
|
3488
|
+
formId: props.formId,
|
|
3489
|
+
theme: props.challengeTheme,
|
|
3490
|
+
appearance: props.challengeAppearance,
|
|
3367
3491
|
className: slotClass3("fillo-turnstile", appearance, { slot: "turnstile" }),
|
|
3368
3492
|
onToken: handleChallengeToken,
|
|
3369
3493
|
onError: () => setChallengeError(true),
|
|
@@ -3531,7 +3655,7 @@ var Fillo = {
|
|
|
3531
3655
|
};
|
|
3532
3656
|
|
|
3533
3657
|
// src/provider.tsx
|
|
3534
|
-
import { useEffect as useEffect10, useId as useId3, useMemo as useMemo7, useState as
|
|
3658
|
+
import { useEffect as useEffect10, useId as useId3, useMemo as useMemo7, useState as useState8 } from "react";
|
|
3535
3659
|
import {
|
|
3536
3660
|
FilloError as FilloError3,
|
|
3537
3661
|
createClient as createClient2,
|
|
@@ -3594,7 +3718,7 @@ function FilloProvider({
|
|
|
3594
3718
|
} = useCodeFormSync(form, activeClient, onError, {
|
|
3595
3719
|
allowUnsynced: Boolean(!activeClient || activeFormId || localPreview)
|
|
3596
3720
|
});
|
|
3597
|
-
const [resyncWarning, setResyncWarning] =
|
|
3721
|
+
const [resyncWarning, setResyncWarning] = useState8(null);
|
|
3598
3722
|
const rawSchema = codeForm ? resolvedSchema && !devChrome ? resolvedSchema : codeForm.schema : form;
|
|
3599
3723
|
const normalized = useMemo7(() => normalizeFormSchema2(rawSchema), [rawSchema]);
|
|
3600
3724
|
if (!normalized.ok) {
|
package/dist/styles.css
CHANGED
|
@@ -994,6 +994,15 @@
|
|
|
994
994
|
.fillo-turnstile {
|
|
995
995
|
min-height: 65px;
|
|
996
996
|
}
|
|
997
|
+
/* Bridge mode: the widget lives in Fillo's iframe — keep it flush. */
|
|
998
|
+
.fillo-turnstile-frame {
|
|
999
|
+
display: block;
|
|
1000
|
+
border: 0;
|
|
1001
|
+
}
|
|
1002
|
+
/* interaction-only: while the check runs invisibly, reserve no space. */
|
|
1003
|
+
.fillo-turnstile[data-fillo-challenge-visible="false"] {
|
|
1004
|
+
min-height: 0;
|
|
1005
|
+
}
|
|
997
1006
|
.fillo-turnstile-error {
|
|
998
1007
|
margin: 0;
|
|
999
1008
|
color: var(--fillo-error);
|
|
@@ -994,6 +994,15 @@
|
|
|
994
994
|
.fillo-turnstile {
|
|
995
995
|
min-height: 65px;
|
|
996
996
|
}
|
|
997
|
+
/* Bridge mode: the widget lives in Fillo's iframe — keep it flush. */
|
|
998
|
+
.fillo-turnstile-frame {
|
|
999
|
+
display: block;
|
|
1000
|
+
border: 0;
|
|
1001
|
+
}
|
|
1002
|
+
/* interaction-only: while the check runs invisibly, reserve no space. */
|
|
1003
|
+
.fillo-turnstile[data-fillo-challenge-visible="false"] {
|
|
1004
|
+
min-height: 0;
|
|
1005
|
+
}
|
|
997
1006
|
.fillo-turnstile-error {
|
|
998
1007
|
margin: 0;
|
|
999
1008
|
color: var(--fillo-error);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@usefillo/react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"description": "Headless React components for embedding Fillo forms natively in your product.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"access": "public"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@usefillo/core": "^0.
|
|
46
|
+
"@usefillo/core": "^0.17.0"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
49
|
"react": "^18.0.0 || ^19.0.0",
|