@usefillo/react 0.9.0 → 0.10.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/README.md +25 -18
- package/dist/index.d.ts +32 -1
- package/dist/index.js +674 -382
- package/dist/styles.css +55 -0
- package/dist/styles.unlayered.css +55 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
// src/FilloForm.tsx
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
useCallback,
|
|
6
|
+
useEffect as useEffect8,
|
|
7
|
+
useId as useId2,
|
|
8
|
+
useMemo as useMemo5,
|
|
9
|
+
useRef as useRef7,
|
|
10
|
+
useState as useState6,
|
|
11
|
+
useSyncExternalStore as useSyncExternalStore2
|
|
12
|
+
} from "react";
|
|
5
13
|
import {
|
|
6
14
|
FilloError as FilloError2,
|
|
7
15
|
createClient,
|
|
8
16
|
formSchemasEqual,
|
|
17
|
+
isBuildTimeDevEnv as isBuildTimeDevEnv2,
|
|
9
18
|
isFilloError as isFilloError2,
|
|
19
|
+
isLikelyDevEnv,
|
|
10
20
|
needsExplicitSubmit,
|
|
11
21
|
normalizeFormSchema,
|
|
12
22
|
normalizeFormTheme,
|
|
@@ -61,11 +71,90 @@ function useStrings() {
|
|
|
61
71
|
return useContext(FilloStringsContext);
|
|
62
72
|
}
|
|
63
73
|
|
|
74
|
+
// src/chrome.tsx
|
|
75
|
+
import { isBuildTimeDevEnv } from "@usefillo/core";
|
|
76
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
77
|
+
function safeHttpUrl(href) {
|
|
78
|
+
try {
|
|
79
|
+
if (!href) return null;
|
|
80
|
+
const url = new URL(href);
|
|
81
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") return null;
|
|
82
|
+
return url.toString();
|
|
83
|
+
} catch {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
var warnedPreviewInProduction = false;
|
|
88
|
+
function warnPreviewInProduction() {
|
|
89
|
+
if (warnedPreviewInProduction || isBuildTimeDevEnv()) return;
|
|
90
|
+
warnedPreviewInProduction = true;
|
|
91
|
+
console.warn(
|
|
92
|
+
"[fillo] `preview` is enabled in a production build. Preview is cosmetic only \u2014 it shows developer chrome and never changes where submissions go or whether they are accepted \u2014 but remove it before respondents see this page."
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
function PreviewBadge() {
|
|
96
|
+
return /* @__PURE__ */ jsx("span", { className: "fillo-preview-badge", "data-fillo": "preview-badge", children: "Preview" });
|
|
97
|
+
}
|
|
98
|
+
function DevNoClientNotice() {
|
|
99
|
+
return /* @__PURE__ */ jsxs("div", { className: "fillo-devwarning", role: "alert", children: [
|
|
100
|
+
/* @__PURE__ */ jsx("strong", { children: "No Fillo client connected." }),
|
|
101
|
+
" This form is render-only and won\u2019t save responses. Pass a ",
|
|
102
|
+
/* @__PURE__ */ jsx("code", { children: "client" }),
|
|
103
|
+
" to collect them in Fillo, or forward them with webhooks. (This notice only appears in development and preview.)"
|
|
104
|
+
] });
|
|
105
|
+
}
|
|
106
|
+
function DevDraftNotice({ warningUrl }) {
|
|
107
|
+
const safeUrl = safeHttpUrl(warningUrl);
|
|
108
|
+
return /* @__PURE__ */ jsxs("div", { className: "fillo-devwarning", role: "alert", children: [
|
|
109
|
+
/* @__PURE__ */ jsx("strong", { children: "Draft form." }),
|
|
110
|
+
" It renders here, but it won\u2019t accept responses until you publish it in your Fillo dashboard.",
|
|
111
|
+
" ",
|
|
112
|
+
safeUrl && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
113
|
+
"Connect storage to publish:",
|
|
114
|
+
" ",
|
|
115
|
+
/* @__PURE__ */ jsx("a", { href: safeUrl, target: "_blank", rel: "noopener noreferrer", children: safeUrl }),
|
|
116
|
+
".",
|
|
117
|
+
" "
|
|
118
|
+
] }),
|
|
119
|
+
"(This notice only appears in development and preview.)"
|
|
120
|
+
] });
|
|
121
|
+
}
|
|
122
|
+
function DevStagedNotice() {
|
|
123
|
+
return /* @__PURE__ */ jsxs("div", { className: "fillo-devwarning", role: "alert", children: [
|
|
124
|
+
/* @__PURE__ */ jsx("strong", { children: "Code changes are staged." }),
|
|
125
|
+
" This local preview is not the live form yet. Publish the changes in Fillo before deploying them for respondents."
|
|
126
|
+
] });
|
|
127
|
+
}
|
|
128
|
+
function DevSyncErrorNotice({ error }) {
|
|
129
|
+
return /* @__PURE__ */ jsxs("div", { className: "fillo-devwarning", role: "alert", children: [
|
|
130
|
+
/* @__PURE__ */ jsxs("strong", { children: [
|
|
131
|
+
"Form sync needs attention",
|
|
132
|
+
error.code ? ` (${error.code})` : "",
|
|
133
|
+
"."
|
|
134
|
+
] }),
|
|
135
|
+
" ",
|
|
136
|
+
error.message,
|
|
137
|
+
" The safe form remains available for development."
|
|
138
|
+
] });
|
|
139
|
+
}
|
|
140
|
+
function DevChrome(props) {
|
|
141
|
+
const notice = props.devNotices === false ? null : props.syncError ? /* @__PURE__ */ jsx(DevSyncErrorNotice, { error: props.syncError }) : props.staged ? /* @__PURE__ */ jsx(DevStagedNotice, {}) : props.draft ? /* @__PURE__ */ jsx(DevDraftNotice, { warningUrl: props.warningUrl }) : props.noClient ? /* @__PURE__ */ jsx(DevNoClientNotice, {}) : null;
|
|
142
|
+
if (!props.preview && !notice) return null;
|
|
143
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
144
|
+
props.preview && /* @__PURE__ */ jsx(PreviewBadge, {}),
|
|
145
|
+
notice
|
|
146
|
+
] });
|
|
147
|
+
}
|
|
148
|
+
|
|
64
149
|
// src/context.ts
|
|
65
150
|
import { allFields } from "@usefillo/core";
|
|
66
151
|
import { createContext as createContext2, useContext as useContext2, useId, useMemo } from "react";
|
|
67
152
|
var FilloContext = createContext2(null);
|
|
68
153
|
var FilloInstanceIdContext = createContext2(null);
|
|
154
|
+
var FilloChromeContext = createContext2(null);
|
|
155
|
+
function useFilloChrome() {
|
|
156
|
+
return useContext2(FilloChromeContext);
|
|
157
|
+
}
|
|
69
158
|
function useFilloInstanceId() {
|
|
70
159
|
const instanceId = useContext2(FilloInstanceIdContext);
|
|
71
160
|
const fallbackId = useId().replace(/:/g, "");
|
|
@@ -128,6 +217,12 @@ function useFilloController(options) {
|
|
|
128
217
|
surface: options.surface ?? "headless",
|
|
129
218
|
// Wrapped so the engine (created once) always calls the latest resolver.
|
|
130
219
|
resolveFormId: options.resolveFormId ? () => optsRef.current.resolveFormId() : void 0,
|
|
220
|
+
// A getter, not a captured boolean: the dev-chrome gate upgrades right
|
|
221
|
+
// after hydration (build-time snapshot → hostname-aware check), but the
|
|
222
|
+
// engine is created once at mount and reads this at submit time.
|
|
223
|
+
get verboseResolutionErrors() {
|
|
224
|
+
return optsRef.current.verboseResolutionErrors;
|
|
225
|
+
},
|
|
131
226
|
respondent: options.respondent,
|
|
132
227
|
// Challenge wiring read through optsRef so the engine (created once) sees
|
|
133
228
|
// the latest token/reset each submit — the widget solves after mount.
|
|
@@ -164,11 +259,11 @@ function useFilloController(options) {
|
|
|
164
259
|
[store, state, options.form, options.formId, options.client]
|
|
165
260
|
);
|
|
166
261
|
}
|
|
167
|
-
function useDraftFlush(api) {
|
|
168
|
-
const
|
|
262
|
+
function useDraftFlush(api, enabled = true) {
|
|
263
|
+
const active = enabled && Boolean(api.form.settings.saveProgress);
|
|
169
264
|
const flush = api.flushDraft;
|
|
170
265
|
useEffect(() => {
|
|
171
|
-
if (!
|
|
266
|
+
if (!active || typeof window === "undefined") return;
|
|
172
267
|
const onPageHide = () => flush();
|
|
173
268
|
const onVisibility = () => {
|
|
174
269
|
if (document.visibilityState === "hidden") flush();
|
|
@@ -179,7 +274,7 @@ function useDraftFlush(api) {
|
|
|
179
274
|
window.removeEventListener("pagehide", onPageHide);
|
|
180
275
|
document.removeEventListener("visibilitychange", onVisibility);
|
|
181
276
|
};
|
|
182
|
-
}, [
|
|
277
|
+
}, [active, flush]);
|
|
183
278
|
}
|
|
184
279
|
|
|
185
280
|
// src/define.ts
|
|
@@ -230,8 +325,14 @@ function useCodeFormSync(form, client, onError, options) {
|
|
|
230
325
|
error: null,
|
|
231
326
|
noticeError: null,
|
|
232
327
|
staged: false,
|
|
328
|
+
accepting: void 0,
|
|
329
|
+
acceptingReason: void 0,
|
|
330
|
+
uploadsAvailable: void 0,
|
|
233
331
|
resolvedSchema: void 0,
|
|
234
|
-
resolvedTheme: void 0
|
|
332
|
+
resolvedTheme: void 0,
|
|
333
|
+
warning: void 0,
|
|
334
|
+
warningCode: void 0,
|
|
335
|
+
warningUrl: void 0
|
|
235
336
|
} : {
|
|
236
337
|
key: syncKey,
|
|
237
338
|
client,
|
|
@@ -286,11 +387,21 @@ function useCodeFormSync(form, client, onError, options) {
|
|
|
286
387
|
error: null,
|
|
287
388
|
noticeError,
|
|
288
389
|
staged: Boolean(r.staged),
|
|
390
|
+
accepting: r.accepting,
|
|
391
|
+
acceptingReason: r.acceptingReason,
|
|
392
|
+
uploadsAvailable: r.uploadsAvailable,
|
|
289
393
|
resolvedSchema: r.resolvedSchema,
|
|
290
|
-
resolvedTheme: r.resolvedTheme
|
|
394
|
+
resolvedTheme: r.resolvedTheme,
|
|
395
|
+
warning: r.warning,
|
|
396
|
+
warningCode: r.warningCode,
|
|
397
|
+
warningUrl: r.warningUrl
|
|
291
398
|
});
|
|
292
399
|
const dashboardUrl = `${client.baseUrl || "https://fillo.so"}/forms/${r.formId}`;
|
|
293
|
-
if (r.warning)
|
|
400
|
+
if (r.warning) {
|
|
401
|
+
console.warn(
|
|
402
|
+
`[fillo] ${r.warning}${r.warningUrl ? ` Connect storage to publish: ${r.warningUrl}` : ""}`
|
|
403
|
+
);
|
|
404
|
+
}
|
|
294
405
|
if (noticeError) {
|
|
295
406
|
console.warn(
|
|
296
407
|
`[fillo] form sync needs attention (${noticeError.code}): ${noticeError.message}`
|
|
@@ -337,8 +448,14 @@ function useCodeFormSync(form, client, onError, options) {
|
|
|
337
448
|
noticeError: synced.noticeError,
|
|
338
449
|
syncing: Boolean(syncKey) && !synced.settled,
|
|
339
450
|
staged: synced.staged,
|
|
451
|
+
accepting: synced.accepting,
|
|
452
|
+
acceptingReason: synced.acceptingReason,
|
|
453
|
+
uploadsAvailable: synced.uploadsAvailable,
|
|
340
454
|
resolvedSchema: synced.resolvedSchema,
|
|
341
|
-
resolvedTheme: synced.resolvedTheme
|
|
455
|
+
resolvedTheme: synced.resolvedTheme,
|
|
456
|
+
warning: synced.warning,
|
|
457
|
+
warningCode: synced.warningCode,
|
|
458
|
+
warningUrl: synced.warningUrl
|
|
342
459
|
} : {
|
|
343
460
|
formId: null,
|
|
344
461
|
status: null,
|
|
@@ -366,7 +483,7 @@ import {
|
|
|
366
483
|
toE164
|
|
367
484
|
} from "@usefillo/core";
|
|
368
485
|
import { useEffect as useEffect3, useLayoutEffect, useMemo as useMemo3, useRef as useRef3, useState as useState2 } from "react";
|
|
369
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
486
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
370
487
|
function localeCountry() {
|
|
371
488
|
if (typeof navigator === "undefined") return void 0;
|
|
372
489
|
const tag = navigator.languages?.[0] || navigator.language || "";
|
|
@@ -449,15 +566,15 @@ function PhoneField({ field, value, error, setValue, ids: providedIds }) {
|
|
|
449
566
|
inputRef.current?.focus();
|
|
450
567
|
}
|
|
451
568
|
const describedBy = [field.description ? ids.descriptionId : null, error ? ids.errorId : null].filter(Boolean).join(" ") || void 0;
|
|
452
|
-
return /* @__PURE__ */
|
|
453
|
-
/* @__PURE__ */
|
|
569
|
+
return /* @__PURE__ */ jsxs2("div", { ...slots.wrapperProps(`fillo-field fillo-field--phone${error ? " fillo-field--error" : ""}`), children: [
|
|
570
|
+
/* @__PURE__ */ jsxs2("label", { className: slots.label, "data-fillo": "label", id: ids.labelId, htmlFor: ids.inputId, children: [
|
|
454
571
|
field.label,
|
|
455
|
-
!field.required && /* @__PURE__ */
|
|
572
|
+
!field.required && /* @__PURE__ */ jsx2("span", { className: "fillo-optional", children: strings.optional })
|
|
456
573
|
] }),
|
|
457
|
-
field.description && /* @__PURE__ */
|
|
458
|
-
/* @__PURE__ */
|
|
459
|
-
/* @__PURE__ */
|
|
460
|
-
/* @__PURE__ */
|
|
574
|
+
field.description && /* @__PURE__ */ jsx2("p", { className: slots.description, "data-fillo": "fieldDescription", id: ids.descriptionId, children: field.description }),
|
|
575
|
+
/* @__PURE__ */ jsxs2("div", { className: "fillo-phone", children: [
|
|
576
|
+
/* @__PURE__ */ jsx2(CountrySelect, { country, onPick: pickCountry, labelledBy: ids.labelId }),
|
|
577
|
+
/* @__PURE__ */ jsx2(
|
|
461
578
|
"input",
|
|
462
579
|
{
|
|
463
580
|
ref: inputRef,
|
|
@@ -476,7 +593,7 @@ function PhoneField({ field, value, error, setValue, ids: providedIds }) {
|
|
|
476
593
|
}
|
|
477
594
|
)
|
|
478
595
|
] }),
|
|
479
|
-
error && /* @__PURE__ */
|
|
596
|
+
error && /* @__PURE__ */ jsx2("p", { className: slots.error, "data-fillo": "error", id: ids.errorId, role: "alert", children: error })
|
|
480
597
|
] });
|
|
481
598
|
}
|
|
482
599
|
function CountrySelect({
|
|
@@ -552,8 +669,8 @@ function CountrySelect({
|
|
|
552
669
|
triggerRef.current?.focus();
|
|
553
670
|
}
|
|
554
671
|
}
|
|
555
|
-
return /* @__PURE__ */
|
|
556
|
-
/* @__PURE__ */
|
|
672
|
+
return /* @__PURE__ */ jsxs2("div", { className: "fillo-phone-country", ref: rootRef, children: [
|
|
673
|
+
/* @__PURE__ */ jsxs2(
|
|
557
674
|
"button",
|
|
558
675
|
{
|
|
559
676
|
ref: triggerRef,
|
|
@@ -565,17 +682,17 @@ function CountrySelect({
|
|
|
565
682
|
"aria-label": `Country: ${country.name} (+${country.dialCode})`,
|
|
566
683
|
onClick: () => setOpen((o) => !o),
|
|
567
684
|
children: [
|
|
568
|
-
/* @__PURE__ */
|
|
569
|
-
/* @__PURE__ */
|
|
685
|
+
/* @__PURE__ */ jsx2("span", { className: "fillo-phone-flag-emoji", "aria-hidden": "true", children: flagEmoji(country.iso2) }),
|
|
686
|
+
/* @__PURE__ */ jsxs2("span", { className: "fillo-phone-dial", children: [
|
|
570
687
|
"+",
|
|
571
688
|
country.dialCode
|
|
572
689
|
] }),
|
|
573
|
-
/* @__PURE__ */
|
|
690
|
+
/* @__PURE__ */ jsx2("span", { className: "fillo-phone-caret", "aria-hidden": "true", children: "\u25BE" })
|
|
574
691
|
]
|
|
575
692
|
}
|
|
576
693
|
),
|
|
577
|
-
open && /* @__PURE__ */
|
|
578
|
-
/* @__PURE__ */
|
|
694
|
+
open && /* @__PURE__ */ jsxs2("div", { ref: popoverRef, className: `fillo-phone-popover fillo-phone-popover--${placement}`, children: [
|
|
695
|
+
/* @__PURE__ */ jsx2(
|
|
579
696
|
"input",
|
|
580
697
|
{
|
|
581
698
|
ref: searchRef,
|
|
@@ -596,8 +713,8 @@ function CountrySelect({
|
|
|
596
713
|
onKeyDown: onSearchKey
|
|
597
714
|
}
|
|
598
715
|
),
|
|
599
|
-
/* @__PURE__ */
|
|
600
|
-
matches.map((c, i) => /* @__PURE__ */
|
|
716
|
+
/* @__PURE__ */ jsxs2("ul", { className: "fillo-phone-list", role: "listbox", id: listId, "aria-label": "Country", children: [
|
|
717
|
+
matches.map((c, i) => /* @__PURE__ */ jsxs2(
|
|
601
718
|
"li",
|
|
602
719
|
{
|
|
603
720
|
id: `${listId}-opt-${i}`,
|
|
@@ -610,9 +727,9 @@ function CountrySelect({
|
|
|
610
727
|
choose(c);
|
|
611
728
|
},
|
|
612
729
|
children: [
|
|
613
|
-
/* @__PURE__ */
|
|
614
|
-
/* @__PURE__ */
|
|
615
|
-
/* @__PURE__ */
|
|
730
|
+
/* @__PURE__ */ jsx2("span", { className: "fillo-phone-flag-emoji", "aria-hidden": "true", children: flagEmoji(c.iso2) }),
|
|
731
|
+
/* @__PURE__ */ jsx2("span", { className: "fillo-phone-option-name", children: c.name }),
|
|
732
|
+
/* @__PURE__ */ jsxs2("span", { className: "fillo-phone-option-dial", children: [
|
|
616
733
|
"+",
|
|
617
734
|
c.dialCode
|
|
618
735
|
] })
|
|
@@ -620,7 +737,7 @@ function CountrySelect({
|
|
|
620
737
|
},
|
|
621
738
|
c.iso2
|
|
622
739
|
)),
|
|
623
|
-
matches.length === 0 && /* @__PURE__ */
|
|
740
|
+
matches.length === 0 && /* @__PURE__ */ jsx2("li", { className: "fillo-phone-empty", children: "No matches" })
|
|
624
741
|
] })
|
|
625
742
|
] })
|
|
626
743
|
] });
|
|
@@ -628,7 +745,7 @@ function CountrySelect({
|
|
|
628
745
|
|
|
629
746
|
// src/signature.tsx
|
|
630
747
|
import { useEffect as useEffect4, useRef as useRef4, useState as useState3 } from "react";
|
|
631
|
-
import { jsx as
|
|
748
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
632
749
|
function SignatureField({ field, value, error, setValue, ids: providedIds }) {
|
|
633
750
|
const ids = useFilloFieldIds(field.id, providedIds);
|
|
634
751
|
const slots = useFieldSlots(field, error);
|
|
@@ -733,21 +850,21 @@ function SignatureField({ field, value, error, setValue, ids: providedIds }) {
|
|
|
733
850
|
setValue(null);
|
|
734
851
|
}
|
|
735
852
|
const describedBy = [field.description ? ids.descriptionId : null, error ? ids.errorId : null].filter(Boolean).join(" ") || void 0;
|
|
736
|
-
return /* @__PURE__ */
|
|
737
|
-
/* @__PURE__ */
|
|
853
|
+
return /* @__PURE__ */ jsxs3("div", { ...slots.wrapperProps(`fillo-field fillo-field--signature${error ? " fillo-field--error" : ""}`), children: [
|
|
854
|
+
/* @__PURE__ */ jsxs3("span", { className: slots.label, "data-fillo": "label", id: ids.labelId, children: [
|
|
738
855
|
field.label,
|
|
739
|
-
!field.required && /* @__PURE__ */
|
|
856
|
+
!field.required && /* @__PURE__ */ jsx3("span", { className: "fillo-optional", children: strings.optional })
|
|
740
857
|
] }),
|
|
741
|
-
field.description && /* @__PURE__ */
|
|
742
|
-
/* @__PURE__ */
|
|
858
|
+
field.description && /* @__PURE__ */ jsx3("p", { className: slots.description, "data-fillo": "fieldDescription", id: ids.descriptionId, children: field.description }),
|
|
859
|
+
/* @__PURE__ */ jsxs3(
|
|
743
860
|
"div",
|
|
744
861
|
{
|
|
745
862
|
role: "group",
|
|
746
863
|
"aria-labelledby": ids.labelId,
|
|
747
864
|
"aria-describedby": describedBy,
|
|
748
865
|
children: [
|
|
749
|
-
/* @__PURE__ */
|
|
750
|
-
/* @__PURE__ */
|
|
866
|
+
/* @__PURE__ */ jsxs3("div", { className: slots.control("fillo-signature"), "data-fillo": "control", children: [
|
|
867
|
+
/* @__PURE__ */ jsx3(
|
|
751
868
|
"canvas",
|
|
752
869
|
{
|
|
753
870
|
ref: canvasRef,
|
|
@@ -760,12 +877,12 @@ function SignatureField({ field, value, error, setValue, ids: providedIds }) {
|
|
|
760
877
|
onPointerLeave: end
|
|
761
878
|
}
|
|
762
879
|
),
|
|
763
|
-
hasInk && /* @__PURE__ */
|
|
764
|
-
!hasInk && /* @__PURE__ */
|
|
880
|
+
hasInk && /* @__PURE__ */ jsx3("button", { type: "button", className: "fillo-signature-clear", onClick: clear, children: "Clear" }),
|
|
881
|
+
!hasInk && /* @__PURE__ */ jsx3("span", { className: "fillo-signature-hint", "aria-hidden": "true", children: "Sign here" })
|
|
765
882
|
] }),
|
|
766
|
-
/* @__PURE__ */
|
|
767
|
-
/* @__PURE__ */
|
|
768
|
-
/* @__PURE__ */
|
|
883
|
+
/* @__PURE__ */ jsxs3("div", { className: "fillo-signature-type", children: [
|
|
884
|
+
/* @__PURE__ */ jsx3("label", { className: "fillo-signature-type-label", htmlFor: typedInputId, children: "Or type your full name to sign" }),
|
|
885
|
+
/* @__PURE__ */ jsx3(
|
|
769
886
|
"input",
|
|
770
887
|
{
|
|
771
888
|
id: typedInputId,
|
|
@@ -787,13 +904,13 @@ function SignatureField({ field, value, error, setValue, ids: providedIds }) {
|
|
|
787
904
|
]
|
|
788
905
|
}
|
|
789
906
|
),
|
|
790
|
-
error && /* @__PURE__ */
|
|
907
|
+
error && /* @__PURE__ */ jsx3("p", { className: slots.error, "data-fillo": "error", id: ids.errorId, role: "alert", children: error })
|
|
791
908
|
] });
|
|
792
909
|
}
|
|
793
910
|
|
|
794
911
|
// src/upload.tsx
|
|
795
912
|
import { useEffect as useEffect5, useRef as useRef5, useState as useState4 } from "react";
|
|
796
|
-
import { Fragment, jsx as
|
|
913
|
+
import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
797
914
|
function uploadStatus(inFlight, doneCount, strings) {
|
|
798
915
|
const failed = inFlight.filter((f) => f.error).length;
|
|
799
916
|
const active = inFlight.length - failed;
|
|
@@ -813,6 +930,7 @@ function FileUploadField({ field, value, error, setValue, api, ids: providedIds
|
|
|
813
930
|
const ids = useFilloFieldIds(field.id, providedIds);
|
|
814
931
|
const slots = useFieldSlots(field, error);
|
|
815
932
|
const strings = useStrings();
|
|
933
|
+
const chrome = useFilloChrome();
|
|
816
934
|
const schema = field;
|
|
817
935
|
const inputRef = useRef5(null);
|
|
818
936
|
const [inFlight, setInFlight] = useState4([]);
|
|
@@ -836,7 +954,9 @@ function FileUploadField({ field, value, error, setValue, api, ids: providedIds
|
|
|
836
954
|
const maxFiles = schema.maxFiles ?? 1;
|
|
837
955
|
const maxMb = schema.maxFileSizeMb ?? 500;
|
|
838
956
|
const maxBytes = maxMb * 1024 * 1024;
|
|
839
|
-
const
|
|
957
|
+
const storageBlocked = chrome?.uploadsAvailable === false;
|
|
958
|
+
const storageUrl = storageBlocked && chrome?.devChrome ? safeHttpUrl(chrome.warningUrl) : null;
|
|
959
|
+
const canUpload = Boolean(api.client && api.formId) && !storageBlocked;
|
|
840
960
|
const activeInFlight = inFlight.filter((f) => !f.error).length;
|
|
841
961
|
const remaining = maxFiles - files.length - activeInFlight;
|
|
842
962
|
async function startUpload(file) {
|
|
@@ -898,13 +1018,13 @@ function FileUploadField({ field, value, error, setValue, api, ids: providedIds
|
|
|
898
1018
|
setValue(next);
|
|
899
1019
|
}
|
|
900
1020
|
const describedBy = [field.description ? ids.descriptionId : null, error ? ids.errorId : null].filter(Boolean).join(" ") || void 0;
|
|
901
|
-
return /* @__PURE__ */
|
|
902
|
-
/* @__PURE__ */
|
|
1021
|
+
return /* @__PURE__ */ jsxs4("div", { ...slots.wrapperProps(`fillo-field fillo-field--file_upload${error ? " fillo-field--error" : ""}`), children: [
|
|
1022
|
+
/* @__PURE__ */ jsxs4("label", { className: slots.label, "data-fillo": "label", id: ids.labelId, htmlFor: ids.inputId, children: [
|
|
903
1023
|
field.label,
|
|
904
|
-
!field.required && /* @__PURE__ */
|
|
1024
|
+
!field.required && /* @__PURE__ */ jsx4("span", { className: "fillo-optional", children: strings.optional })
|
|
905
1025
|
] }),
|
|
906
|
-
field.description && /* @__PURE__ */
|
|
907
|
-
remaining > 0 && /* @__PURE__ */
|
|
1026
|
+
field.description && /* @__PURE__ */ jsx4("p", { className: slots.description, "data-fillo": "fieldDescription", id: ids.descriptionId, children: field.description }),
|
|
1027
|
+
remaining > 0 && /* @__PURE__ */ jsxs4(
|
|
908
1028
|
"div",
|
|
909
1029
|
{
|
|
910
1030
|
"data-fillo": "control",
|
|
@@ -935,7 +1055,7 @@ function FileUploadField({ field, value, error, setValue, api, ids: providedIds
|
|
|
935
1055
|
}
|
|
936
1056
|
},
|
|
937
1057
|
children: [
|
|
938
|
-
/* @__PURE__ */
|
|
1058
|
+
/* @__PURE__ */ jsx4(
|
|
939
1059
|
"input",
|
|
940
1060
|
{
|
|
941
1061
|
ref: inputRef,
|
|
@@ -948,14 +1068,20 @@ function FileUploadField({ field, value, error, setValue, api, ids: providedIds
|
|
|
948
1068
|
onChange: (e) => canUpload && handleFiles(e.target.files)
|
|
949
1069
|
}
|
|
950
1070
|
),
|
|
951
|
-
canUpload ? /* @__PURE__ */
|
|
952
|
-
/* @__PURE__ */
|
|
953
|
-
/* @__PURE__ */
|
|
954
|
-
] }) : /* @__PURE__ */
|
|
1071
|
+
canUpload ? /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
1072
|
+
/* @__PURE__ */ jsx4("span", { className: "fillo-dropzone-title", children: strings.dropzoneTitle(maxFiles > 1) }),
|
|
1073
|
+
/* @__PURE__ */ jsx4("span", { className: "fillo-dropzone-hint", children: strings.dropzoneHint(maxMb) })
|
|
1074
|
+
] }) : storageBlocked && chrome?.devChrome ? /* @__PURE__ */ jsxs4("span", { className: "fillo-dropzone-hint", children: [
|
|
1075
|
+
"Connect file storage to enable uploads",
|
|
1076
|
+
storageUrl && /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
1077
|
+
" \u2014 ",
|
|
1078
|
+
/* @__PURE__ */ jsx4("a", { href: storageUrl, target: "_blank", rel: "noopener noreferrer", children: storageUrl })
|
|
1079
|
+
] })
|
|
1080
|
+
] }) : /* @__PURE__ */ jsx4("span", { className: "fillo-dropzone-hint", children: storageBlocked ? strings.uploadsUnavailable : strings.uploadsDisabled })
|
|
955
1081
|
]
|
|
956
1082
|
}
|
|
957
1083
|
),
|
|
958
|
-
/* @__PURE__ */
|
|
1084
|
+
/* @__PURE__ */ jsx4(
|
|
959
1085
|
"div",
|
|
960
1086
|
{
|
|
961
1087
|
role: "status",
|
|
@@ -965,11 +1091,11 @@ function FileUploadField({ field, value, error, setValue, api, ids: providedIds
|
|
|
965
1091
|
children: uploadStatus(inFlight, files.length, strings)
|
|
966
1092
|
}
|
|
967
1093
|
),
|
|
968
|
-
(inFlight.length > 0 || files.length > 0) && /* @__PURE__ */
|
|
969
|
-
files.map((f) => /* @__PURE__ */
|
|
970
|
-
/* @__PURE__ */
|
|
971
|
-
/* @__PURE__ */
|
|
972
|
-
/* @__PURE__ */
|
|
1094
|
+
(inFlight.length > 0 || files.length > 0) && /* @__PURE__ */ jsxs4("ul", { className: "fillo-files", children: [
|
|
1095
|
+
files.map((f) => /* @__PURE__ */ jsxs4("li", { className: "fillo-file fillo-file--done", children: [
|
|
1096
|
+
/* @__PURE__ */ jsx4("span", { className: "fillo-file-name", children: f.name }),
|
|
1097
|
+
/* @__PURE__ */ jsx4("span", { className: "fillo-file-meta", children: formatBytes(f.size) }),
|
|
1098
|
+
/* @__PURE__ */ jsx4(
|
|
973
1099
|
"button",
|
|
974
1100
|
{
|
|
975
1101
|
type: "button",
|
|
@@ -980,7 +1106,7 @@ function FileUploadField({ field, value, error, setValue, api, ids: providedIds
|
|
|
980
1106
|
}
|
|
981
1107
|
)
|
|
982
1108
|
] }, f.fileId)),
|
|
983
|
-
inFlight.map((f) => /* @__PURE__ */
|
|
1109
|
+
inFlight.map((f) => /* @__PURE__ */ jsxs4(
|
|
984
1110
|
"li",
|
|
985
1111
|
{
|
|
986
1112
|
className: `fillo-file${f.error ? " fillo-file--failed" : ""}`,
|
|
@@ -992,10 +1118,10 @@ function FileUploadField({ field, value, error, setValue, api, ids: providedIds
|
|
|
992
1118
|
"aria-valuenow": Math.round(f.fraction * 100)
|
|
993
1119
|
},
|
|
994
1120
|
children: [
|
|
995
|
-
/* @__PURE__ */
|
|
996
|
-
f.error ? /* @__PURE__ */
|
|
997
|
-
/* @__PURE__ */
|
|
998
|
-
f.file && !f.tooLarge && remaining > 0 && /* @__PURE__ */
|
|
1121
|
+
/* @__PURE__ */ jsx4("span", { className: "fillo-file-name", children: f.name }),
|
|
1122
|
+
f.error ? /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
1123
|
+
/* @__PURE__ */ jsx4("span", { className: "fillo-file-meta", children: f.error }),
|
|
1124
|
+
f.file && !f.tooLarge && remaining > 0 && /* @__PURE__ */ jsx4(
|
|
999
1125
|
"button",
|
|
1000
1126
|
{
|
|
1001
1127
|
type: "button",
|
|
@@ -1010,7 +1136,7 @@ function FileUploadField({ field, value, error, setValue, api, ids: providedIds
|
|
|
1010
1136
|
children: strings.uploadRetry
|
|
1011
1137
|
}
|
|
1012
1138
|
),
|
|
1013
|
-
/* @__PURE__ */
|
|
1139
|
+
/* @__PURE__ */ jsx4(
|
|
1014
1140
|
"button",
|
|
1015
1141
|
{
|
|
1016
1142
|
type: "button",
|
|
@@ -1020,14 +1146,14 @@ function FileUploadField({ field, value, error, setValue, api, ids: providedIds
|
|
|
1020
1146
|
children: "\xD7"
|
|
1021
1147
|
}
|
|
1022
1148
|
)
|
|
1023
|
-
] }) : /* @__PURE__ */
|
|
1024
|
-
/* @__PURE__ */
|
|
1149
|
+
] }) : /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
1150
|
+
/* @__PURE__ */ jsxs4("span", { className: "fillo-file-meta", children: [
|
|
1025
1151
|
Math.round(f.fraction * 100),
|
|
1026
1152
|
"% of ",
|
|
1027
1153
|
formatBytes(f.size)
|
|
1028
1154
|
] }),
|
|
1029
|
-
/* @__PURE__ */
|
|
1030
|
-
/* @__PURE__ */
|
|
1155
|
+
/* @__PURE__ */ jsx4("span", { className: "fillo-progress", "aria-hidden": "true", children: /* @__PURE__ */ jsx4("span", { className: "fillo-progress-bar", style: { width: `${f.fraction * 100}%` } }) }),
|
|
1156
|
+
/* @__PURE__ */ jsx4(
|
|
1031
1157
|
"button",
|
|
1032
1158
|
{
|
|
1033
1159
|
type: "button",
|
|
@@ -1043,12 +1169,12 @@ function FileUploadField({ field, value, error, setValue, api, ids: providedIds
|
|
|
1043
1169
|
f.key
|
|
1044
1170
|
))
|
|
1045
1171
|
] }),
|
|
1046
|
-
error && /* @__PURE__ */
|
|
1172
|
+
error && /* @__PURE__ */ jsx4("p", { className: slots.error, "data-fillo": "error", id: ids.errorId, role: "alert", children: error })
|
|
1047
1173
|
] });
|
|
1048
1174
|
}
|
|
1049
1175
|
|
|
1050
1176
|
// src/fields.tsx
|
|
1051
|
-
import { jsx as
|
|
1177
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1052
1178
|
function fieldAria(field, error, ids) {
|
|
1053
1179
|
const describedBy = [
|
|
1054
1180
|
field.description ? ids.descriptionId : null,
|
|
@@ -1095,7 +1221,7 @@ function FieldShell({
|
|
|
1095
1221
|
const descriptionCls = useSlotClass("fillo-description", { slot: "fieldDescription", ...state });
|
|
1096
1222
|
const errorCls = useSlotClass("fillo-error", { slot: "error", ...state });
|
|
1097
1223
|
const strings = useStrings();
|
|
1098
|
-
return /* @__PURE__ */
|
|
1224
|
+
return /* @__PURE__ */ jsxs5(
|
|
1099
1225
|
"div",
|
|
1100
1226
|
{
|
|
1101
1227
|
className: fieldCls,
|
|
@@ -1105,13 +1231,13 @@ function FieldShell({
|
|
|
1105
1231
|
"data-invalid": error ? "" : void 0,
|
|
1106
1232
|
"data-required": field.required ? "" : void 0,
|
|
1107
1233
|
children: [
|
|
1108
|
-
/* @__PURE__ */
|
|
1234
|
+
/* @__PURE__ */ jsxs5("label", { className: labelCls, "data-fillo": "label", id: ids.labelId, htmlFor: ids.inputId, children: [
|
|
1109
1235
|
field.label,
|
|
1110
|
-
!field.required && /* @__PURE__ */
|
|
1236
|
+
!field.required && /* @__PURE__ */ jsx5("span", { className: "fillo-optional", children: strings.optional })
|
|
1111
1237
|
] }),
|
|
1112
|
-
field.description && /* @__PURE__ */
|
|
1238
|
+
field.description && /* @__PURE__ */ jsx5("p", { className: descriptionCls, "data-fillo": "fieldDescription", id: ids.descriptionId, children: field.description }),
|
|
1113
1239
|
children,
|
|
1114
|
-
error && /* @__PURE__ */
|
|
1240
|
+
error && /* @__PURE__ */ jsx5("p", { className: errorCls, "data-fillo": "error", id: ids.errorId, role: "alert", children: error })
|
|
1115
1241
|
]
|
|
1116
1242
|
}
|
|
1117
1243
|
);
|
|
@@ -1120,7 +1246,7 @@ function TextInput({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1120
1246
|
const ids = useFilloFieldIds(field.id, providedIds);
|
|
1121
1247
|
const type = field.kind === "email" ? "email" : field.kind === "url" ? "url" : field.kind === "number" ? "number" : field.kind === "date" ? "date" : "text";
|
|
1122
1248
|
const control = useControl("fillo-input", field, error);
|
|
1123
|
-
return /* @__PURE__ */
|
|
1249
|
+
return /* @__PURE__ */ jsx5(FieldShell, { field, error, ids, children: /* @__PURE__ */ jsx5(
|
|
1124
1250
|
"input",
|
|
1125
1251
|
{
|
|
1126
1252
|
id: ids.inputId,
|
|
@@ -1139,7 +1265,7 @@ function TextInput({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1139
1265
|
function LongText({ field, value, error, setValue, ids: providedIds }) {
|
|
1140
1266
|
const ids = useFilloFieldIds(field.id, providedIds);
|
|
1141
1267
|
const control = useControl("fillo-input fillo-textarea", field, error);
|
|
1142
|
-
return /* @__PURE__ */
|
|
1268
|
+
return /* @__PURE__ */ jsx5(FieldShell, { field, error, ids, children: /* @__PURE__ */ jsx5(
|
|
1143
1269
|
"textarea",
|
|
1144
1270
|
{
|
|
1145
1271
|
id: ids.inputId,
|
|
@@ -1176,7 +1302,7 @@ var OPTION_ICON_PATHS = {
|
|
|
1176
1302
|
};
|
|
1177
1303
|
function ChoiceIcon({ icon }) {
|
|
1178
1304
|
if (!icon) return null;
|
|
1179
|
-
return /* @__PURE__ */
|
|
1305
|
+
return /* @__PURE__ */ jsx5(
|
|
1180
1306
|
"svg",
|
|
1181
1307
|
{
|
|
1182
1308
|
className: "fillo-option-icon",
|
|
@@ -1187,7 +1313,7 @@ function ChoiceIcon({ icon }) {
|
|
|
1187
1313
|
strokeLinecap: "round",
|
|
1188
1314
|
strokeLinejoin: "round",
|
|
1189
1315
|
"aria-hidden": "true",
|
|
1190
|
-
children: /* @__PURE__ */
|
|
1316
|
+
children: /* @__PURE__ */ jsx5("path", { d: OPTION_ICON_PATHS[icon] })
|
|
1191
1317
|
}
|
|
1192
1318
|
);
|
|
1193
1319
|
}
|
|
@@ -1201,7 +1327,7 @@ function SingleChoice({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1201
1327
|
const isOtherValue = typeof value === "string" && value !== "" && !choice.options.some((o) => o.id === value);
|
|
1202
1328
|
const [otherOn, setOtherOn] = useState5(isOtherValue);
|
|
1203
1329
|
const otherActive = isOtherValue || value === "" && otherOn;
|
|
1204
|
-
return /* @__PURE__ */
|
|
1330
|
+
return /* @__PURE__ */ jsx5(FieldShell, { field, error, ids, children: /* @__PURE__ */ jsxs5(
|
|
1205
1331
|
"div",
|
|
1206
1332
|
{
|
|
1207
1333
|
className: slotClass2("fillo-options", appearance, { slot: "options", kind: field.kind, fieldId: field.id }),
|
|
@@ -1210,7 +1336,7 @@ function SingleChoice({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1210
1336
|
"aria-labelledby": ids.labelId,
|
|
1211
1337
|
...fieldAria(field, error, ids),
|
|
1212
1338
|
children: [
|
|
1213
|
-
options.map((opt) => /* @__PURE__ */
|
|
1339
|
+
options.map((opt) => /* @__PURE__ */ jsxs5(
|
|
1214
1340
|
"label",
|
|
1215
1341
|
{
|
|
1216
1342
|
"data-option": opt.id,
|
|
@@ -1228,7 +1354,7 @@ function SingleChoice({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1228
1354
|
setValue(opt.id);
|
|
1229
1355
|
},
|
|
1230
1356
|
children: [
|
|
1231
|
-
/* @__PURE__ */
|
|
1357
|
+
/* @__PURE__ */ jsx5(
|
|
1232
1358
|
"input",
|
|
1233
1359
|
{
|
|
1234
1360
|
type: "radio",
|
|
@@ -1241,15 +1367,15 @@ function SingleChoice({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1241
1367
|
}
|
|
1242
1368
|
}
|
|
1243
1369
|
),
|
|
1244
|
-
/* @__PURE__ */
|
|
1245
|
-
/* @__PURE__ */
|
|
1370
|
+
/* @__PURE__ */ jsx5(ChoiceIcon, { icon: opt.icon }),
|
|
1371
|
+
/* @__PURE__ */ jsx5("span", { className: slotClass2("fillo-option-label", appearance, { slot: "optionLabel", kind: field.kind, fieldId: field.id }), "data-fillo": "optionLabel", children: opt.label })
|
|
1246
1372
|
]
|
|
1247
1373
|
},
|
|
1248
1374
|
opt.id
|
|
1249
1375
|
)),
|
|
1250
1376
|
choice.allowOther && // A <div>, not a <label>, so editing the free-text input below can't
|
|
1251
1377
|
// toggle the radio. The radio + "Other" text keep their own <label>.
|
|
1252
|
-
/* @__PURE__ */
|
|
1378
|
+
/* @__PURE__ */ jsxs5(
|
|
1253
1379
|
"div",
|
|
1254
1380
|
{
|
|
1255
1381
|
"data-fillo": "option",
|
|
@@ -1260,8 +1386,8 @@ function SingleChoice({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1260
1386
|
otherActive
|
|
1261
1387
|
),
|
|
1262
1388
|
children: [
|
|
1263
|
-
/* @__PURE__ */
|
|
1264
|
-
/* @__PURE__ */
|
|
1389
|
+
/* @__PURE__ */ jsxs5("label", { className: "fillo-option-main", children: [
|
|
1390
|
+
/* @__PURE__ */ jsx5(
|
|
1265
1391
|
"input",
|
|
1266
1392
|
{
|
|
1267
1393
|
type: "radio",
|
|
@@ -1274,9 +1400,9 @@ function SingleChoice({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1274
1400
|
}
|
|
1275
1401
|
}
|
|
1276
1402
|
),
|
|
1277
|
-
/* @__PURE__ */
|
|
1403
|
+
/* @__PURE__ */ jsx5("span", { className: slotClass2("fillo-option-label", appearance, { slot: "optionLabel", kind: field.kind, fieldId: field.id }), "data-fillo": "optionLabel", children: strings.other })
|
|
1278
1404
|
] }),
|
|
1279
|
-
otherActive && /* @__PURE__ */
|
|
1405
|
+
otherActive && /* @__PURE__ */ jsx5(
|
|
1280
1406
|
"input",
|
|
1281
1407
|
{
|
|
1282
1408
|
type: "text",
|
|
@@ -1308,7 +1434,7 @@ function MultiChoice({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1308
1434
|
const toggle = (id) => setValue(selected.includes(id) ? selected.filter((v) => v !== id) : [...selected, id]);
|
|
1309
1435
|
const appearance = useFilloAppearance();
|
|
1310
1436
|
const optionCls = (base, optionId, isSelected) => slotClass2(base, appearance, { slot: "option", kind: field.kind, fieldId: field.id, optionId, selected: isSelected });
|
|
1311
|
-
return /* @__PURE__ */
|
|
1437
|
+
return /* @__PURE__ */ jsx5(FieldShell, { field, error, ids, children: /* @__PURE__ */ jsxs5(
|
|
1312
1438
|
"div",
|
|
1313
1439
|
{
|
|
1314
1440
|
className: slotClass2("fillo-options", appearance, { slot: "options", kind: field.kind, fieldId: field.id }),
|
|
@@ -1317,7 +1443,7 @@ function MultiChoice({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1317
1443
|
"aria-labelledby": ids.labelId,
|
|
1318
1444
|
...fieldAria(field, error, ids),
|
|
1319
1445
|
children: [
|
|
1320
|
-
options.map((opt) => /* @__PURE__ */
|
|
1446
|
+
options.map((opt) => /* @__PURE__ */ jsxs5(
|
|
1321
1447
|
"label",
|
|
1322
1448
|
{
|
|
1323
1449
|
"data-option": opt.id,
|
|
@@ -1334,7 +1460,7 @@ function MultiChoice({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1334
1460
|
toggle(opt.id);
|
|
1335
1461
|
},
|
|
1336
1462
|
children: [
|
|
1337
|
-
/* @__PURE__ */
|
|
1463
|
+
/* @__PURE__ */ jsx5(
|
|
1338
1464
|
"input",
|
|
1339
1465
|
{
|
|
1340
1466
|
type: "checkbox",
|
|
@@ -1343,15 +1469,15 @@ function MultiChoice({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1343
1469
|
onChange: () => toggle(opt.id)
|
|
1344
1470
|
}
|
|
1345
1471
|
),
|
|
1346
|
-
/* @__PURE__ */
|
|
1347
|
-
/* @__PURE__ */
|
|
1472
|
+
/* @__PURE__ */ jsx5(ChoiceIcon, { icon: opt.icon }),
|
|
1473
|
+
/* @__PURE__ */ jsx5("span", { className: slotClass2("fillo-option-label", appearance, { slot: "optionLabel", kind: field.kind, fieldId: field.id }), "data-fillo": "optionLabel", children: opt.label })
|
|
1348
1474
|
]
|
|
1349
1475
|
},
|
|
1350
1476
|
opt.id
|
|
1351
1477
|
)),
|
|
1352
1478
|
choice.allowOther && // A <div>, not a <label>, so editing the free-text input below can't
|
|
1353
1479
|
// toggle the checkbox. The checkbox + "Other" text keep their own <label>.
|
|
1354
|
-
/* @__PURE__ */
|
|
1480
|
+
/* @__PURE__ */ jsxs5(
|
|
1355
1481
|
"div",
|
|
1356
1482
|
{
|
|
1357
1483
|
"data-fillo": "option",
|
|
@@ -1362,8 +1488,8 @@ function MultiChoice({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1362
1488
|
otherActive
|
|
1363
1489
|
),
|
|
1364
1490
|
children: [
|
|
1365
|
-
/* @__PURE__ */
|
|
1366
|
-
/* @__PURE__ */
|
|
1491
|
+
/* @__PURE__ */ jsxs5("label", { className: "fillo-option-main", children: [
|
|
1492
|
+
/* @__PURE__ */ jsx5(
|
|
1367
1493
|
"input",
|
|
1368
1494
|
{
|
|
1369
1495
|
type: "checkbox",
|
|
@@ -1379,9 +1505,9 @@ function MultiChoice({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1379
1505
|
}
|
|
1380
1506
|
}
|
|
1381
1507
|
),
|
|
1382
|
-
/* @__PURE__ */
|
|
1508
|
+
/* @__PURE__ */ jsx5("span", { className: slotClass2("fillo-option-label", appearance, { slot: "optionLabel", kind: field.kind, fieldId: field.id }), "data-fillo": "optionLabel", children: strings.other })
|
|
1383
1509
|
] }),
|
|
1384
|
-
otherActive && /* @__PURE__ */
|
|
1510
|
+
otherActive && /* @__PURE__ */ jsx5(
|
|
1385
1511
|
"input",
|
|
1386
1512
|
{
|
|
1387
1513
|
type: "text",
|
|
@@ -1419,9 +1545,9 @@ function Dropdown({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1419
1545
|
const [otherOn, setOtherOn] = useState5(isOtherValue);
|
|
1420
1546
|
const otherActive = isOtherValue || value === "" && otherOn;
|
|
1421
1547
|
const control = useControl("fillo-input fillo-select", field, error);
|
|
1422
|
-
return /* @__PURE__ */
|
|
1423
|
-
/* @__PURE__ */
|
|
1424
|
-
/* @__PURE__ */
|
|
1548
|
+
return /* @__PURE__ */ jsxs5(FieldShell, { field, error, ids, children: [
|
|
1549
|
+
/* @__PURE__ */ jsxs5("div", { className: "fillo-select-wrap", children: [
|
|
1550
|
+
/* @__PURE__ */ jsxs5(
|
|
1425
1551
|
"select",
|
|
1426
1552
|
{
|
|
1427
1553
|
id: ids.inputId,
|
|
@@ -1438,15 +1564,15 @@ function Dropdown({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1438
1564
|
}
|
|
1439
1565
|
},
|
|
1440
1566
|
children: [
|
|
1441
|
-
/* @__PURE__ */
|
|
1442
|
-
options.map((opt) => /* @__PURE__ */
|
|
1443
|
-
choice.allowOther && /* @__PURE__ */
|
|
1567
|
+
/* @__PURE__ */ jsx5("option", { value: "", children: field.placeholder ?? strings.choosePlaceholder }),
|
|
1568
|
+
options.map((opt) => /* @__PURE__ */ jsx5("option", { value: opt.id, children: opt.label }, opt.id)),
|
|
1569
|
+
choice.allowOther && /* @__PURE__ */ jsx5("option", { value: otherValue, children: `${strings.other}\u2026` })
|
|
1444
1570
|
]
|
|
1445
1571
|
}
|
|
1446
1572
|
),
|
|
1447
|
-
/* @__PURE__ */
|
|
1573
|
+
/* @__PURE__ */ jsx5("span", { className: "fillo-select-icon", "aria-hidden": "true" })
|
|
1448
1574
|
] }),
|
|
1449
|
-
otherActive && /* @__PURE__ */
|
|
1575
|
+
otherActive && /* @__PURE__ */ jsx5(
|
|
1450
1576
|
"input",
|
|
1451
1577
|
{
|
|
1452
1578
|
type: "text",
|
|
@@ -1467,19 +1593,19 @@ function Checkbox({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1467
1593
|
const slots = useFieldSlots(field, error);
|
|
1468
1594
|
const checkbox = field;
|
|
1469
1595
|
if (checkbox.appearance === "toggle") {
|
|
1470
|
-
return /* @__PURE__ */
|
|
1596
|
+
return /* @__PURE__ */ jsxs5(
|
|
1471
1597
|
"div",
|
|
1472
1598
|
{
|
|
1473
1599
|
...slots.wrapperProps(
|
|
1474
1600
|
`fillo-field fillo-field--checkbox fillo-field--toggle${error ? " fillo-field--error" : ""}`
|
|
1475
1601
|
),
|
|
1476
1602
|
children: [
|
|
1477
|
-
/* @__PURE__ */
|
|
1478
|
-
/* @__PURE__ */
|
|
1603
|
+
/* @__PURE__ */ jsxs5("label", { className: "fillo-toggle", "data-fillo": "option", "data-checked": value === true ? "" : void 0, children: [
|
|
1604
|
+
/* @__PURE__ */ jsx5("span", { className: "fillo-toggle-copy", children: /* @__PURE__ */ jsxs5("span", { className: slotClass2("fillo-option-label", appearance, { slot: "optionLabel", kind: field.kind, fieldId: field.id }), "data-fillo": "optionLabel", children: [
|
|
1479
1605
|
field.label,
|
|
1480
|
-
!field.required && /* @__PURE__ */
|
|
1606
|
+
!field.required && /* @__PURE__ */ jsx5("span", { className: "fillo-optional", children: strings.optional })
|
|
1481
1607
|
] }) }),
|
|
1482
|
-
/* @__PURE__ */
|
|
1608
|
+
/* @__PURE__ */ jsx5(
|
|
1483
1609
|
"input",
|
|
1484
1610
|
{
|
|
1485
1611
|
id: ids.inputId,
|
|
@@ -1492,23 +1618,23 @@ function Checkbox({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1492
1618
|
...fieldAria(field, error, ids)
|
|
1493
1619
|
}
|
|
1494
1620
|
),
|
|
1495
|
-
/* @__PURE__ */
|
|
1621
|
+
/* @__PURE__ */ jsx5("span", { className: "fillo-toggle-track", "aria-hidden": "true", children: /* @__PURE__ */ jsx5("span", { className: "fillo-toggle-thumb" }) })
|
|
1496
1622
|
] }),
|
|
1497
|
-
field.description && /* @__PURE__ */
|
|
1498
|
-
error && /* @__PURE__ */
|
|
1623
|
+
field.description && /* @__PURE__ */ jsx5("p", { className: slots.description, "data-fillo": "fieldDescription", id: ids.descriptionId, children: field.description }),
|
|
1624
|
+
error && /* @__PURE__ */ jsx5("p", { className: slots.error, "data-fillo": "error", id: ids.errorId, role: "alert", children: error })
|
|
1499
1625
|
]
|
|
1500
1626
|
}
|
|
1501
1627
|
);
|
|
1502
1628
|
}
|
|
1503
|
-
return /* @__PURE__ */
|
|
1629
|
+
return /* @__PURE__ */ jsxs5(
|
|
1504
1630
|
"div",
|
|
1505
1631
|
{
|
|
1506
1632
|
...slots.wrapperProps(
|
|
1507
1633
|
`fillo-field fillo-field--checkbox${error ? " fillo-field--error" : ""}`
|
|
1508
1634
|
),
|
|
1509
1635
|
children: [
|
|
1510
|
-
/* @__PURE__ */
|
|
1511
|
-
/* @__PURE__ */
|
|
1636
|
+
/* @__PURE__ */ jsxs5("label", { className: "fillo-option", "data-fillo": "option", "data-checked": value === true ? "" : void 0, children: [
|
|
1637
|
+
/* @__PURE__ */ jsx5(
|
|
1512
1638
|
"input",
|
|
1513
1639
|
{
|
|
1514
1640
|
id: ids.inputId,
|
|
@@ -1519,13 +1645,13 @@ function Checkbox({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1519
1645
|
...fieldAria(field, error, ids)
|
|
1520
1646
|
}
|
|
1521
1647
|
),
|
|
1522
|
-
/* @__PURE__ */
|
|
1648
|
+
/* @__PURE__ */ jsxs5("span", { className: slotClass2("fillo-option-label", appearance, { slot: "optionLabel", kind: field.kind, fieldId: field.id }), "data-fillo": "optionLabel", children: [
|
|
1523
1649
|
field.label,
|
|
1524
|
-
!field.required && /* @__PURE__ */
|
|
1650
|
+
!field.required && /* @__PURE__ */ jsx5("span", { className: "fillo-optional", children: strings.optional })
|
|
1525
1651
|
] })
|
|
1526
1652
|
] }),
|
|
1527
|
-
field.description && /* @__PURE__ */
|
|
1528
|
-
error && /* @__PURE__ */
|
|
1653
|
+
field.description && /* @__PURE__ */ jsx5("p", { className: slots.description, "data-fillo": "fieldDescription", id: ids.descriptionId, children: field.description }),
|
|
1654
|
+
error && /* @__PURE__ */ jsx5("p", { className: slots.error, "data-fillo": "error", id: ids.errorId, role: "alert", children: error })
|
|
1529
1655
|
]
|
|
1530
1656
|
}
|
|
1531
1657
|
);
|
|
@@ -1564,7 +1690,7 @@ function Rating({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1564
1690
|
const tabbable = rovingIndex(steps, selected);
|
|
1565
1691
|
const appearance = useFilloAppearance();
|
|
1566
1692
|
const control = useControl("fillo-rating", field, error);
|
|
1567
|
-
return /* @__PURE__ */
|
|
1693
|
+
return /* @__PURE__ */ jsx5(FieldShell, { field, error, ids, children: /* @__PURE__ */ jsx5(
|
|
1568
1694
|
"div",
|
|
1569
1695
|
{
|
|
1570
1696
|
...control,
|
|
@@ -1572,7 +1698,7 @@ function Rating({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1572
1698
|
"aria-labelledby": ids.labelId,
|
|
1573
1699
|
...fieldAria(field, error, ids),
|
|
1574
1700
|
onKeyDown: (e) => radiogroupKeyDown(e, steps, selected, (n) => setValue(n)),
|
|
1575
|
-
children: steps.map((n, i) => /* @__PURE__ */
|
|
1701
|
+
children: steps.map((n, i) => /* @__PURE__ */ jsx5(
|
|
1576
1702
|
"button",
|
|
1577
1703
|
{
|
|
1578
1704
|
type: "button",
|
|
@@ -1615,8 +1741,8 @@ function LinearScale({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1615
1741
|
].filter(Boolean).join(" ") || void 0;
|
|
1616
1742
|
const appearance = useFilloAppearance();
|
|
1617
1743
|
const control = useControl("fillo-scale", field, error);
|
|
1618
|
-
return /* @__PURE__ */
|
|
1619
|
-
/* @__PURE__ */
|
|
1744
|
+
return /* @__PURE__ */ jsxs5(FieldShell, { field, error, ids, children: [
|
|
1745
|
+
/* @__PURE__ */ jsx5(
|
|
1620
1746
|
"div",
|
|
1621
1747
|
{
|
|
1622
1748
|
...control,
|
|
@@ -1626,7 +1752,7 @@ function LinearScale({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1626
1752
|
"aria-required": field.required ? true : void 0,
|
|
1627
1753
|
"aria-describedby": describedBy,
|
|
1628
1754
|
onKeyDown: (e) => radiogroupKeyDown(e, steps, current, (n) => setValue(n)),
|
|
1629
|
-
children: steps.map((n, i) => /* @__PURE__ */
|
|
1755
|
+
children: steps.map((n, i) => /* @__PURE__ */ jsx5(
|
|
1630
1756
|
"button",
|
|
1631
1757
|
{
|
|
1632
1758
|
type: "button",
|
|
@@ -1649,9 +1775,9 @@ function LinearScale({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1649
1775
|
))
|
|
1650
1776
|
}
|
|
1651
1777
|
),
|
|
1652
|
-
hasLabels && /* @__PURE__ */
|
|
1653
|
-
/* @__PURE__ */
|
|
1654
|
-
/* @__PURE__ */
|
|
1778
|
+
hasLabels && /* @__PURE__ */ jsxs5("div", { className: "fillo-scale-labels", children: [
|
|
1779
|
+
/* @__PURE__ */ jsx5("span", { id: minLabelId, children: scale.minLabel }),
|
|
1780
|
+
/* @__PURE__ */ jsx5("span", { id: maxLabelId, children: scale.maxLabel })
|
|
1655
1781
|
] })
|
|
1656
1782
|
] });
|
|
1657
1783
|
}
|
|
@@ -1671,21 +1797,21 @@ function Ranking({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1671
1797
|
[next[index], next[target]] = [next[target], next[index]];
|
|
1672
1798
|
setValue(next);
|
|
1673
1799
|
}
|
|
1674
|
-
return /* @__PURE__ */
|
|
1800
|
+
return /* @__PURE__ */ jsx5(FieldShell, { field, error, ids, children: /* @__PURE__ */ jsx5(
|
|
1675
1801
|
"div",
|
|
1676
1802
|
{
|
|
1677
1803
|
id: ids.inputId,
|
|
1678
1804
|
role: "group",
|
|
1679
1805
|
"aria-labelledby": ids.labelId,
|
|
1680
1806
|
...fieldAria(field, error, ids),
|
|
1681
|
-
children: /* @__PURE__ */
|
|
1807
|
+
children: /* @__PURE__ */ jsx5("ol", { className: "fillo-ranking", children: order.map((id, index) => {
|
|
1682
1808
|
const option = ranking.options.find((o) => o.id === id);
|
|
1683
1809
|
if (!option) return null;
|
|
1684
|
-
return /* @__PURE__ */
|
|
1685
|
-
/* @__PURE__ */
|
|
1686
|
-
/* @__PURE__ */
|
|
1687
|
-
/* @__PURE__ */
|
|
1688
|
-
/* @__PURE__ */
|
|
1810
|
+
return /* @__PURE__ */ jsxs5("li", { className: "fillo-ranking-item", children: [
|
|
1811
|
+
/* @__PURE__ */ jsx5("span", { className: "fillo-ranking-index", children: index + 1 }),
|
|
1812
|
+
/* @__PURE__ */ jsx5("span", { className: "fillo-ranking-label", children: option.label }),
|
|
1813
|
+
/* @__PURE__ */ jsxs5("span", { className: "fillo-ranking-controls", children: [
|
|
1814
|
+
/* @__PURE__ */ jsx5(
|
|
1689
1815
|
"button",
|
|
1690
1816
|
{
|
|
1691
1817
|
type: "button",
|
|
@@ -1696,7 +1822,7 @@ function Ranking({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1696
1822
|
children: "\u2191"
|
|
1697
1823
|
}
|
|
1698
1824
|
),
|
|
1699
|
-
/* @__PURE__ */
|
|
1825
|
+
/* @__PURE__ */ jsx5(
|
|
1700
1826
|
"button",
|
|
1701
1827
|
{
|
|
1702
1828
|
type: "button",
|
|
@@ -1717,7 +1843,7 @@ function Matrix({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1717
1843
|
const ids = useFilloFieldIds(field.id, providedIds);
|
|
1718
1844
|
const matrix = field;
|
|
1719
1845
|
const answers = value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
1720
|
-
return /* @__PURE__ */
|
|
1846
|
+
return /* @__PURE__ */ jsx5(FieldShell, { field, error, ids, children: /* @__PURE__ */ jsx5(
|
|
1721
1847
|
"div",
|
|
1722
1848
|
{
|
|
1723
1849
|
id: ids.inputId,
|
|
@@ -1725,12 +1851,12 @@ function Matrix({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1725
1851
|
role: "group",
|
|
1726
1852
|
"aria-labelledby": ids.labelId,
|
|
1727
1853
|
...fieldAria(field, error, ids),
|
|
1728
|
-
children: /* @__PURE__ */
|
|
1729
|
-
/* @__PURE__ */
|
|
1730
|
-
/* @__PURE__ */
|
|
1731
|
-
matrix.columns.map((col) => /* @__PURE__ */
|
|
1854
|
+
children: /* @__PURE__ */ jsxs5("table", { className: "fillo-matrix", children: [
|
|
1855
|
+
/* @__PURE__ */ jsx5("thead", { children: /* @__PURE__ */ jsxs5("tr", { children: [
|
|
1856
|
+
/* @__PURE__ */ jsx5("th", {}),
|
|
1857
|
+
matrix.columns.map((col) => /* @__PURE__ */ jsx5("th", { scope: "col", children: col.label }, col.id))
|
|
1732
1858
|
] }) }),
|
|
1733
|
-
/* @__PURE__ */
|
|
1859
|
+
/* @__PURE__ */ jsx5("tbody", { children: matrix.rows.map((row) => {
|
|
1734
1860
|
const rowLabelId = `${ids.inputId}-${row.id}`;
|
|
1735
1861
|
const answered = answers[row.id] !== void 0;
|
|
1736
1862
|
const rowInvalid = Boolean(error) && field.required && !answered;
|
|
@@ -1741,17 +1867,17 @@ function Matrix({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1741
1867
|
// radiogroup lives on an inner element that aria-owns the radios
|
|
1742
1868
|
// scattered across the cells (they can't be DOM-nested inside a
|
|
1743
1869
|
// single element within a table row).
|
|
1744
|
-
/* @__PURE__ */
|
|
1745
|
-
/* @__PURE__ */
|
|
1870
|
+
/* @__PURE__ */ jsxs5("tr", { children: [
|
|
1871
|
+
/* @__PURE__ */ jsx5("th", { scope: "row", children: /* @__PURE__ */ jsx5(
|
|
1746
1872
|
"div",
|
|
1747
1873
|
{
|
|
1748
1874
|
role: "radiogroup",
|
|
1749
1875
|
"aria-labelledby": rowLabelId,
|
|
1750
1876
|
"aria-owns": matrix.columns.map(radioId).join(" "),
|
|
1751
|
-
children: /* @__PURE__ */
|
|
1877
|
+
children: /* @__PURE__ */ jsx5("span", { id: rowLabelId, children: row.label })
|
|
1752
1878
|
}
|
|
1753
1879
|
) }),
|
|
1754
|
-
matrix.columns.map((col) => /* @__PURE__ */
|
|
1880
|
+
matrix.columns.map((col) => /* @__PURE__ */ jsx5("td", { children: /* @__PURE__ */ jsx5(
|
|
1755
1881
|
"input",
|
|
1756
1882
|
{
|
|
1757
1883
|
id: radioId(col),
|
|
@@ -1798,11 +1924,11 @@ var DEFAULT_COMPONENTS = {
|
|
|
1798
1924
|
function ContentRenderer({ block }) {
|
|
1799
1925
|
switch (block.kind) {
|
|
1800
1926
|
case "heading":
|
|
1801
|
-
return /* @__PURE__ */
|
|
1927
|
+
return /* @__PURE__ */ jsx5("h3", { className: "fillo-heading", children: block.text });
|
|
1802
1928
|
case "paragraph":
|
|
1803
|
-
return /* @__PURE__ */
|
|
1929
|
+
return /* @__PURE__ */ jsx5("p", { className: "fillo-paragraph", children: block.text });
|
|
1804
1930
|
case "divider":
|
|
1805
|
-
return /* @__PURE__ */
|
|
1931
|
+
return /* @__PURE__ */ jsx5("hr", { className: "fillo-divider" });
|
|
1806
1932
|
}
|
|
1807
1933
|
}
|
|
1808
1934
|
function blockHasPiping(block) {
|
|
@@ -1841,7 +1967,7 @@ function BlockRendererInner({ block, api, components, customComponents }) {
|
|
|
1841
1967
|
block = pipeBlock(block, api.data, api.form);
|
|
1842
1968
|
const ids = useFilloFieldIds(block.id);
|
|
1843
1969
|
const strings = useStrings();
|
|
1844
|
-
if (!isField(block)) return /* @__PURE__ */
|
|
1970
|
+
if (!isField(block)) return /* @__PURE__ */ jsx5(ContentRenderer, { block });
|
|
1845
1971
|
let Component;
|
|
1846
1972
|
if (block.kind === "custom") {
|
|
1847
1973
|
Component = customComponents?.[block.component] ?? components?.custom;
|
|
@@ -1859,7 +1985,7 @@ function BlockRendererInner({ block, api, components, customComponents }) {
|
|
|
1859
1985
|
}
|
|
1860
1986
|
const rawError = api.errors[block.id];
|
|
1861
1987
|
const error = rawError === REQUIRED_FIELD_MESSAGE ? strings.required : rawError;
|
|
1862
|
-
return /* @__PURE__ */
|
|
1988
|
+
return /* @__PURE__ */ jsx5(
|
|
1863
1989
|
Component,
|
|
1864
1990
|
{
|
|
1865
1991
|
field: block,
|
|
@@ -1896,7 +2022,7 @@ function FormField({
|
|
|
1896
2022
|
const block = api.form.pages.flatMap((p) => p.blocks).find((b) => b.id === id);
|
|
1897
2023
|
if (!block) return null;
|
|
1898
2024
|
if (isField(block) && !visibleIds.has(block.id)) return null;
|
|
1899
|
-
return /* @__PURE__ */
|
|
2025
|
+
return /* @__PURE__ */ jsx5(
|
|
1900
2026
|
BlockRenderer,
|
|
1901
2027
|
{
|
|
1902
2028
|
block,
|
|
@@ -1909,7 +2035,7 @@ function FormField({
|
|
|
1909
2035
|
|
|
1910
2036
|
// src/turnstile.tsx
|
|
1911
2037
|
import { useEffect as useEffect7, useRef as useRef6 } from "react";
|
|
1912
|
-
import { jsx as
|
|
2038
|
+
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
1913
2039
|
var SCRIPT_SRC = "https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit";
|
|
1914
2040
|
var SCRIPT_ID = "fillo-turnstile-script";
|
|
1915
2041
|
var scriptPromise = null;
|
|
@@ -2016,7 +2142,7 @@ function TurnstileWidget({
|
|
|
2016
2142
|
onTokenRef.current(void 0);
|
|
2017
2143
|
};
|
|
2018
2144
|
}, [siteKey]);
|
|
2019
|
-
return /* @__PURE__ */
|
|
2145
|
+
return /* @__PURE__ */ jsx6(
|
|
2020
2146
|
"div",
|
|
2021
2147
|
{
|
|
2022
2148
|
ref: containerRef,
|
|
@@ -2027,7 +2153,7 @@ function TurnstileWidget({
|
|
|
2027
2153
|
}
|
|
2028
2154
|
|
|
2029
2155
|
// src/FilloForm.tsx
|
|
2030
|
-
import { Fragment as
|
|
2156
|
+
import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
2031
2157
|
function themeProps(theme) {
|
|
2032
2158
|
const safeTheme = normalizeFormTheme(theme);
|
|
2033
2159
|
if (!safeTheme) return {};
|
|
@@ -2044,16 +2170,6 @@ function themeProps(theme) {
|
|
|
2044
2170
|
...hasColorTheme ? { "data-fillo-has-color-theme": "" } : {}
|
|
2045
2171
|
};
|
|
2046
2172
|
}
|
|
2047
|
-
function safeHttpUrl(href) {
|
|
2048
|
-
try {
|
|
2049
|
-
if (!href) return null;
|
|
2050
|
-
const url = new URL(href);
|
|
2051
|
-
if (url.protocol !== "http:" && url.protocol !== "https:") return null;
|
|
2052
|
-
return url.toString();
|
|
2053
|
-
} catch {
|
|
2054
|
-
return null;
|
|
2055
|
-
}
|
|
2056
|
-
}
|
|
2057
2173
|
function badgeUrl(href) {
|
|
2058
2174
|
const url = new URL(safeHttpUrl(href) ?? "https://fillo.so");
|
|
2059
2175
|
url.searchParams.set("utm_source", "form_badge");
|
|
@@ -2094,6 +2210,9 @@ function FilloForm(props) {
|
|
|
2094
2210
|
);
|
|
2095
2211
|
const [loaded, setLoaded] = useState6(null);
|
|
2096
2212
|
const [loadFailure, setLoadFailure] = useState6(null);
|
|
2213
|
+
const devEnv = useIsDevEnv();
|
|
2214
|
+
const devChrome = props.preview === true || devEnv;
|
|
2215
|
+
if (props.preview === true) warnPreviewInProduction();
|
|
2097
2216
|
const codeForm = isCodeForm2(form) ? form : null;
|
|
2098
2217
|
const onError = props.onError;
|
|
2099
2218
|
const {
|
|
@@ -2105,14 +2224,19 @@ function FilloForm(props) {
|
|
|
2105
2224
|
noticeError: syncNoticeError,
|
|
2106
2225
|
syncing: codeFormSyncing,
|
|
2107
2226
|
staged: codeChangesStaged,
|
|
2227
|
+
accepting: syncedAccepting,
|
|
2228
|
+
acceptingReason: syncedAcceptingReason,
|
|
2229
|
+
uploadsAvailable: syncedUploadsAvailable,
|
|
2108
2230
|
resolvedSchema,
|
|
2109
|
-
resolvedTheme
|
|
2231
|
+
resolvedTheme,
|
|
2232
|
+
warningCode: syncedWarningCode,
|
|
2233
|
+
warningUrl: syncedWarningUrl
|
|
2110
2234
|
} = useCodeFormSync(form, client, onError, {
|
|
2111
2235
|
// An absent client is intentional render-only use. Builder previews and an
|
|
2112
2236
|
// explicit canonical id also do not need publishable-key resolution.
|
|
2113
2237
|
allowUnsynced: Boolean(!client || props.skipValidation || formId)
|
|
2114
2238
|
});
|
|
2115
|
-
const renderResolvedSnapshot = Boolean(resolvedSchema && !
|
|
2239
|
+
const renderResolvedSnapshot = Boolean(resolvedSchema && !devChrome);
|
|
2116
2240
|
const inlineSchema = codeForm ? renderResolvedSnapshot ? resolvedSchema : codeForm.schema : form;
|
|
2117
2241
|
const onErrorRef = useRef7(onError);
|
|
2118
2242
|
useEffect8(() => {
|
|
@@ -2135,6 +2259,9 @@ function FilloForm(props) {
|
|
|
2135
2259
|
schema: published.schema,
|
|
2136
2260
|
theme: published.theme,
|
|
2137
2261
|
closed: Boolean(published.closed),
|
|
2262
|
+
accepting: published.accepting,
|
|
2263
|
+
acceptingReason: published.acceptingReason,
|
|
2264
|
+
uploadsAvailable: published.uploadsAvailable,
|
|
2138
2265
|
branding: published.branding ?? null,
|
|
2139
2266
|
challenge: published.challenge ?? null
|
|
2140
2267
|
});
|
|
@@ -2158,25 +2285,52 @@ function FilloForm(props) {
|
|
|
2158
2285
|
const theme = props.appearance?.theme ?? props.theme ?? resolvedCodeTheme ?? currentLoaded?.theme ?? void 0;
|
|
2159
2286
|
if (normalized && !normalized.ok) {
|
|
2160
2287
|
const error = new FilloError2(`This form could not be rendered: ${normalized.error}`, 422);
|
|
2161
|
-
if (props.renderError) return /* @__PURE__ */
|
|
2162
|
-
return /* @__PURE__ */
|
|
2288
|
+
if (props.renderError) return /* @__PURE__ */ jsx7(Fragment3, { children: props.renderError(error) });
|
|
2289
|
+
return /* @__PURE__ */ jsx7("div", { className: `fillo-form fillo-form--error ${props.className ?? ""}`, children: strings.renderFailed });
|
|
2163
2290
|
}
|
|
2164
2291
|
if (loadError) {
|
|
2165
2292
|
if (props.renderError) {
|
|
2166
|
-
return /* @__PURE__ */
|
|
2293
|
+
return /* @__PURE__ */ jsx7(Fragment3, { children: props.renderError(loadError) });
|
|
2167
2294
|
}
|
|
2168
2295
|
const message = loadError.status === 404 ? strings.loadFailedNotFound : loadError.status === 0 ? strings.loadFailedNetwork : strings.loadFailed;
|
|
2169
|
-
return /* @__PURE__ */
|
|
2296
|
+
return /* @__PURE__ */ jsx7("div", { className: `fillo-form fillo-form--error ${props.className ?? ""}`, children: message });
|
|
2297
|
+
}
|
|
2298
|
+
if (currentLoaded?.accepting === false && schema) {
|
|
2299
|
+
const variant = overlayVariant(currentLoaded.acceptingReason);
|
|
2300
|
+
if (props.renderError) {
|
|
2301
|
+
const error = variant === "closed" ? new FilloError2(
|
|
2302
|
+
"This form is no longer accepting responses.",
|
|
2303
|
+
403,
|
|
2304
|
+
void 0,
|
|
2305
|
+
currentLoaded.acceptingReason
|
|
2306
|
+
) : new FilloError2(
|
|
2307
|
+
"This form isn't accepting responses yet.",
|
|
2308
|
+
403,
|
|
2309
|
+
void 0,
|
|
2310
|
+
currentLoaded.acceptingReason
|
|
2311
|
+
);
|
|
2312
|
+
return /* @__PURE__ */ jsx7(Fragment3, { children: props.renderError(error) });
|
|
2313
|
+
}
|
|
2314
|
+
return /* @__PURE__ */ jsx7(
|
|
2315
|
+
NotOpenOverlay,
|
|
2316
|
+
{
|
|
2317
|
+
...props,
|
|
2318
|
+
variant,
|
|
2319
|
+
schema,
|
|
2320
|
+
theme,
|
|
2321
|
+
branding: currentLoaded.branding
|
|
2322
|
+
}
|
|
2323
|
+
);
|
|
2170
2324
|
}
|
|
2171
2325
|
if (currentLoaded?.closed) {
|
|
2172
|
-
return /* @__PURE__ */
|
|
2326
|
+
return /* @__PURE__ */ jsx7("div", { className: `fillo-form fillo-form--closed ${props.className ?? ""}`, ...themeProps(theme), children: /* @__PURE__ */ jsx7("p", { className: "fillo-closed", children: strings.closed }) });
|
|
2173
2327
|
}
|
|
2174
2328
|
const canonicalSyncError = codeForm && syncError && !props.skipValidation ? syncError : null;
|
|
2175
2329
|
if (canonicalSyncError && props.renderError) {
|
|
2176
|
-
return /* @__PURE__ */
|
|
2330
|
+
return /* @__PURE__ */ jsx7(Fragment3, { children: props.renderError(canonicalSyncError) });
|
|
2177
2331
|
}
|
|
2178
|
-
if (canonicalSyncError && !
|
|
2179
|
-
return /* @__PURE__ */
|
|
2332
|
+
if (canonicalSyncError && !devChrome) {
|
|
2333
|
+
return /* @__PURE__ */ jsx7(
|
|
2180
2334
|
"div",
|
|
2181
2335
|
{
|
|
2182
2336
|
className: `fillo-form fillo-form--error ${props.className ?? ""}`,
|
|
@@ -2189,23 +2343,42 @@ function FilloForm(props) {
|
|
|
2189
2343
|
);
|
|
2190
2344
|
}
|
|
2191
2345
|
const notPublished = Boolean(codeForm) && syncedStatus === "draft" && !props.skipValidation;
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2346
|
+
const syncNotAccepting = Boolean(codeForm) && !props.skipValidation && syncedStatus === "published" && syncedAccepting === false;
|
|
2347
|
+
if ((notPublished || syncNotAccepting) && !devChrome) {
|
|
2348
|
+
const variant = notPublished ? "notOpen" : overlayVariant(syncedAcceptingReason);
|
|
2349
|
+
const error = variant === "closed" ? new FilloError2(
|
|
2350
|
+
"This form is no longer accepting responses.",
|
|
2351
|
+
403,
|
|
2352
|
+
void 0,
|
|
2353
|
+
syncedAcceptingReason
|
|
2354
|
+
) : new FilloError2("This form isn't published yet.", 403);
|
|
2355
|
+
if (props.renderError) return /* @__PURE__ */ jsx7(Fragment3, { children: props.renderError(error) });
|
|
2356
|
+
if (schema) {
|
|
2357
|
+
return /* @__PURE__ */ jsx7(
|
|
2358
|
+
NotOpenOverlay,
|
|
2359
|
+
{
|
|
2360
|
+
...props,
|
|
2361
|
+
variant,
|
|
2362
|
+
schema,
|
|
2363
|
+
theme,
|
|
2364
|
+
branding: syncedBranding
|
|
2365
|
+
}
|
|
2366
|
+
);
|
|
2367
|
+
}
|
|
2368
|
+
return /* @__PURE__ */ jsx7("div", { className: `fillo-form fillo-form--closed ${props.className ?? ""}`, ...themeProps(theme), children: /* @__PURE__ */ jsx7("p", { className: "fillo-closed", children: variant === "closed" ? strings.closed : strings.notLive }) });
|
|
2196
2369
|
}
|
|
2197
2370
|
const awaitingCanonical = Boolean(codeForm && codeFormSyncing && !props.skipValidation);
|
|
2198
|
-
if (!
|
|
2199
|
-
return /* @__PURE__ */
|
|
2371
|
+
if (!devChrome && awaitingCanonical || !schema) {
|
|
2372
|
+
return /* @__PURE__ */ jsxs6(
|
|
2200
2373
|
"div",
|
|
2201
2374
|
{
|
|
2202
2375
|
className: `fillo-form fillo-form--loading ${props.className ?? ""}`,
|
|
2203
2376
|
"aria-busy": "true",
|
|
2204
2377
|
...themeProps(theme),
|
|
2205
2378
|
children: [
|
|
2206
|
-
/* @__PURE__ */
|
|
2207
|
-
/* @__PURE__ */
|
|
2208
|
-
/* @__PURE__ */
|
|
2379
|
+
/* @__PURE__ */ jsx7("div", { className: "fillo-skeleton" }),
|
|
2380
|
+
/* @__PURE__ */ jsx7("div", { className: "fillo-skeleton" }),
|
|
2381
|
+
/* @__PURE__ */ jsx7("div", { className: "fillo-skeleton fillo-skeleton--short" })
|
|
2209
2382
|
]
|
|
2210
2383
|
}
|
|
2211
2384
|
);
|
|
@@ -2213,7 +2386,7 @@ function FilloForm(props) {
|
|
|
2213
2386
|
const branding = codeForm ? syncedBranding : currentLoaded?.branding;
|
|
2214
2387
|
const poweredBy = branding?.poweredBy ?? true;
|
|
2215
2388
|
const challenge = props.challenge ?? (codeForm ? syncedChallenge : currentLoaded?.challenge) ?? void 0;
|
|
2216
|
-
return /* @__PURE__ */
|
|
2389
|
+
return /* @__PURE__ */ jsx7(
|
|
2217
2390
|
ResolvedForm,
|
|
2218
2391
|
{
|
|
2219
2392
|
...props,
|
|
@@ -2227,57 +2400,80 @@ function FilloForm(props) {
|
|
|
2227
2400
|
draftNotice: notPublished,
|
|
2228
2401
|
stagedNotice: codeChangesStaged,
|
|
2229
2402
|
syncErrorNotice: syncError ?? syncNoticeError ?? void 0,
|
|
2403
|
+
uploadsAvailable: codeForm ? syncedUploadsAvailable : currentLoaded?.uploadsAvailable,
|
|
2404
|
+
syncWarningCode: codeForm ? syncedWarningCode : void 0,
|
|
2405
|
+
syncWarningUrl: syncedWarningUrl,
|
|
2230
2406
|
challenge
|
|
2231
2407
|
}
|
|
2232
2408
|
);
|
|
2233
2409
|
}
|
|
2234
2410
|
function PoweredBy({ label, href }) {
|
|
2235
|
-
return /* @__PURE__ */
|
|
2236
|
-
}
|
|
2237
|
-
function isDevEnv() {
|
|
2238
|
-
return typeof process !== "undefined" && process.env?.NODE_ENV !== "production";
|
|
2239
|
-
}
|
|
2240
|
-
function DevNoClientNotice() {
|
|
2241
|
-
return /* @__PURE__ */ jsxs5("div", { className: "fillo-devwarning", role: "alert", children: [
|
|
2242
|
-
/* @__PURE__ */ jsx6("strong", { children: "No Fillo client connected." }),
|
|
2243
|
-
" This form is render-only and won\u2019t save responses. Pass a ",
|
|
2244
|
-
/* @__PURE__ */ jsx6("code", { children: "client" }),
|
|
2245
|
-
" to collect them in Fillo, or forward them with webhooks. (This notice only appears in development.)"
|
|
2246
|
-
] });
|
|
2411
|
+
return /* @__PURE__ */ jsx7("a", { className: "fillo-powered", href: badgeUrl(href), target: "_blank", rel: "noopener noreferrer", children: label ?? "Powered by Fillo" });
|
|
2247
2412
|
}
|
|
2248
|
-
function
|
|
2249
|
-
return
|
|
2250
|
-
/* @__PURE__ */ jsx6("strong", { children: "Draft form." }),
|
|
2251
|
-
" It renders here, but it won\u2019t accept responses until you publish it in your Fillo dashboard. (This notice only appears in development.)"
|
|
2252
|
-
] });
|
|
2413
|
+
function overlayVariant(reason) {
|
|
2414
|
+
return reason === "expired" || reason === "capped" || reason === "storage_full" ? "closed" : "notOpen";
|
|
2253
2415
|
}
|
|
2254
|
-
function
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2416
|
+
function NotOpenOverlay(props) {
|
|
2417
|
+
const strings = resolveStrings(props.strings);
|
|
2418
|
+
const closedFlavor = props.variant === "closed";
|
|
2419
|
+
return /* @__PURE__ */ jsxs6(
|
|
2420
|
+
"div",
|
|
2421
|
+
{
|
|
2422
|
+
className: `fillo-form fillo-form--not-open ${props.className ?? ""}`,
|
|
2423
|
+
"data-fillo": "root",
|
|
2424
|
+
"data-state": "closed",
|
|
2425
|
+
...themeProps(props.theme),
|
|
2426
|
+
children: [
|
|
2427
|
+
/* @__PURE__ */ jsx7(
|
|
2428
|
+
"div",
|
|
2429
|
+
{
|
|
2430
|
+
className: "fillo-not-open-preview",
|
|
2431
|
+
"aria-hidden": "true",
|
|
2432
|
+
ref: (node) => node?.setAttribute("inert", ""),
|
|
2433
|
+
children: /* @__PURE__ */ jsx7(
|
|
2434
|
+
ResolvedForm,
|
|
2435
|
+
{
|
|
2436
|
+
schema: props.schema,
|
|
2437
|
+
theme: props.theme,
|
|
2438
|
+
appearance: props.appearance,
|
|
2439
|
+
strings: props.strings,
|
|
2440
|
+
components: props.components,
|
|
2441
|
+
customComponents: props.customComponents,
|
|
2442
|
+
initialData: props.initialData,
|
|
2443
|
+
showTitle: props.showTitle,
|
|
2444
|
+
displayOnly: true
|
|
2445
|
+
}
|
|
2446
|
+
)
|
|
2447
|
+
}
|
|
2448
|
+
),
|
|
2449
|
+
/* @__PURE__ */ jsxs6("div", { className: "fillo-not-open-card", "data-fillo": "not-open-card", role: "status", children: [
|
|
2450
|
+
/* @__PURE__ */ jsx7("h2", { className: "fillo-not-open-title", children: closedFlavor ? strings.closedTitle : strings.notOpenTitle }),
|
|
2451
|
+
/* @__PURE__ */ jsx7("p", { className: "fillo-not-open-body", children: closedFlavor ? strings.closed : strings.notOpenBody })
|
|
2452
|
+
] }),
|
|
2453
|
+
(props.branding?.poweredBy ?? true) && /* @__PURE__ */ jsx7(PoweredBy, { label: props.branding?.label, href: props.branding?.href })
|
|
2454
|
+
]
|
|
2455
|
+
}
|
|
2456
|
+
);
|
|
2259
2457
|
}
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
error.code ? ` (${error.code})` : "",
|
|
2265
|
-
"."
|
|
2266
|
-
] }),
|
|
2267
|
-
" ",
|
|
2268
|
-
error.message,
|
|
2269
|
-
" The safe form remains available for development."
|
|
2270
|
-
] });
|
|
2458
|
+
var noopSubscribe = () => () => {
|
|
2459
|
+
};
|
|
2460
|
+
function useIsDevEnv() {
|
|
2461
|
+
return useSyncExternalStore2(noopSubscribe, isLikelyDevEnv, isBuildTimeDevEnv2);
|
|
2271
2462
|
}
|
|
2272
2463
|
function ResolvedForm(props) {
|
|
2273
2464
|
const { schema, theme } = props;
|
|
2465
|
+
const displayOnly = props.displayOnly === true;
|
|
2274
2466
|
const instanceId = useId2().replace(/:/g, "");
|
|
2467
|
+
const devEnv = useIsDevEnv();
|
|
2468
|
+
const devChrome = !displayOnly && (props.preview === true || devEnv);
|
|
2275
2469
|
const hpRef = useRef7(null);
|
|
2276
2470
|
const codeForm = isCodeForm2(props.form) ? props.form : null;
|
|
2277
2471
|
const client = props.client;
|
|
2278
2472
|
const challenge = props.challenge;
|
|
2279
2473
|
const challengeRequired = Boolean(challenge);
|
|
2280
2474
|
const challengeTokenRef = useRef7(void 0);
|
|
2475
|
+
const [resyncWarning, setResyncWarning] = useState6(null);
|
|
2476
|
+
const [resolutionFailureCode, setResolutionFailureCode] = useState6(null);
|
|
2281
2477
|
const [challengeToken, setChallengeToken] = useState6(void 0);
|
|
2282
2478
|
const [challengeError, setChallengeError] = useState6(false);
|
|
2283
2479
|
const resetChallengeRef = useRef7(void 0);
|
|
@@ -2295,6 +2491,11 @@ function ResolvedForm(props) {
|
|
|
2295
2491
|
async () => {
|
|
2296
2492
|
try {
|
|
2297
2493
|
const result = await syncCodeForm2(client, codeForm, { bypassCache: true });
|
|
2494
|
+
setResyncWarning({
|
|
2495
|
+
code: result.warningCode,
|
|
2496
|
+
url: result.warningUrl,
|
|
2497
|
+
uploadsAvailable: result.uploadsAvailable
|
|
2498
|
+
});
|
|
2298
2499
|
if (result.status === "draft") {
|
|
2299
2500
|
throw new FilloError2(
|
|
2300
2501
|
"This form is no longer published. Submission was stopped before sending answers.",
|
|
@@ -2303,6 +2504,14 @@ function ResolvedForm(props) {
|
|
|
2303
2504
|
"form_not_published"
|
|
2304
2505
|
);
|
|
2305
2506
|
}
|
|
2507
|
+
if (result.accepting === false) {
|
|
2508
|
+
throw new FilloError2(
|
|
2509
|
+
"This form is no longer accepting responses. Submission was stopped before sending answers.",
|
|
2510
|
+
403,
|
|
2511
|
+
void 0,
|
|
2512
|
+
result.acceptingReason ?? "form_not_accepting"
|
|
2513
|
+
);
|
|
2514
|
+
}
|
|
2306
2515
|
const authoritative = normalizeFormSchema(result.resolvedSchema ?? codeForm.schema);
|
|
2307
2516
|
if (!authoritative.ok) {
|
|
2308
2517
|
throw new FilloError2(
|
|
@@ -2332,9 +2541,11 @@ function ResolvedForm(props) {
|
|
|
2332
2541
|
);
|
|
2333
2542
|
props.onError?.(notice);
|
|
2334
2543
|
}
|
|
2544
|
+
setResolutionFailureCode(null);
|
|
2335
2545
|
return result.formId;
|
|
2336
2546
|
} catch (error) {
|
|
2337
2547
|
const failure = isFilloError2(error) ? error : new FilloError2(error instanceof Error ? error.message : String(error), 0);
|
|
2548
|
+
setResolutionFailureCode(failure.code ?? "sync_failed");
|
|
2338
2549
|
console.warn(
|
|
2339
2550
|
`[fillo] form sync failed${failure.code ? ` (${failure.code})` : ""}: ${failure.message}`
|
|
2340
2551
|
);
|
|
@@ -2358,6 +2569,9 @@ function ResolvedForm(props) {
|
|
|
2358
2569
|
// "default", never the bare-engine "headless".
|
|
2359
2570
|
surface: "default",
|
|
2360
2571
|
resolveFormId,
|
|
2572
|
+
// Dev chrome gets the developer-grade resolution failure (message + code)
|
|
2573
|
+
// instead of the respondent-safe "unavailable" fallback.
|
|
2574
|
+
verboseResolutionErrors: devChrome,
|
|
2361
2575
|
respondent: props.respondent,
|
|
2362
2576
|
challengeRequired,
|
|
2363
2577
|
getChallengeToken: () => challengeTokenRef.current,
|
|
@@ -2366,8 +2580,20 @@ function ResolvedForm(props) {
|
|
|
2366
2580
|
resetChallengeRef.current?.();
|
|
2367
2581
|
}
|
|
2368
2582
|
});
|
|
2369
|
-
useDraftFlush(api);
|
|
2583
|
+
useDraftFlush(api, !displayOnly);
|
|
2370
2584
|
const themed = useMemo5(() => themeProps(theme), [theme]);
|
|
2585
|
+
const warningCode = resyncWarning ? resyncWarning.code : props.syncWarningCode;
|
|
2586
|
+
const warningUrl = resyncWarning ? resyncWarning.url : props.syncWarningUrl;
|
|
2587
|
+
const uploadsAvailable = resyncWarning ? resyncWarning.uploadsAvailable : props.uploadsAvailable;
|
|
2588
|
+
const chromeValue = useMemo5(
|
|
2589
|
+
() => ({
|
|
2590
|
+
devChrome,
|
|
2591
|
+
uploadsAvailable,
|
|
2592
|
+
warningCode,
|
|
2593
|
+
warningUrl
|
|
2594
|
+
}),
|
|
2595
|
+
[devChrome, uploadsAvailable, warningCode, warningUrl]
|
|
2596
|
+
);
|
|
2371
2597
|
const settings = schema.settings;
|
|
2372
2598
|
const submitted = api.status === "submitted";
|
|
2373
2599
|
const liveSubmit = submitted && !api.restoredSubmission;
|
|
@@ -2390,7 +2616,7 @@ function ResolvedForm(props) {
|
|
|
2390
2616
|
const appearance = props.appearance;
|
|
2391
2617
|
const strings = resolveStrings(props.strings);
|
|
2392
2618
|
if (api.status === "submitted") {
|
|
2393
|
-
return /* @__PURE__ */
|
|
2619
|
+
return /* @__PURE__ */ jsxs6(
|
|
2394
2620
|
"div",
|
|
2395
2621
|
{
|
|
2396
2622
|
ref: successRef,
|
|
@@ -2406,19 +2632,19 @@ function ResolvedForm(props) {
|
|
|
2406
2632
|
"data-state": "submitted",
|
|
2407
2633
|
...themed,
|
|
2408
2634
|
children: [
|
|
2409
|
-
props.renderSuccess ? props.renderSuccess() : /* @__PURE__ */
|
|
2635
|
+
props.renderSuccess ? props.renderSuccess() : /* @__PURE__ */ jsxs6(
|
|
2410
2636
|
"div",
|
|
2411
2637
|
{
|
|
2412
2638
|
className: slotClass3("fillo-success", appearance, { slot: "success", status: "submitted" }),
|
|
2413
2639
|
"data-fillo": "success",
|
|
2414
2640
|
children: [
|
|
2415
|
-
/* @__PURE__ */
|
|
2416
|
-
/* @__PURE__ */
|
|
2417
|
-
/* @__PURE__ */
|
|
2641
|
+
/* @__PURE__ */ jsx7("div", { className: "fillo-success-mark", "aria-hidden": "true", children: "\u2713" }),
|
|
2642
|
+
/* @__PURE__ */ jsx7("h2", { className: "fillo-success-title", children: settings.successTitle ?? strings.successTitle }),
|
|
2643
|
+
/* @__PURE__ */ jsx7("p", { className: "fillo-success-message", children: api.duplicateSubmission ? strings.alreadyAnswered : settings.successMessage ?? strings.successMessage })
|
|
2418
2644
|
]
|
|
2419
2645
|
}
|
|
2420
2646
|
),
|
|
2421
|
-
props.poweredBy && /* @__PURE__ */
|
|
2647
|
+
props.poweredBy && /* @__PURE__ */ jsx7(PoweredBy, { label: props.brandLabel, href: props.brandHref })
|
|
2422
2648
|
]
|
|
2423
2649
|
}
|
|
2424
2650
|
);
|
|
@@ -2434,13 +2660,162 @@ function ResolvedForm(props) {
|
|
|
2434
2660
|
const progressTotal = Math.max(progressSeq.length, 1);
|
|
2435
2661
|
const progressPos = progressSeq.indexOf(api.page.id);
|
|
2436
2662
|
const progressStep = progressPos >= 0 ? progressPos + 1 : Math.min(api.pageIndex + 1, progressTotal);
|
|
2437
|
-
|
|
2663
|
+
const submitFixUrl = devChrome && resolutionFailureCode === "form_not_published" && warningCode === "storage_required" ? safeHttpUrl(warningUrl) : null;
|
|
2664
|
+
const rootClass = slotClass3(`fillo-form ${props.className ?? ""}`, appearance, {
|
|
2665
|
+
slot: "root",
|
|
2666
|
+
status: api.status
|
|
2667
|
+
});
|
|
2668
|
+
const formBody = /* @__PURE__ */ jsxs6(Fragment3, { children: [
|
|
2669
|
+
devChrome && /* @__PURE__ */ jsx7(
|
|
2670
|
+
DevChrome,
|
|
2671
|
+
{
|
|
2672
|
+
preview: props.preview,
|
|
2673
|
+
devNotices: props.devNotices,
|
|
2674
|
+
syncError: props.syncErrorNotice,
|
|
2675
|
+
staged: props.stagedNotice,
|
|
2676
|
+
draft: props.draftNotice,
|
|
2677
|
+
warningUrl: props.syncWarningUrl,
|
|
2678
|
+
noClient: !props.client && !props.skipValidation
|
|
2679
|
+
}
|
|
2680
|
+
),
|
|
2681
|
+
(api.resumedDraft || api.editingPrevious) && /* @__PURE__ */ jsxs6(
|
|
2682
|
+
"div",
|
|
2683
|
+
{
|
|
2684
|
+
className: slotClass3("fillo-resume", appearance, { slot: "resume" }),
|
|
2685
|
+
"data-fillo": "resume",
|
|
2686
|
+
role: "status",
|
|
2687
|
+
children: [
|
|
2688
|
+
/* @__PURE__ */ jsx7("span", { className: "fillo-resume-text", children: api.editingPrevious ? strings.editNotice : strings.resumeNotice }),
|
|
2689
|
+
/* @__PURE__ */ jsx7("button", { type: "button", className: "fillo-resume-clear", onClick: api.resetDraft, children: strings.resumeStartOver })
|
|
2690
|
+
]
|
|
2691
|
+
}
|
|
2692
|
+
),
|
|
2693
|
+
api.resumeLinkFailed && !api.resumedDraft && !api.editingPrevious && /* @__PURE__ */ jsx7(
|
|
2694
|
+
"div",
|
|
2695
|
+
{
|
|
2696
|
+
className: slotClass3("fillo-resume fillo-resume--expired", appearance, { slot: "resume" }),
|
|
2697
|
+
"data-fillo": "resume",
|
|
2698
|
+
role: "status",
|
|
2699
|
+
children: /* @__PURE__ */ jsx7("span", { className: "fillo-resume-text", children: strings.resumeLinkExpired })
|
|
2700
|
+
}
|
|
2701
|
+
),
|
|
2702
|
+
multiPage && settings.showProgress !== false && /* @__PURE__ */ jsx7(
|
|
2703
|
+
"div",
|
|
2704
|
+
{
|
|
2705
|
+
className: slotClass3("fillo-progress-track", appearance, { slot: "progress" }),
|
|
2706
|
+
"data-fillo": "progress",
|
|
2707
|
+
role: "progressbar",
|
|
2708
|
+
"aria-label": schema.title || "Form progress",
|
|
2709
|
+
"aria-valuemin": 1,
|
|
2710
|
+
"aria-valuemax": progressTotal,
|
|
2711
|
+
"aria-valuenow": progressStep,
|
|
2712
|
+
children: /* @__PURE__ */ jsx7(
|
|
2713
|
+
"div",
|
|
2714
|
+
{
|
|
2715
|
+
className: slotClass3("fillo-progress-fill", appearance, { slot: "progressFill" }),
|
|
2716
|
+
"data-fillo": "progressFill",
|
|
2717
|
+
style: { "--fillo-progress-value": `${progressStep / progressTotal * 100}%` }
|
|
2718
|
+
}
|
|
2719
|
+
)
|
|
2720
|
+
}
|
|
2721
|
+
),
|
|
2722
|
+
api.pageIndex === 0 && props.showTitle !== false && /* @__PURE__ */ jsxs6("header", { className: slotClass3("fillo-header", appearance, { slot: "header" }), "data-fillo": "header", children: [
|
|
2723
|
+
schema.title && /* @__PURE__ */ jsx7("h1", { className: slotClass3("fillo-title", appearance, { slot: "title" }), "data-fillo": "title", children: schema.title }),
|
|
2724
|
+
schema.description && /* @__PURE__ */ jsx7(
|
|
2725
|
+
"p",
|
|
2726
|
+
{
|
|
2727
|
+
className: slotClass3("fillo-form-description", appearance, { slot: "description" }),
|
|
2728
|
+
"data-fillo": "description",
|
|
2729
|
+
children: schema.description
|
|
2730
|
+
}
|
|
2731
|
+
)
|
|
2732
|
+
] }),
|
|
2733
|
+
multiPage && api.page.title && api.pageIndex > 0 && /* @__PURE__ */ jsx7("h2", { className: slotClass3("fillo-page-title", appearance, { slot: "pageTitle" }), "data-fillo": "pageTitle", children: api.page.title }),
|
|
2734
|
+
/* @__PURE__ */ jsx7("div", { className: slotClass3("fillo-blocks", appearance, { slot: "blocks" }), "data-fillo": "blocks", ref: blocksRef, tabIndex: -1, children: api.blocks.map((block) => /* @__PURE__ */ jsx7(
|
|
2735
|
+
BlockRenderer,
|
|
2736
|
+
{
|
|
2737
|
+
block,
|
|
2738
|
+
api,
|
|
2739
|
+
components: props.components,
|
|
2740
|
+
customComponents: props.customComponents
|
|
2741
|
+
},
|
|
2742
|
+
block.id
|
|
2743
|
+
)) }),
|
|
2744
|
+
!displayOnly && /* @__PURE__ */ jsx7(
|
|
2745
|
+
"input",
|
|
2746
|
+
{
|
|
2747
|
+
type: "text",
|
|
2748
|
+
name: "fillo_hp_field",
|
|
2749
|
+
className: "fillo-hp",
|
|
2750
|
+
tabIndex: -1,
|
|
2751
|
+
autoComplete: "off",
|
|
2752
|
+
"aria-hidden": "true",
|
|
2753
|
+
defaultValue: "",
|
|
2754
|
+
style: HP_STYLE,
|
|
2755
|
+
ref: (element) => {
|
|
2756
|
+
hpRef.current = element;
|
|
2757
|
+
assertHpHidden(element);
|
|
2758
|
+
}
|
|
2759
|
+
}
|
|
2760
|
+
),
|
|
2761
|
+
challenge && showChallenge && /* @__PURE__ */ jsxs6("div", { className: "fillo-turnstile-slot", "data-fillo": "turnstile-slot", children: [
|
|
2762
|
+
/* @__PURE__ */ jsx7(
|
|
2763
|
+
TurnstileWidget,
|
|
2764
|
+
{
|
|
2765
|
+
siteKey: challenge.siteKey,
|
|
2766
|
+
className: slotClass3("fillo-turnstile", appearance, { slot: "turnstile" }),
|
|
2767
|
+
onToken: handleChallengeToken,
|
|
2768
|
+
onError: () => setChallengeError(true),
|
|
2769
|
+
registerReset: registerChallengeReset
|
|
2770
|
+
}
|
|
2771
|
+
),
|
|
2772
|
+
challengeError && /* @__PURE__ */ jsx7("p", { className: "fillo-turnstile-error", role: "alert", children: strings.challengeUnavailable })
|
|
2773
|
+
] }),
|
|
2774
|
+
(api.status === "error" || api.submitError) && /* @__PURE__ */ jsxs6("p", { className: "fillo-submit-error", role: "alert", children: [
|
|
2775
|
+
api.submitError ?? strings.submitFailed,
|
|
2776
|
+
submitFixUrl && /* @__PURE__ */ jsxs6(Fragment3, { children: [
|
|
2777
|
+
" ",
|
|
2778
|
+
"Connect storage:",
|
|
2779
|
+
" ",
|
|
2780
|
+
/* @__PURE__ */ jsx7("a", { href: submitFixUrl, target: "_blank", rel: "noopener noreferrer", children: submitFixUrl })
|
|
2781
|
+
] })
|
|
2782
|
+
] }),
|
|
2783
|
+
showFooter && /* @__PURE__ */ jsxs6("footer", { className: slotClass3("fillo-footer", appearance, { slot: "footer" }), "data-fillo": "footer", children: [
|
|
2784
|
+
multiPage && !api.isFirstPage && /* @__PURE__ */ jsx7(
|
|
2785
|
+
"button",
|
|
2786
|
+
{
|
|
2787
|
+
type: "button",
|
|
2788
|
+
className: slotClass3("fillo-button fillo-button--ghost", appearance, { slot: "button", variant: "ghost" }),
|
|
2789
|
+
"data-fillo": "button",
|
|
2790
|
+
onClick: api.back,
|
|
2791
|
+
children: strings.back
|
|
2792
|
+
}
|
|
2793
|
+
),
|
|
2794
|
+
showPrimaryButton && /* @__PURE__ */ jsx7(
|
|
2795
|
+
"button",
|
|
2796
|
+
{
|
|
2797
|
+
type: "submit",
|
|
2798
|
+
className: slotClass3("fillo-button fillo-button--primary", appearance, { slot: "button", variant: "primary" }),
|
|
2799
|
+
"data-fillo": "button",
|
|
2800
|
+
disabled: api.status === "submitting" || api.uploading || // Wait for the human check before enabling submit (never
|
|
2801
|
+
// "Next"). The server rejects a tokenless submit regardless —
|
|
2802
|
+
// this is UX only.
|
|
2803
|
+
showChallenge && !challengeToken,
|
|
2804
|
+
children: api.uploading ? strings.uploading : api.status === "submitting" ? strings.submitting : api.isLastPage ? settings.submitLabel ?? strings.submit : strings.next
|
|
2805
|
+
}
|
|
2806
|
+
)
|
|
2807
|
+
] }),
|
|
2808
|
+
props.poweredBy && /* @__PURE__ */ jsx7(PoweredBy, { label: props.brandLabel, href: props.brandHref })
|
|
2809
|
+
] });
|
|
2810
|
+
return /* @__PURE__ */ jsx7(FilloContext.Provider, { value: api, children: /* @__PURE__ */ jsx7(FilloChromeContext.Provider, { value: chromeValue, children: /* @__PURE__ */ jsx7(FilloStringsContext.Provider, { value: strings, children: /* @__PURE__ */ jsx7(FilloAppearanceContext.Provider, { value: appearance, children: /* @__PURE__ */ jsx7(FilloInstanceIdContext.Provider, { value: instanceId, children: displayOnly ? (
|
|
2811
|
+
// Display-only preview: a plain div (no form element to submit) with
|
|
2812
|
+
// every control inside a natively disabled fieldset — unfocusable
|
|
2813
|
+
// and unfillable even without the stylesheet or inert support.
|
|
2814
|
+
/* @__PURE__ */ jsx7("div", { className: rootClass, "data-fillo": "preview-form", "data-state": "preview", ...themed, children: /* @__PURE__ */ jsx7("fieldset", { disabled: true, className: "fillo-not-open-fields", children: formBody }) })
|
|
2815
|
+
) : /* @__PURE__ */ jsx7(
|
|
2438
2816
|
"form",
|
|
2439
2817
|
{
|
|
2440
|
-
className:
|
|
2441
|
-
slot: "root",
|
|
2442
|
-
status: api.status
|
|
2443
|
-
}),
|
|
2818
|
+
className: rootClass,
|
|
2444
2819
|
"data-fillo": "root",
|
|
2445
2820
|
"data-state": api.status,
|
|
2446
2821
|
"data-page": api.pageIndex + 1,
|
|
@@ -2458,134 +2833,9 @@ function ResolvedForm(props) {
|
|
|
2458
2833
|
focusFirstInvalid(formEl);
|
|
2459
2834
|
}
|
|
2460
2835
|
},
|
|
2461
|
-
children:
|
|
2462
|
-
!props.client && !props.skipValidation && isDevEnv() && /* @__PURE__ */ jsx6(DevNoClientNotice, {}),
|
|
2463
|
-
props.draftNotice && isDevEnv() && /* @__PURE__ */ jsx6(DevDraftNotice, {}),
|
|
2464
|
-
props.stagedNotice && isDevEnv() && /* @__PURE__ */ jsx6(DevStagedNotice, {}),
|
|
2465
|
-
props.syncErrorNotice && isDevEnv() && /* @__PURE__ */ jsx6(DevSyncErrorNotice, { error: props.syncErrorNotice }),
|
|
2466
|
-
(api.resumedDraft || api.editingPrevious) && /* @__PURE__ */ jsxs5(
|
|
2467
|
-
"div",
|
|
2468
|
-
{
|
|
2469
|
-
className: slotClass3("fillo-resume", appearance, { slot: "resume" }),
|
|
2470
|
-
"data-fillo": "resume",
|
|
2471
|
-
role: "status",
|
|
2472
|
-
children: [
|
|
2473
|
-
/* @__PURE__ */ jsx6("span", { className: "fillo-resume-text", children: api.editingPrevious ? strings.editNotice : strings.resumeNotice }),
|
|
2474
|
-
/* @__PURE__ */ jsx6("button", { type: "button", className: "fillo-resume-clear", onClick: api.resetDraft, children: strings.resumeStartOver })
|
|
2475
|
-
]
|
|
2476
|
-
}
|
|
2477
|
-
),
|
|
2478
|
-
api.resumeLinkFailed && !api.resumedDraft && !api.editingPrevious && /* @__PURE__ */ jsx6(
|
|
2479
|
-
"div",
|
|
2480
|
-
{
|
|
2481
|
-
className: slotClass3("fillo-resume fillo-resume--expired", appearance, { slot: "resume" }),
|
|
2482
|
-
"data-fillo": "resume",
|
|
2483
|
-
role: "status",
|
|
2484
|
-
children: /* @__PURE__ */ jsx6("span", { className: "fillo-resume-text", children: strings.resumeLinkExpired })
|
|
2485
|
-
}
|
|
2486
|
-
),
|
|
2487
|
-
multiPage && settings.showProgress !== false && /* @__PURE__ */ jsx6(
|
|
2488
|
-
"div",
|
|
2489
|
-
{
|
|
2490
|
-
className: slotClass3("fillo-progress-track", appearance, { slot: "progress" }),
|
|
2491
|
-
"data-fillo": "progress",
|
|
2492
|
-
role: "progressbar",
|
|
2493
|
-
"aria-label": schema.title || "Form progress",
|
|
2494
|
-
"aria-valuemin": 1,
|
|
2495
|
-
"aria-valuemax": progressTotal,
|
|
2496
|
-
"aria-valuenow": progressStep,
|
|
2497
|
-
children: /* @__PURE__ */ jsx6(
|
|
2498
|
-
"div",
|
|
2499
|
-
{
|
|
2500
|
-
className: slotClass3("fillo-progress-fill", appearance, { slot: "progressFill" }),
|
|
2501
|
-
"data-fillo": "progressFill",
|
|
2502
|
-
style: { "--fillo-progress-value": `${progressStep / progressTotal * 100}%` }
|
|
2503
|
-
}
|
|
2504
|
-
)
|
|
2505
|
-
}
|
|
2506
|
-
),
|
|
2507
|
-
api.pageIndex === 0 && props.showTitle !== false && /* @__PURE__ */ jsxs5("header", { className: slotClass3("fillo-header", appearance, { slot: "header" }), "data-fillo": "header", children: [
|
|
2508
|
-
schema.title && /* @__PURE__ */ jsx6("h1", { className: slotClass3("fillo-title", appearance, { slot: "title" }), "data-fillo": "title", children: schema.title }),
|
|
2509
|
-
schema.description && /* @__PURE__ */ jsx6(
|
|
2510
|
-
"p",
|
|
2511
|
-
{
|
|
2512
|
-
className: slotClass3("fillo-form-description", appearance, { slot: "description" }),
|
|
2513
|
-
"data-fillo": "description",
|
|
2514
|
-
children: schema.description
|
|
2515
|
-
}
|
|
2516
|
-
)
|
|
2517
|
-
] }),
|
|
2518
|
-
multiPage && api.page.title && api.pageIndex > 0 && /* @__PURE__ */ jsx6("h2", { className: slotClass3("fillo-page-title", appearance, { slot: "pageTitle" }), "data-fillo": "pageTitle", children: api.page.title }),
|
|
2519
|
-
/* @__PURE__ */ jsx6("div", { className: slotClass3("fillo-blocks", appearance, { slot: "blocks" }), "data-fillo": "blocks", ref: blocksRef, tabIndex: -1, children: api.blocks.map((block) => /* @__PURE__ */ jsx6(
|
|
2520
|
-
BlockRenderer,
|
|
2521
|
-
{
|
|
2522
|
-
block,
|
|
2523
|
-
api,
|
|
2524
|
-
components: props.components,
|
|
2525
|
-
customComponents: props.customComponents
|
|
2526
|
-
},
|
|
2527
|
-
block.id
|
|
2528
|
-
)) }),
|
|
2529
|
-
/* @__PURE__ */ jsx6(
|
|
2530
|
-
"input",
|
|
2531
|
-
{
|
|
2532
|
-
type: "text",
|
|
2533
|
-
name: "fillo_hp_field",
|
|
2534
|
-
className: "fillo-hp",
|
|
2535
|
-
tabIndex: -1,
|
|
2536
|
-
autoComplete: "off",
|
|
2537
|
-
"aria-hidden": "true",
|
|
2538
|
-
defaultValue: "",
|
|
2539
|
-
style: HP_STYLE,
|
|
2540
|
-
ref: (element) => {
|
|
2541
|
-
hpRef.current = element;
|
|
2542
|
-
assertHpHidden(element);
|
|
2543
|
-
}
|
|
2544
|
-
}
|
|
2545
|
-
),
|
|
2546
|
-
challenge && showChallenge && /* @__PURE__ */ jsxs5("div", { className: "fillo-turnstile-slot", "data-fillo": "turnstile-slot", children: [
|
|
2547
|
-
/* @__PURE__ */ jsx6(
|
|
2548
|
-
TurnstileWidget,
|
|
2549
|
-
{
|
|
2550
|
-
siteKey: challenge.siteKey,
|
|
2551
|
-
className: slotClass3("fillo-turnstile", appearance, { slot: "turnstile" }),
|
|
2552
|
-
onToken: handleChallengeToken,
|
|
2553
|
-
onError: () => setChallengeError(true),
|
|
2554
|
-
registerReset: registerChallengeReset
|
|
2555
|
-
}
|
|
2556
|
-
),
|
|
2557
|
-
challengeError && /* @__PURE__ */ jsx6("p", { className: "fillo-turnstile-error", role: "alert", children: strings.challengeUnavailable })
|
|
2558
|
-
] }),
|
|
2559
|
-
(api.status === "error" || api.submitError) && /* @__PURE__ */ jsx6("p", { className: "fillo-submit-error", role: "alert", children: api.submitError ?? strings.submitFailed }),
|
|
2560
|
-
showFooter && /* @__PURE__ */ jsxs5("footer", { className: slotClass3("fillo-footer", appearance, { slot: "footer" }), "data-fillo": "footer", children: [
|
|
2561
|
-
multiPage && !api.isFirstPage && /* @__PURE__ */ jsx6(
|
|
2562
|
-
"button",
|
|
2563
|
-
{
|
|
2564
|
-
type: "button",
|
|
2565
|
-
className: slotClass3("fillo-button fillo-button--ghost", appearance, { slot: "button", variant: "ghost" }),
|
|
2566
|
-
"data-fillo": "button",
|
|
2567
|
-
onClick: api.back,
|
|
2568
|
-
children: strings.back
|
|
2569
|
-
}
|
|
2570
|
-
),
|
|
2571
|
-
showPrimaryButton && /* @__PURE__ */ jsx6(
|
|
2572
|
-
"button",
|
|
2573
|
-
{
|
|
2574
|
-
type: "submit",
|
|
2575
|
-
className: slotClass3("fillo-button fillo-button--primary", appearance, { slot: "button", variant: "primary" }),
|
|
2576
|
-
"data-fillo": "button",
|
|
2577
|
-
disabled: api.status === "submitting" || api.uploading || // Wait for the human check before enabling submit (never
|
|
2578
|
-
// "Next"). The server rejects a tokenless submit regardless —
|
|
2579
|
-
// this is UX only.
|
|
2580
|
-
showChallenge && !challengeToken,
|
|
2581
|
-
children: api.uploading ? strings.uploading : api.status === "submitting" ? strings.submitting : api.isLastPage ? settings.submitLabel ?? strings.submit : strings.next
|
|
2582
|
-
}
|
|
2583
|
-
)
|
|
2584
|
-
] }),
|
|
2585
|
-
props.poweredBy && /* @__PURE__ */ jsx6(PoweredBy, { label: props.brandLabel, href: props.brandHref })
|
|
2586
|
-
]
|
|
2836
|
+
children: formBody
|
|
2587
2837
|
}
|
|
2588
|
-
) }) }) }) });
|
|
2838
|
+
) }) }) }) }) });
|
|
2589
2839
|
}
|
|
2590
2840
|
|
|
2591
2841
|
// src/jsx.tsx
|
|
@@ -2593,17 +2843,18 @@ import { useRef as useRef8 } from "react";
|
|
|
2593
2843
|
import {
|
|
2594
2844
|
codeFormFromJsx,
|
|
2595
2845
|
FilloJsxError,
|
|
2846
|
+
isLikelyDevEnv as isLikelyDevEnv2,
|
|
2596
2847
|
JSX_BLOCK_COMPONENTS
|
|
2597
2848
|
} from "@usefillo/core";
|
|
2598
|
-
import { jsx as
|
|
2849
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
2599
2850
|
var B = JSX_BLOCK_COMPONENTS;
|
|
2600
|
-
var
|
|
2851
|
+
var isDevEnv = () => isLikelyDevEnv2();
|
|
2601
2852
|
function JsxForm(props) {
|
|
2602
2853
|
const { id, title, description, settings, theme, client, children, ...rest } = props;
|
|
2603
2854
|
const compiled = codeFormFromJsx({ id, title, description, settings, theme }, children);
|
|
2604
2855
|
const json = JSON.stringify(compiled.schema) + JSON.stringify(compiled.theme ?? null);
|
|
2605
2856
|
const memo2 = useRef8(null);
|
|
2606
|
-
if (memo2.current && memo2.current.json !== json &&
|
|
2857
|
+
if (memo2.current && memo2.current.json !== json && isDevEnv()) {
|
|
2607
2858
|
console.warn(
|
|
2608
2859
|
`[fillo] "${id}": form structure changed between renders. If you just edited the form, ignore this. Otherwise: conditional questions are visibleIf={when(\u2026)\u2026}, never {cond && <Fillo.\u2026/>} \u2014 a per-visitor schema churns drafts in your workspace.`
|
|
2609
2860
|
);
|
|
@@ -2611,7 +2862,7 @@ function JsxForm(props) {
|
|
|
2611
2862
|
if (!memo2.current || memo2.current.json !== json) {
|
|
2612
2863
|
memo2.current = { json, form: compiled };
|
|
2613
2864
|
}
|
|
2614
|
-
return /* @__PURE__ */
|
|
2865
|
+
return /* @__PURE__ */ jsx8(FilloForm, { form: memo2.current.form, client, ...rest });
|
|
2615
2866
|
}
|
|
2616
2867
|
function defineFormFromJsx(element) {
|
|
2617
2868
|
if (!element || element.type !== JsxForm) {
|
|
@@ -2655,7 +2906,7 @@ var Fillo = {
|
|
|
2655
2906
|
};
|
|
2656
2907
|
|
|
2657
2908
|
// src/provider.tsx
|
|
2658
|
-
import { useId as useId3, useMemo as useMemo6 } from "react";
|
|
2909
|
+
import { useId as useId3, useMemo as useMemo6, useState as useState7 } from "react";
|
|
2659
2910
|
import {
|
|
2660
2911
|
FilloError as FilloError3,
|
|
2661
2912
|
formSchemasEqual as formSchemasEqual2,
|
|
@@ -2664,7 +2915,7 @@ import {
|
|
|
2664
2915
|
resolveStrings as resolveStrings2,
|
|
2665
2916
|
syncCodeForm as syncCodeForm3
|
|
2666
2917
|
} from "@usefillo/core";
|
|
2667
|
-
import { Fragment as
|
|
2918
|
+
import { Fragment as Fragment4, jsx as jsx9 } from "react/jsx-runtime";
|
|
2668
2919
|
function FilloProvider({
|
|
2669
2920
|
children,
|
|
2670
2921
|
form,
|
|
@@ -2674,20 +2925,30 @@ function FilloProvider({
|
|
|
2674
2925
|
strings,
|
|
2675
2926
|
onError,
|
|
2676
2927
|
renderError,
|
|
2928
|
+
preview,
|
|
2677
2929
|
...options
|
|
2678
2930
|
}) {
|
|
2679
2931
|
const instanceId = useId3().replace(/:/g, "");
|
|
2932
|
+
const devEnv = useIsDevEnv();
|
|
2933
|
+
const devChrome = preview === true || devEnv;
|
|
2934
|
+
if (preview === true) warnPreviewInProduction();
|
|
2680
2935
|
const codeForm = isCodeForm2(form) ? form : null;
|
|
2681
2936
|
const {
|
|
2682
2937
|
formId: syncedFormId,
|
|
2683
2938
|
status: syncedStatus,
|
|
2684
2939
|
error: syncError,
|
|
2685
2940
|
syncing: codeFormSyncing,
|
|
2686
|
-
resolvedSchema
|
|
2941
|
+
resolvedSchema,
|
|
2942
|
+
accepting: syncedAccepting,
|
|
2943
|
+
acceptingReason: syncedAcceptingReason,
|
|
2944
|
+
uploadsAvailable,
|
|
2945
|
+
warningCode,
|
|
2946
|
+
warningUrl
|
|
2687
2947
|
} = useCodeFormSync(form, client, onError, {
|
|
2688
2948
|
allowUnsynced: Boolean(!client || formId || options.skipValidation)
|
|
2689
2949
|
});
|
|
2690
|
-
const
|
|
2950
|
+
const [resyncWarning, setResyncWarning] = useState7(null);
|
|
2951
|
+
const rawSchema = codeForm ? resolvedSchema && !devChrome ? resolvedSchema : codeForm.schema : form;
|
|
2691
2952
|
const normalized = useMemo6(() => normalizeFormSchema2(rawSchema), [rawSchema]);
|
|
2692
2953
|
if (!normalized.ok) {
|
|
2693
2954
|
throw new FilloError3(`This form could not be rendered: ${normalized.error}`, 422);
|
|
@@ -2697,6 +2958,11 @@ function FilloProvider({
|
|
|
2697
2958
|
() => codeForm && client?.key ? async () => {
|
|
2698
2959
|
try {
|
|
2699
2960
|
const result = await syncCodeForm3(client, codeForm, { bypassCache: true });
|
|
2961
|
+
setResyncWarning({
|
|
2962
|
+
code: result.warningCode,
|
|
2963
|
+
url: result.warningUrl,
|
|
2964
|
+
uploadsAvailable: result.uploadsAvailable
|
|
2965
|
+
});
|
|
2700
2966
|
if (result.status === "draft") {
|
|
2701
2967
|
throw new FilloError3(
|
|
2702
2968
|
"This form is no longer published. Submission was stopped before sending answers.",
|
|
@@ -2705,6 +2971,14 @@ function FilloProvider({
|
|
|
2705
2971
|
"form_not_published"
|
|
2706
2972
|
);
|
|
2707
2973
|
}
|
|
2974
|
+
if (result.accepting === false) {
|
|
2975
|
+
throw new FilloError3(
|
|
2976
|
+
"This form is no longer accepting responses. Submission was stopped before sending answers.",
|
|
2977
|
+
403,
|
|
2978
|
+
void 0,
|
|
2979
|
+
result.acceptingReason ?? "form_not_accepting"
|
|
2980
|
+
);
|
|
2981
|
+
}
|
|
2708
2982
|
const authoritative = normalizeFormSchema2(result.resolvedSchema ?? codeForm.schema);
|
|
2709
2983
|
if (!authoritative.ok) {
|
|
2710
2984
|
throw new FilloError3(
|
|
@@ -2752,25 +3026,43 @@ function FilloProvider({
|
|
|
2752
3026
|
form: schema,
|
|
2753
3027
|
formId: syncedFormId ?? formId,
|
|
2754
3028
|
resolveFormId,
|
|
3029
|
+
// Dev chrome (preview or dev env) surfaces the developer-grade resolution
|
|
3030
|
+
// failure (message + code); an explicit option wins.
|
|
3031
|
+
verboseResolutionErrors: options.verboseResolutionErrors ?? devChrome,
|
|
2755
3032
|
// Headless = your markup, no Fillo-rendered layout. Free like every embed
|
|
2756
3033
|
// method; recorded per response so usage stays measurable.
|
|
2757
3034
|
surface: "headless"
|
|
2758
3035
|
});
|
|
2759
3036
|
useDraftFlush(api);
|
|
3037
|
+
const freshWarningCode = resyncWarning ? resyncWarning.code : warningCode;
|
|
3038
|
+
const freshWarningUrl = resyncWarning ? resyncWarning.url : warningUrl;
|
|
3039
|
+
const freshUploadsAvailable = resyncWarning ? resyncWarning.uploadsAvailable : uploadsAvailable;
|
|
3040
|
+
const chromeValue = useMemo6(
|
|
3041
|
+
() => ({
|
|
3042
|
+
devChrome,
|
|
3043
|
+
uploadsAvailable: freshUploadsAvailable,
|
|
3044
|
+
warningCode: freshWarningCode,
|
|
3045
|
+
warningUrl: freshWarningUrl
|
|
3046
|
+
}),
|
|
3047
|
+
[devChrome, freshUploadsAvailable, freshWarningCode, freshWarningUrl]
|
|
3048
|
+
);
|
|
2760
3049
|
const resolvedStrings = resolveStrings2(strings);
|
|
2761
3050
|
const canonicalSyncError = syncError && !options.skipValidation ? syncError : null;
|
|
2762
|
-
if (canonicalSyncError && renderError) return /* @__PURE__ */
|
|
2763
|
-
if (canonicalSyncError && !
|
|
2764
|
-
if (codeFormSyncing && !options.skipValidation && !
|
|
3051
|
+
if (canonicalSyncError && renderError) return /* @__PURE__ */ jsx9(Fragment4, { children: renderError(canonicalSyncError) });
|
|
3052
|
+
if (canonicalSyncError && !devChrome) return null;
|
|
3053
|
+
if (codeFormSyncing && !options.skipValidation && !devChrome) return null;
|
|
2765
3054
|
const notPublished = Boolean(codeForm) && syncedStatus === "draft" && !options.skipValidation;
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
3055
|
+
const notAccepting = Boolean(codeForm) && syncedStatus === "published" && syncedAccepting === false && !options.skipValidation;
|
|
3056
|
+
if ((notPublished || notAccepting) && !devChrome) {
|
|
3057
|
+
const error = notPublished ? new FilloError3("This form isn't published yet.", 403, void 0, "form_not_published") : new FilloError3(
|
|
3058
|
+
"This form is no longer accepting responses.",
|
|
3059
|
+
403,
|
|
3060
|
+
void 0,
|
|
3061
|
+
syncedAcceptingReason ?? "form_not_accepting"
|
|
3062
|
+
);
|
|
3063
|
+
return renderError ? /* @__PURE__ */ jsx9(Fragment4, { children: renderError(error) }) : null;
|
|
2769
3064
|
}
|
|
2770
|
-
return /* @__PURE__ */
|
|
2771
|
-
}
|
|
2772
|
-
function isDevEnv3() {
|
|
2773
|
-
return typeof process !== "undefined" && process.env?.NODE_ENV !== "production";
|
|
3065
|
+
return /* @__PURE__ */ jsx9(FilloContext.Provider, { value: api, children: /* @__PURE__ */ jsx9(FilloChromeContext.Provider, { value: chromeValue, children: /* @__PURE__ */ jsx9(FilloStringsContext.Provider, { value: resolvedStrings, children: /* @__PURE__ */ jsx9(FilloAppearanceContext.Provider, { value: appearance, children: /* @__PURE__ */ jsx9(FilloInstanceIdContext.Provider, { value: instanceId, children }) }) }) }) });
|
|
2774
3066
|
}
|
|
2775
3067
|
|
|
2776
3068
|
// src/index.ts
|